Skip to main content
Version: Next

Vertical Tables, sep, and Stream Filling

Composite data (beans, containers) has four common read modes in Excel:

#ModeNotes
1Stream, multi-cellSpan columns; read left to right; blanks often skipped
2Stream, single cell + sepOne cell split by separators into a data stream
3Column constraint, multi-cellChild ##var rows pin each sub-field to a column; atomics support empty defaults
4Multi-row (containers only)Field name *name; one element per row; each element can use modes 1–3

stream multi-cell stream single-cell sep column constraint multi-row

Column constraints can nest; multi-row applies only to container types.

Vertical tables (common for singleton config)

Horizontal table: one record per row. Vertical table: A1 is ##column or ##vertical, one field per row, good for global config.

Schema

<table name="TbGlobal" value="GlobalConfig" mode="one" input="global.xlsx"/>
<bean name="GlobalConfig">
<var name="bag_init_size" type="int"/>
<var name="bag_max_size" type="int"/>
<var name="guild_open_level" type="int"/>
</bean>

Horizontal form (for comparison)

##varbag_init_sizebag_max_sizeguild_open_level
##typeintintint
2010010

Vertical form

A1 must be ##column (or ##vertical):

##column##type##
bag_init_sizeint20
bag_max_sizeint100
guild_open_levelint10

sep: write composite data in one cell

If giving each field its own column makes the table too wide, use sep=<char> to split within one cell.

sep can be written on:

LocationExample
Excel field namepos#sep=,
bean tags<bean name="Vec3" tags="sep=,">
typeType1#sep=,, (list#sep=\|),int

Multiple characters mean “any of these characters is a separator”, not that the whole string is one separator. When # / & are separators, write \# / \&.

Where sep appears and what it does:

LocationBehavior
Excel field name (e.g. x#sep=,)Split each cell in that column range by sep, then read in stream mode
bean tags (e.g. <bean tags="sep=,">)Whole string split by sep, then stream-read bean fields
type tag (e.g. Vec#sep=,, (list#sep=\|),int)Next token is the whole value; split by sep and stream-read
container typeSee below

Two ways to apply sep on containers:

  • On the container itself (e.g. list#sep=|): next string is the whole container; split by sep and read elements.
  • On the element type (e.g. list,(Vec#sep=,)): each element segment is split separately.

Combined example: (list#sep=|),(Vec#sep=,) — list elements separated by |, Vec by ,.

sep read bean sep read plain container sep read struct container

Example: Vec3

<bean name="Vec3" sep=",">
<var name="x" type="float"/>
<var name="y" type="float"/>
<var name="z" type="float"/>
</bean>
##varidpos
##typeintVec3
11.0,2.0,3.0
20,0,1

Or declare only on the field name: pos#sep=,, with type still Vec3.

Example: nesting + sep

<bean name="Type1">
<var name="a" type="int"/>
<var name="b" type="string"/>
<var name="c" type="bool"/>
</bean>
<bean name="Type3">
<var name="a" type="int"/>
<var name="b" type="bool"/>
<var name="c" type="Type1#sep=,"/>
</bean>
##varidabc
##typeintintboolType1#sep=,
110true1,hello,false

c = { a:1, b:"hello", c:false }.

Example: list + sep

Field name nums, type (list#sep=|),int:

##varidnums
##typeint(list#sep=|),int
11|3|5|9

nums = [1,3,5,9].

Stream semantics

When stream mode applies: non-atomic data (bean/container) is limited to a column range or a sep segment, and sub-fields are not column-constrained — child data is read in stream order.

BehaviorNotes
Blank cellOften skipped (cannot distinguish blank from default)
Nullable beannull / {} / type name have special rules
End of containerRead until } or end of stream

Therefore “leave empty for default” does not work in sep/stream cells — fill explicit defaults:

TypeEmpty/default in stream mode
boolMust fill false / true
int / floatMust fill 0 or another valid number
stringEmpty string as ""
Nullable (e.g. int?)null
ContainerEmpty container ends with }

stream example

Red rows that leave bool/string blanks are skipped and cause “insufficient data” errors.

Stream read rules by type:

  • Polymorphic bean: read type name string, then stream-read subclass fields
  • Nullable bean: read string first; null = empty; {} or type name = non-null and continue. Valid Vec3: 1,2,3, null, {},1,2,3, vec3,1,2,3
  • array / list / set: stop on } or end of stream; else read elements in a loop
  • map: read key/value pairs in a loop until } or end of stream

Compare with column constraints: when pinned to atomic columns, empty can mean default; bean/container interiors without sub-field pins still use stream mode.

Common pitfalls

  • Forgot to change A1 to ##column for a vertical table.
  • Number of sep segments does not match the number of fields.
  • Used blanks for 0/false in a stream cell, causing field misalignment.