Struct
This guide demonstrates different features of excel struct type.
Cross-cell struct
Syntax: <StructType>ColumnType.
Each column name should be prefixed with the same struct variable name, which is just the same as struct type name by default.
For example, a worksheet ItemConf in HelloWorld.xlsx:
| PropertyID | PropertyName | PropertyDesc | 
|---|---|---|
| {Property}int32 | string | string | 
| Property’s ID | Property’s Name | Property’s Description | 
| 1 | Orange | A kind of sour fruit. | 
Note that each column name in ItemConf is prefixed with struct variable name Property which is same as struct type name.
Generated:
hello_world.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};
message ItemConf {
  option (tableau.worksheet) = {name:"ItemConf"};
  Property property = 1 [(tableau.field) = {name:"Property"}];
  message Property {
    int32 id = 1 [(tableau.field) = {name:"ID"}];
    string name = 2 [(tableau.field) = {name:"Name"}];
    string desc = 3 [(tableau.field) = {name:"Desc"}];
  }
}
ItemConf.json
{
    "property":  {
        "id":  1,
        "name":  "Orange",
        "desc":  "A kind of sour fruit."
    }
}
Note
Cross-cell struct is usually used together with:
- cross-cell horizontal/vertical map, as map value type. Map â
- cross-cell horizontal/vertical list, as list element type. List â
Incell struct
Each field type of the struct should be scalar type.
For example, a worksheet ItemConf in HelloWorld.xlsx:
| ID | Prop | 
|---|---|
| map<int32, Item> | {int32 ID,string Name,string Desc}Property | 
| Item’s ID | Item’s property. | 
| 1 | 1,Orange,A good fruit. | 
| 2 | 2,Apple | 
| 3 | 3 | 
The Property column’s type is in-cell struct {int32 ID,string Name,string Desc}Property.
Generated:
hello_world.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};
message ItemConf {
  option (tableau.worksheet) = {name:"ItemConf"};
  map<uint32, Item> item_map = 1 [(tableau.field) = {key:"ID" layout:LAYOUT_VERTICAL}];
  message Item {
    uint32 id = 1 [(tableau.field) = {name:"ID"}];
    Property prop = 2 [(tableau.field) = {name:"Prop" span:SPAN_INNER_CELL}];
    message Property {
      int32 id = 1 [(tableau.field) = {name:"ID"}];
      string name = 2 [(tableau.field) = {name:"Name"}];
      string desc = 3 [(tableau.field) = {name:"Desc"}];
    }
  }
}
ItemConf.json
{
    "itemMap":  {
        "1":  {
            "id":  1,
            "prop":  {
                "id":  1,
                "name":  "Apple",
                "desc":  "A kind of delicious fruit."
            }
        },
        "2":  {
            "id":  2,
            "prop":  {
                "id":  2,
                "name":  "Orange",
                "desc":  ""
            }
        },
        "3":  {
            "id":  3,
            "prop":  {
                "id":  3,
                "name":  "",
                "desc":  ""
            }
        }
    }
}
Predefined struct
For example, struct type Prop in common.proto is defined as:
message Prop {
  int32 id = 1 [(tableau.field).name = "ID"];
  int32 value = 2 [(tableau.field).name = "Value"];
}
A worksheet ItemConf in HelloWorld.xlsx:
| ID | Prop1ID | Prop1Value | Prop2ID | Prop2Value | 
|---|---|---|---|---|
| map<uint32, Item> | [.Prop]int32 | int32 | int32 | int32 | 
| Item’s ID | Prop1’s ID | Prop1’s value | Prop2’s ID | Prop2’s value | 
| 1 | 1 | 100 | 2 | 200 | 
| 2 | 3 | 300 | 4 | 400 | 
| 3 | 5 | 500 | 
Generated:
hello_world.proto
// --snip--
import "common.proto";
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};
message ItemConf {
  option (tableau.worksheet) = {name:"ItemConf"};
  map<uint32, Item> item_map = 1 [(tableau.field) = {key:"ID" layout:LAYOUT_VERTICAL}];
  message Item {
    uint32 id = 1 [(tableau.field) = {name:"ID"}];
    repeated Prop prop_list = 2 [(tableau.field) = {name:"Prop" layout:LAYOUT_HORIZONTAL}];
  }
}
ItemConf.json
{
    "itemMap":  {
        "1":  {
            "id":  1,
            "propList":  [
                {
                    "id":  1,
                    "value":  100
                },
                {
                    "id":  2,
                    "value":  200
                }
            ]
        },
        "2":  {
            "id":  2,
            "propList":  [
                {
                    "id":  3,
                    "value":  300
                },
                {
                    "id":  4,
                    "value":  400
                }
            ]
        },
        "3":  {
            "id":  3,
            "propList":  [
                {
                    "id":  5,
                    "value":  500
                }
            ]
        }
    }
}
Predefined incell struct
Each field type of the predefined struct should be scalar type.
For example, Property in common.proto is predefined as:
message Property {
  int32 id = 1 [(tableau.field) = {name:"ID"}];
  string name = 2 [(tableau.field) = {name:"Name"}];
  string desc = 3 [(tableau.field) = {name:"Desc"}];
}
A worksheet ItemConf in HelloWorld.xlsx:
| ID | Prop | 
|---|---|
| map<uint32, Item> | {.Property} | 
| Item’s ID | Item’s property. | 
| 1 | 1,Orange,A good fruit. | 
| 2 | 2,Apple | 
| 3 | 3 | 
The Prop column’s type is a predefined struct Property.
Generated:
hello_world.proto
// --snip--
import "common.proto";
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};
message ItemConf {
  option (tableau.worksheet) = {name:"ItemConf"};
  map<uint32, Item> item_map = 1 [(tableau.field) = {key:"ID" layout:LAYOUT_VERTICAL}];
  message Item {
    uint32 id = 1 [(tableau.field) = {name:"ID"}];
    protoconf.Property prop = 2 [(tableau.field) = {name:"Prop" span:SPAN_INNER_CELL}];
  }
}
ItemConf.json
{
    "itemMap":  {
        "1":  {
            "id":  1,
            "prop":  {
                "id":  1,
                "name":  "Apple",
                "desc":  "A kind of delicious fruit."
            }
        },
        "2":  {
            "id":  2,
            "prop":  {
                "id":  2,
                "name":  "Orange",
                "desc":  ""
            }
        },
        "3":  {
            "id":  3,
            "prop":  {
                "id":  3,
                "name":  "",
                "desc":  ""
            }
        }
    }
}
Custom named struct
By default, struct variable name is same as struct type name, but you can specify a different struct variable name. Custom named struct is mainly used to identify name prefix of continuous cells in name row, when the tableau (protogen) can’t auto-recognize the variable name.
Syntax: just after struct type name, use parentheses () to specify struct variable name: VariableType(VariableName).
For example, Item is predefined:
message Item {
  int32 id = 1 [(tableau.field).name = "ID"];
  int32 num = 2 [(tableau.field).name = "Num"];
}
A worksheet ItemConf in HelloWorld.xlsx:
| RewardItemID | RewardItemNum | CostItemID | CostItemNum | PredefinedItemID | PredefinedItemNum | 
|---|---|---|---|---|---|
| {Item(RewardItem)}int32 | int32 | {Item(CostItem)}int32 | int32 | {.Item(PredefinedItem)}int32 | int32 | 
| Item’s ID | Item’s ID | Cost’s ID | Cost’s ID | Predefined item’s ID | Predefined item’s ID | 
| 1 | 100 | 2 | 200 | 10 | 20 | 
Details:
In type cell {Item(RewardItem)}int32, RewardItem is the custom variable name of new defined struct Item. And in type cell {Item(CostItem)}int32, CostItem is the custom variable name of just already defined struct Item in the same scope. Finally, in type cell {.Item(PredefinedItem)}int32, PredefinedItem is the custom variable name of predefined struct Item at global (at the same protobuf package).
Generated:
hello_world.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};
message ItemConf {
  option (tableau.worksheet) = {name:"ItemConf"};
  Item reward_item = 1 [(tableau.field) = {name:"RewardItem"}];
  message Item {
    int32 id = 1 [(tableau.field) = {name:"ID"}];
    int32 num = 2 [(tableau.field) = {name:"Num"}];
  }
  Item cost_item = 2 [(tableau.field) = {name:"CostItem"}];
  protoconf.Item predefined_item = 3 [(tableau.field) = {name:"PredefinedItem"}];
}
ItemConf.json
{
    "rewardItem": {
        "id": 1,
        "num": 100
    },
    "costItem": {
        "id": 2,
        "num": 200
    },
    "predefinedItem": {
        "id": 10,
        "num": 20
    }
}
Advanced predefined incell struct
In some situations, you may want to configure any complex struct in a cell, so tableau support two kinds of protobuf serialized formats: text format, and JSON format.
Syntax: in field prop, specify form option as FORM_TEXT or FORM_JSON.
For example, Transform is predefined as:
message Transform {
  Vector3 position = 1;
  Vector3 rotation = 2;
  Vector3 scale = 3;
}
message Vector3 {
  float x = 1;
  float y = 2;
  float z = 3;
}
A worksheet ItemConf in HelloWorld.xlsx:
| Transform1 | Transform2 | 
|---|---|
| {.Transform}|{form:FORM_TEXT} | {.Transform}|{form:FORM_JSON} | 
| Box’s transform1 | Box’s transform2 | 
| position:{x:1 y:2 z:3} rotation:{x:4 y:5 z:6} scale:{x:7 y:8 z:9} | {“position”:{“x”:1, “y”:2, “z”:3}, “rotation”:{“x”:4, “y”:5, “z”:6}, “scale”:{“x”:7, “y”:8, “z”:9}} | 
Generated:
hello_world.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};
message ItemConf {
  option (tableau.worksheet) = {name:"ItemConf"};
  protoconf.Transform transform_1 = 1 [(tableau.field) = {name:"Transform1" span:SPAN_INNER_CELL prop{form:FORM_TEXT}}];
  protoconf.Transform transform_2 = 2 [(tableau.field) = {name:"Transform2" span:SPAN_INNER_CELL prop:{form:FORM_JSON}}];
}
ItemConf.json
{
    "transform1":  {
        "position":  {
            "x":  1,
            "y":  2,
            "z":  3
        },
        "rotation":  {
            "x":  4,
            "y":  5,
            "z":  6
        },
        "scale":  {
            "x":  7,
            "y":  8,
            "z":  9
        }
    },
    "transform2":  {
        "position":  {
            "x":  1,
            "y":  2,
            "z":  3
        },
        "rotation":  {
            "x":  4,
            "y":  5,
            "z":  6
        },
        "scale":  {
            "x":  7,
            "y":  8,
            "z":  9
        }
    }
}
Define struct type in sheet
There are two kinds of Mode (in metasheet @TABLEAU) to define struct types in a sheet:
- MODE_STRUCT_TYPE: define single struct type in a sheet.
- MODE_STRUCT_TYPE_MULTI: define multiple struct types in a sheet.
Single struct type in sheet
You should specify Mode option to MODE_STRUCT_TYPE in metasheet @TABLEAU.
For example, a worksheet Item in HelloWorld.xlsx:
| Name | Type | 
|---|---|
| ID | uint32 | 
| Num | int32 | 
| FruitType | enum<.FruitType> | 
| Feature | []int32 | 
| Prop | map<int32, string> | 
| Detail | {enum<.ItemType> Type, string Name, string Desc}Detail | 
| Sheet | Mode | 
|---|---|
| Item | MODE_STRUCT_TYPE | 
Generated:
hello_world.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx"};
// Generated from sheet: Item.
message Item {
  uint32 id = 1 [(tableau.field) = {name:"ID"}];
  int32 num = 2 [(tableau.field) = {name:"Num"}];
  protoconf.FruitType fruit_type = 3 [(tableau.field) = {name:"FruitType"}];
  repeated int32 feature_list = 4 [(tableau.field) = {name:"Feature" layout:LAYOUT_INCELL}];
  map<int32, string> prop_map = 5 [(tableau.field) = {name:"Prop" layout:LAYOUT_INCELL}];
  Detail detail = 6 [(tableau.field) = {name:"Detail" span:SPAN_INNER_CELL}];
  message Detail {
    protoconf.ItemType type = 1 [(tableau.field) = {name:"Type"}];
    string name = 2 [(tableau.field) = {name:"Name"}];
    string desc = 3 [(tableau.field) = {name:"Desc"}];
  }
}
Multiple struct types in sheet
A block defines a struct type, and it is a series of contiguous non-empty rows. So different blocks are seperated by one or more empty rows.
You should specify Mode option to MODE_STRUCT_TYPE_MULTI in metasheet @TABLEAU.
For example, a worksheet Item in HelloWorld.xlsx:
| Tree | Tree note | 
|---|---|
| Name | Type | 
| ID | uint32 | 
| Num | int32 | 
| Pet | Pet note | 
| Name | Type | 
| Kind | int32 | 
| Tip | []string | 
| FruitShop | FruitShop note | 
| Name | Type | 
| FruitType | enum<.FruitType> | 
| Prop | map<int32, string> | 
| Sheet | Mode | 
|---|---|
| Item | MODE_STRUCT_TYPE_MULTI | 
Generated:
hello_world.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx"};
message Tree {
  option (tableau.struct) = {name:"StructType" note:"Tree note"};
  uint32 id = 1 [(tableau.field) = {name:"ID"}];
  int32 num = 2 [(tableau.field) = {name:"Num"}];
}
message Pet {
  option (tableau.struct) = {name:"StructType" note:"Pet note"};
  int32 kind = 1 [(tableau.field) = {name:"Kind"}];
  repeated string tip_list = 2 [(tableau.field) = {name:"Tip" layout:LAYOUT_INCELL}];
}
message FruitShop {
  option (tableau.struct) = {name:"StructType" note:"FruitShop note"};
  protoconf.FruitType fruit_type = 1 [(tableau.field) = {name:"FruitType"}];
  map<int32, string> prop_map = 2 [(tableau.field) = {name:"Prop" layout:LAYOUT_INCELL}];
}
Specify Number column
In Number column, you can specify custom unique field number.
For example, a worksheet Item in HelloWorld.xlsx:
| Number | Name | Type | 
|---|---|---|
| 1 | ID | uint32 | 
| 20 | Num | int32 | 
| 30 | FruitType | enum<.FruitType> | 
| Sheet | Mode | 
|---|---|
| Item | MODE_STRUCT_TYPE | 
Generated:
hello_world.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx"};
// Generated from sheet: Item.
message Item {
  uint32 id = 1 [(tableau.field) = {name:"ID"}];
  int32 num = 20 [(tableau.field) = {name:"Num"}];
  protoconf.FruitType fruit_type = 30 [(tableau.field) = {name:"FruitType"}];
}