Skip to main content
Version: Next

Nested Structures and Collections

This page covers: filling beans, list/array/set/map in Excel, plus multi-level headers and multi-row lists.

When one field spans multiple columns in Excel, mark the range with merged cells (do not repeat the field name in every column). The HTML tables below use colspan to simulate merges.

CSV and formats without merged cells

CSV cannot merge cells. To span multiple columns, put [{fieldName} in the start column and {fieldName}] in the end column; leave middle columns empty. This is equivalent to Excel merges.

Example: field nums (list,int) spans columns C–F: C1=[{nums}, F1={nums}]; the ##type row still says list,int; fill elements left to right.

list multi-column

Element types can be enums or beans; array / list / set work similarly.

list with struct elements

For a single cell, use sep= to specify a separator (e.g. sep=,, |, ;); see sep and stream.

Why multi-level headers (column constraints)

If composite data is filled purely in stream order across columns, blank cells are skipped, which misaligns fields easily; stream mode also does not support “empty = default”. Adding a child ##var row names sub-fields and pins each to a column.

multi-level headers

You can nest to any depth (x1 one level, y1 two levels, z1 three levels, etc.).

Define the structures first

The examples below share these beans (XML sketch; you can also use __beans__.xlsx):

<bean name="Cost">
<var name="id" type="int"/>
<var name="count" type="int"/>
</bean>

<bean name="Reward">
<var name="item_id" type="int"/>
<var name="num" type="int"/>
<var name="desc" type="string"/>
</bean>

Bean: fill across merged columns

Field cost has type Cost. On the first row, merge two columns for cost; on the next ##var row, write the child field names:

##varidcostname
##varidcount
##typeintCoststring
110015兑换A
210021兑换B

How to read it: cost spans two columns; the child row is id / count.

Equivalent understanding: id=1, cost={id:1001,count:5}, name=兑换A.

Nested beans

When reward nests another cost: merge reward on the outer level, then merge cost inside:

##varidreward
##varitem_idnumcost
##varidcount
##typeintintReward
120011010012

You can also skip multi-level headers and pack the nested structure into one cell with sep / compact format.

list / array / set

Use merged cells to mark the full column span of the list; fill elements left to right; blank cells are often ignored.

list,int (up to 4 elements)

##varidnums
##typeintintintintint
1135
210

Result: id=1 → nums=[1,3,5]; id=2 → nums=[10].

list,Cost

Each element takes the bean width (here 2 columns), then multiple elements are laid out horizontally; the first row still merges costs only once:

##varidcosts
##varidcountidcount
##typeintintintintint
11001210021

costs=[{1001,2},{1002,1}].

map

map,int,string: within the merged range, fill key/value pairs.

##variddict
##typeintintstringintstring
112

{1:"剑", 2:"盾"}.

Single cell + dual sep

Maps can also be filled in one cell. To separate key/value and each pair, configure two separators, e.g. map#sep=,; or (map#sep=,;),int,Item#sep=,: the first splits pairs, the second splits key from value (or fields inside value). When value is a bean, each value segment is read in stream mode with sep.

map single cell

When value is a struct:

map value is bean

Maps also support *fieldName multi-row filling:

map multi-row

Column-constrained form (keys as child column names)

Merge attrs on the upper level; use fixed keys as column names below:

##varidattrs
##varatkdefhp
##typeintintintint
1105100

Non-empty cells mean that key-value exists. Example: for id=1, y2 has child columns aaa and ccc with 1 and 2{{"aaa",1},{"ccc",2}}; id=2 with bbb=10, ccc=20, ddd=30{{"bbb",10},{"ccc",20},{"ddd",30}}.

map column constraint

tip

The above is column-constrained map filling. In stream/sep cells, maps also support alternating key-value and compact sep forms; see sep and stream.

Column constraint + multi-row ($key)

In multi-row mode, the $key child column is the map key; remaining columns are value sub-fields (good when value is a bean).

Multi-row list *<name>

When the field name is written as *rewards, multiple rows under the same primary key represent multiple list elements; merge columns for *rewards on the first row:

##varid*rewards
##varitem_idnumdesc
##typeintintintstring
110011第一项
10022第二项
220011

→ id=1 has two rewards; id=2 has one. Good for “one main row + many sub-config rows”.

Arbitrary nesting is supported: each element in a multi-row list can itself be multi-row. For array,bean / list,bean, pair with column constraints on element sub-fields.

multi-row struct list

Extra column-constrained notes

Under column constraints, only atomic types (e.g. int, string) support default values when left empty (int→0, int?→null). If the lowest constrained type is still a container or bean (sub-fields not pinned), child data is still read in stream mode and blanks are skipped.

column constraint with stream

FormPurpose
$typeType column for polymorphic / nullable beans; for flags enums (flags=1), enum items as child columns, OR of non-zero/non-empty cells
$valueLimits actual bean field range; fill all fields in stream mode inside $value
$keyKey column for map multi-row mode

Flags enum column constraint example:

flags column constraint

Polymorphic $type + $value example:

polymorphic column constraint

Common pitfalls

  • Merged column count does not match the columns actually used by the bean / container.
  • Docs or headers repeat the field name in every column, which mismatches Excel merge semantics and confuses designers.
  • Blanks in the middle of a list are ignored, shifting elements (use compact format or explicit separators when you need placeholders).
  • map has only key, no value.
  • Container element types marked nullable (current rules generally disallow this).