Metasheet(元表)

Metasheet 是一个名为 "@TABLEAU" 的 worksheet,用于指定 tableau 解析器的 工作表选项。

概述

以下选项可在 metasheet @TABLEAU 中指定,用于影响对应 worksheet 的布局、功能、loader 等。

选项类型说明
Sheetstring要处理的 worksheet 名称。特别地,# 表示 workbook 名称,可用于设置 workbook 的 Alias
Aliasstring对于 worksheet,alias 用作 proto message 名称。对于 workbook #,alias 用作 proto 文件名(不含扩展名)。
Namerowint32worksheet 中列名定义所在的精确行号。
默认值:1
Typerowint32worksheet 中列类型定义所在的精确行号。
默认值:2
Noterowint32worksheet 中列注释定义所在的精确行号。
默认值:3
Datarowint32worksheet 中数据起始行号。
默认值:4
Namelineint32单元格中列名定义所在的行号。0 表示整个单元格。
默认值:0
Typelineint32单元格中列类型定义所在的行号。0 表示整个单元格。
默认值:0
Transposebool对指定 sheet 进行行列转置。
Nestedboolnamerow 的嵌套命名。
默认值:false
Sepstring工作表的分隔符。
Subsepstring工作表的子分隔符。
Merger[]string将多个具有相同结构的 sheet(逗号分隔)合并为一个。
每个元素可以是:
- 仅 workbook 文件路径或 glob 路径(相对于当前 workbook):<Workbook>,此时 sheet 名称与当前 sheet 相同。
- workbook 文件路径(相对于当前 workbook)加 worksheet 名称:<Workbook>#<Worksheet>
AdjacentKeybool合并具有相同 key 的相邻行。如果 key 单元格未设置,则视为与同列上方最近的 key 相同。
默认值:false
FieldPresencebool为了追踪基本类型(数值、字符串、bytes 和枚举)的字段存在性,生成的字段将标记为 optional
默认值:false
ModeModeSheet 模式。
可用模式:
- MODE_ENUM_TYPE
- MODE_ENUM_TYPE_MULTI
- MODE_STRUCT_TYPE
- MODE_STRUCT_TYPE_MULTI
- MODE_UNION_TYPE
- MODE_UNION_TYPE_MULTI
Scatter[]string将多个具有相同 schema 的 sheet(逗号分隔)分别转换为不同的配置文件。
每个元素可以是:
- workbook 名称或 Glob(相对于当前 workbook):<Workbook>,此时 sheet 名称与当前 sheet 相同。
- workbook 名称(相对于当前 workbook)加 worksheet 名称:<Workbook>#<Worksheet>
Optionalbool该 sheet 中所有字段是否均为可选(字段名存在性)。
PatchPatchSheet patch 类型。
- PATCH_REPLACE
- PATCH_MERGE
WithParentDirboolconfgen:导出 JSON/Bin/Text 文件时创建父目录。
ScatterWithoutBookNameboolconfgen(scatter):导出 JSON/Bin/Text 文件名时不带 book 名称前缀。
OrderedMapbool是否生成 OrderedMap 访问器。
Index[]string生成 index 访问器。
- 单列 Index 格式:Column<ColumnX,ColumnY,...>@IndexName
- 多列 Index 格式:(Column1,Column2,...)<ColumnX,ColumnY,...>@IndexName
OrderedIndex[]string生成 OrderedIndex 访问器。
- 单列 OrderedIndex 格式:Column<ColumnX,ColumnY,...>@IndexName
- 多列 OrderedIndex 格式:(Column1,Column2,...)<ColumnX,ColumnY,...>@IndexName
LangOptionsmap<string, string>指定 loader 语言选项。
有效 key:OrderedMapIndex
不同 kv 之间用 , 分隔,key 和 value 之间用 : 分隔。
如果某个 key 不在 map 中,表示该 loader 选项在所有语言中均支持。
有效 value 为 cppgo 的任意组合(以空格分隔)。
示例:
- OrderedMap:cpp,Index:cpp go // ordered map 仅支持 cpp,index 支持 cpp 和 go
- OrderedMap:cpp // ordered map 仅支持 cpp,index 支持所有语言

@TABLEAU

如果 metasheet @TABLEAU 为空,则同一 workbook 中的所有其他 worksheet 都会被处理。

简单示例

HelloWorld.xlsx 中有一个 worksheet Sheet1,我们希望将其重命名为 ItemConf,定义自定义分隔符为 |,并生成 ordered map 访问器。

因此,HelloWorld.xlsx 中的 metasheet @TABLEAU 应配置如下:

IDName
map<uint32, Item>string
Item’s IDItem’s Name
1Apple
2Orange
3Banana
SheetAliasSepOrderedMap
Sheet1ItemConf|true

Workbook Alias

生成的 proto 文件名是输入文件名的 snake_case 形式。例如,如果 workbook 名为 HelloWorld.xlsx,则生成的 proto 文件名为 hello_world.proto。如果希望手动指定生成的 proto 文件名,可以使用 Alias 选项。在此场景中,# 表示 workbook 名称。

HelloWorld.xlsx 中的 worksheet ItemConf

IDName
map<uint32, Item>string
Item’s IDItem’s Name
1Apple
2Orange
3Banana
SheetAlias
#custom_conf
Sheet1ItemConf

生成结果:

custom_conf.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"}];
    string name = 2 [(tableau.field) = {name:"Name"}];
  }
}

选项 Mode

Sheet mode 定义了 tableauc(protogen)解析 sheet 的方式:数据或类型。

可用模式:

  • MODE_DEFAULT:默认模式,定义 sheet 的数据结构。
  • MODE_ENUM_TYPE:在一个 sheet 中定义单个枚举类型,参见 示例
  • MODE_ENUM_TYPE_MULTI:在一个 sheet 中定义多个枚举类型,参见 示例
  • MODE_STRUCT_TYPE:在一个 sheet 中定义单个 struct 类型,参见 示例
  • MODE_STRUCT_TYPE_MULTI:在一个 sheet 中定义多个 struct 类型,参见 示例
  • MODE_UNION_TYPE:在一个 sheet 中定义单个 union 类型,参见 示例
  • MODE_UNION_TYPE_MULTI:在一个 sheet 中定义多个 union 类型,参见 示例

选项 Transpose

在线性代数中,矩阵的转置是将矩阵沿对角线翻转的操作。类似地,sheet(二维矩阵)的转置意味着将行与列互换。

参见 Excel: 将数据从行转置(旋转)到列,反之亦然

在 metasheet @TABLEAU 中将 Transpose 选项设置为 true

HelloWorld.xlsx 中的 worksheet HeroConf

IDint32Hero’s ID123
NamestringHero’s nameRobin
DescstringHero’s descriptionA big hero!
Skill[]int32Hero’s skills100,101,102
SheetTranspose
HeroConftrue

生成结果:

hello_world.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};

message HeroConf {
  option (tableau.worksheet) = {name:"HeroConf" transpose:true};

  int32 id = 1 [(tableau.field) = {name:"ID"}];
  string name = 2 [(tableau.field) = {name:"Name"}];
  string desc = 3 [(tableau.field) = {name:"Desc"}];
  repeated int32 skill_list = 4 [(tableau.field) = {name:"Skill" layout:LAYOUT_INCELL}];
}
HeroConf.json
{
    "id": 123,
    "name": "Robin",
    "desc": "A big hero!",
    "skillList": [100, 101, 102]
}

选项 Merger

Merger 选项用于将多个具有相同 schema 的 sheet(逗号分隔)合并为一个。

每个元素可以是:

  1. 仅 workbook 文件路径或 Glob 路径(相对于当前 workbook):<Workbook>,此时 sheet 名称与当前 sheet 相同。
  2. workbook 文件路径(相对于当前 workbook)加 worksheet 名称:<Workbook>#<Worksheet>

合并多个 workbook

例如,有三个 workbook,每个都包含一个具有相同 schema 的 worksheet ZoneConf

  • MergerMain.xlsx(主):包含 @TABLEAU metasheet,在 Merger 列中使用 Glob 模式 Merger*.xlsx 匹配所有子 workbook。
  • Merger2.xlsx(子):仅包含数据 worksheet,无需 @TABLEAU metasheet。
  • Merger3.xlsx(子):仅包含数据 worksheet,无需 @TABLEAU metasheet。

第一个(主)workbook:MergerMain.xlsx 中的 worksheet ZoneConf(含 @TABLEAU):

IDNameDifficulty
map<uint32, Zone>stringint32
Zone’s IDZone’s nameZone’s difficulty
1Infinity100
SheetMerger
ZoneConfMerger*.xlsx

第二个(子)workbook:Merger2.xlsx 中的 worksheet ZoneConf(不含 @TABLEAU):

IDNameDifficulty
map<uint32, Zone>stringint32
Zone’s IDZone’s nameZone’s difficulty
2Desert200

第三个(子)workbook:Merger3.xlsx 中的 worksheet ZoneConf(不含 @TABLEAU):

IDNameDifficulty
map<uint32, Zone>stringint32
Zone’s IDZone’s nameZone’s difficulty
3Snowfield300

生成结果:

merger_main.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};

message ZoneConf {
  option (tableau.worksheet) = {name:"ZoneConf" merger:"Merger*.xlsx"};

  map<uint32, Zone> zone_map = 1 [(tableau.field) = {key:"ID" layout:LAYOUT_VERTICAL}];
  message Zone {
    uint32 id = 1 [(tableau.field) = {name:"ID"}];
    string name = 2 [(tableau.field) = {name:"Name"}];
    int32 difficulty = 3 [(tableau.field) = {name:"Difficulty"}];
  }
}
ZoneConf.json
{
    "zoneMap": {
        "1": {"id": 1, "name": "Infinity", "difficulty": 100},
        "2": {"id": 2, "name": "Desert", "difficulty": 200},
        "3": {"id": 3, "name": "Snowfield", "difficulty": 300}
    }
}

合并同一 workbook 中的多个 sheet

例如,同一 workbook Merger.xlsx 中有三个具有相同 schema 的 worksheet:

  • ZoneConf(主 sheet,含 @TABLEAU
  • ZoneConf2(子 sheet)
  • ZoneConf3(子 sheet)

主(也是唯一的)workbook:Merger.xlsx 中的 worksheet ZoneConfZoneConf2ZoneConf3@TABLEAU

IDNameDifficulty
map<uint32, Zone>stringint32
Zone’s IDZone’s nameZone’s difficulty
1Infinity100
IDNameDifficulty
map<uint32, Zone>stringint32
Zone’s IDZone’s nameZone’s difficulty
2Desert200
IDNameDifficulty
map<uint32, Zone>stringint32
Zone’s IDZone’s nameZone’s difficulty
3Snowfield300
SheetMerger
ZoneConfMerger.xlsx#ZoneConf2,Merger.xlsx#ZoneConf3

生成结果:

merger_same.proto
// --snip--
option (tableau.workbook) = {name:"Merger.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};

message ZoneConf {
  option (tableau.worksheet) = {name:"ZoneConf" merger:"Merger.xlsx#ZoneConf2,Merger.xlsx#ZoneConf3"};

  map<uint32, Zone> zone_map = 1 [(tableau.field) = {key:"ID" layout:LAYOUT_VERTICAL}];
  message Zone {
    uint32 id = 1 [(tableau.field) = {name:"ID"}];
    string name = 2 [(tableau.field) = {name:"Name"}];
    int32 difficulty = 3 [(tableau.field) = {name:"Difficulty"}];
  }
}
ZoneConf.json
{
    "zoneMap": {
        "1": {
            "id": 1,
            "name": "Infinity",
            "difficulty": 100
        },
        "2": {
            "id": 2,
            "name": "Desert",
            "difficulty": 200
        },
        "3": {
            "id": 3,
            "name": "Snowfield",
            "difficulty": 300
        }
    }
}

选项 Scatter

Scatter 选项用于将多个具有相同 schema 的 sheet(逗号分隔)分别转换为不同的配置文件。

每个元素可以是:

  1. 仅 workbook 文件路径或 Glob 路径(相对于当前 workbook):<Workbook>,此时 sheet 名称与当前 sheet 相同。
  2. workbook 文件路径(相对于当前 workbook)加 worksheet 名称:<Workbook>#<Worksheet>

例如,有三个 workbook(每个具有相同的 sheet schema,Scatter1.xlsx 为主 workbook):

  • Scatter1.xlsx
  • Scatter2.xlsx
  • Scatter3.xlsx

第一个(主)workbook:Scatter1.xlsx 中的 worksheet ZoneConf(含 @TABLEAU):

IDNameDifficulty
map<uint32, Zone>stringint32
Zone’s IDZone’s nameZone’s difficulty
1Infinity100
SheetScatter
ZoneConfScatter*.xlsx

生成的 protoconf:

scatter_1.proto
// --snip--
option (tableau.workbook) = {name:"HelloWorld.xlsx" namerow:1 typerow:2 noterow:3 datarow:4};

message ZoneConf {
  option (tableau.worksheet) = {name:"ZoneConf" scatter:"Scatter*.xlsx"};

  map<uint32, Zone> zone_map = 1 [(tableau.field) = {key:"ID" layout:LAYOUT_VERTICAL}];
  message Zone {
    uint32 id = 1 [(tableau.field) = {name:"ID"}];
    string name = 2 [(tableau.field) = {name:"Name"}];
    int32 difficulty = 3 [(tableau.field) = {name:"Difficulty"}];
  }
}

预期生成三个不同的配置文件(命名模式:<BookName>_<SheetName>):

Scatter1_ZoneConf.json
{"zoneMap": {"1": {"id": 1, "name": "Infinity", "difficulty": 100}}}
Scatter2_ZoneConf.json
{"zoneMap": {"2": {"id": 2, "name": "Desert", "difficulty": 200}}}
Scatter3_ZoneConf.json
{"zoneMap": {"3": {"id": 3, "name": "Snowfield", "difficulty": 300}}}

选项 OrderedMap

📢 仅适用于每个层级 message 的第一个 map 字段。

如果将 OrderedMap 设置为 true,则 tableau loader 插件将生成 ordered map API:

选项 Index

Index 选项可用于生成 index 访问器。 有两种 index:

  1. 单列 Index
  2. 多列 Index(又称 Composite Index)

如果正确设置了 Index,则 tableau loader 插件将生成 index API:

每列类型可以是:

  • scalar:数值、布尔值、字符串和 bytes。
  • enum:例如:enum<.FruitType>
  • incell scalar list:例如:[]int32
  • incell enum list:例如:[]enum<.FruitType>

示例:HelloWorld.xlsx 中的两个 worksheet ItemConfShopConf

  • ItemConf:对 map value 同一 struct 的列建立 index。
  • ShopConf:对 list element 同一 struct 的列建立 index。
IDNameDesc
map<int32, Item>stringstring
Item’s IDItem’s nameItem’s desc
1AppleA kind of delicious fruit.
2OrangeA kind of sour fruit.
3BananaA kind of calorie-rich fruit.
IDTypeDesc
[Shop]int32int32string
Shop’s IDShop’s typeShop’s desc
11Shoes shop.
21T-Shirt shop.
32Fruite shop.
SheetIndex
ItemConfID@Item, Name@AwardItem, (ID,Name)@SpecialItem
ShopConfID@Shop, Type@ThemeShop, (ID,Type)@SpecialShop

单列 Index

格式:Column<ColumnX,ColumnY,...>@IndexName

@ 是列名和 index 名之间的分隔符。如果未设置 IndexName,则使用该列的父 struct 类型名。可以用逗号分隔指定一个或多个 index。尖括号 <> 中的列指定排序列,结果数组按相同 index key 排序。

示例:

  • ID
  • ID@Item
  • ID<ID>@Item:结果数组按 ID 排序。
  • ID<Type,Priority>@Item:结果数组按 Type 和 Priority 排序。
  • ID, Name@AwardItem
  • ID@Item, Name@AwardItem

多列 Index

格式:(Column1,Column2,...)<ColumnX,ColumnY,...>@IndexName

多列 Index(又称 Composite Index)由同一 struct(list 或 map 中)的多列组成,以提高查询速度。

@ 是括号内列名和 index 名之间的分隔符。如果未设置 IndexName,则使用该列的父 struct 类型名。可以用逗号分隔指定一个或多个 index。尖括号 <> 中的列指定排序列,结果数组按相同 index key 排序。

示例:

  • (ID,Name):未设置 index 名,由父 struct 类型名决定。
  • (ID,Name)@AwardItem
  • (ID,Name)<ID>:结果数组按 ID 排序。
  • (ID,Type)<Type,Priority>@Item:结果数组按 Type 和 Priority 排序。
  • ID@Item, (ID,Name)@AwardItem:一个单列 index 和一个多列 index。

选项 OrderedIndex

OrderedIndex 选项可用于生成 ordered index 访问器。 有两种 ordered index:

  1. 单列 OrderedIndex
  2. 多列 OrderedIndex(又称 Composite OrderedIndex)

如果正确设置了 OrderedIndex,则 tableau loader 插件将生成 index API:

每列类型可以是:

  • scalar:数值、布尔值、字符串和 bytes。
  • enum:例如:enum<.FruitType>
  • incell scalar list:例如:[]int32
  • incell enum list:例如:[]enum<.FruitType>

示例:HelloWorld.xlsx 中的两个 worksheet ItemConfShopConf

  • ItemConf:对 map value 同一 struct 的列建立 ordered index。
  • ShopConf:对 list element 同一 struct 的列建立 ordered index。
IDNameDesc
map<int32, Item>stringstring
Item’s IDItem’s nameItem’s desc
1AppleA kind of delicious fruit.
2OrangeA kind of sour fruit.
3BananaA kind of calorie-rich fruit.
IDTypeDesc
[Shop]int32int32string
Shop’s IDShop’s typeShop’s desc
11Shoes shop.
21T-Shirt shop.
32Fruite shop.
SheetOrderedIndex
ItemConfID@Item, Name@AwardItem, (ID,Name)@SpecialItem
ShopConfID@Shop, Type@ThemeShop, (ID,Type)@SpecialShop

单列 OrderedIndex

格式:Column<ColumnX,ColumnY,...>@IndexName

@ 是列名和 index 名之间的分隔符。如果未设置 IndexName,则使用该列的父 struct 类型名。可以用逗号分隔指定一个或多个 index。尖括号 <> 中的列指定排序列,结果数组按相同 index key 排序。

示例:

  • ID
  • ID@Item
  • ID<ID>@Item:结果数组按 ID 排序。
  • ID<Type,Priority>@Item:结果数组按 Type 和 Priority 排序。
  • ID, Name@AwardItem
  • ID@Item, Name@AwardItem

多列 OrderedIndex

⚠️ 暂不支持。

选项 Patch

// 工作表和字段级别的 Patch 类型。
enum Patch {
  PATCH_NONE = 0;
  // 1 工作表 patch 选项 "PATCH_REPLACE"
  //   - 替换整个 message
  // 2 顶层字段 patch 选项 "PATCH_REPLACE"
  //   - list:先清空字段,然后将 src 中该 list 字段的所有元素追加到 dst 对应的 list 字段。
  //   - map:先清空字段,然后将 src 中该 map 字段的所有条目复制到 dst 对应的 map 字段。
  PATCH_REPLACE = 1;
  // 将 src 合并到 dst(必须是具有相同 descriptor 的 message)。
  //  - scalar:src 中已填充的 scalar 字段复制到 dst。
  //  - message:src 中已填充的单数 message 通过递归调用 [proto.Merge](https://pkg.go.dev/google.golang.org/protobuf/proto#Merge) 合并到 dst。
  //  - list:src 中每个 list 字段的元素追加到 dst 对应的 list 字段。
  //  - map:src 中每个 map 字段的条目复制到 dst 对应的 map 字段,可能替换已有条目。
  //  - unknown:src 的 unknown 字段追加到 dst 的 unknown 字段。
  PATCH_MERGE = 2;
}

选项 Sep

工作表的分隔符,用于分隔:

  • incell list 元素(scalar 或 struct)。
  • incell map 元素。

如果未设置,将使用 Tableauc 配置 中的全局级别分隔符(默认:,)。

选项 Subsep

工作表的子分隔符,用于分隔:

  • 每个 incell map 元素的键值对。
  • 每个 incell struct list 元素的结构体字段。

如果未设置,将使用 Tableauc 配置 中的全局级别子分隔符(默认::)。