概览
Tableau 是一款基于 Protobuf (proto3) 的强大配置转换器。
特性
- 将 Excel/CSV/XML/YAML 转换为 JSON/Text/Bin。
- 使用 Protobuf 定义 Excel/CSV/XML/YAML 的结构。
- 使用 Golang 开发转换引擎。
- 得益于 Protobuf (proto3),支持多种编程语言。
概念
- Importer(导入器):
- 将 Excel/CSV 文件导入为内存中的 Table sheet 集合。
- 将 XML/YAML 文件导入为内存中的 Document sheet 集合。
- Parsers(解析器):
- protogen:将 Excel/CSV/XML/YAML 文件转换为 Protoconf 文件。
- confgen:将 Excel/CSV/XML/YAML 与 Protoconf 文件一起转换为 JSON/Text/Bin 文件。
- Exporter(导出器):
- protogen:将 tableau.Workbook 导出为 proto 文件。
- confgen:将 protobuf message 导出为 JSON/Text/Bin 文件。
- Protoconf:Protocol Buffers (proto3) 的一种方言,通过 tableau options 进行扩展,用于描述 Excel/CSV/XML/YAML 的结构。
工作流程
flowchart TD
subgraph Input
I1(Excel)
click I1 href "https://github.com/qax-os/excelize"
I2(CSV)
click I2 href "https://pkg.go.dev/encoding/csv"
I3(XML)
click I3 href "https://github.com/antchfx/xmlquery"
I4(YAML)
click I4 href "https://github.com/go-yaml/yaml"
end
Input --> I[importer]
click I href "https://github.com/tableauio/tableau/tree/master/internal/importer"
subgraph Protogen
PGP[protogen.Parser] --> E1[protogen.Exporter]
click PGP href "https://github.com/tableauio/tableau/tree/master/internal/protogen/parser.go"
click E1 href "https://github.com/tableauio/tableau/tree/master/internal/protogen/exporter.go"
end
I --> Protogen:::orangeclass
subgraph Confgen
CGP[confgen.Parser] --> CE[confgen.Exporter]
click CGP href "https://github.com/tableauio/tableau/tree/master/internal/confgen/parser.go"
click CE href "https://github.com/tableauio/tableau/tree/master/internal/confgen/mexporter"
end
I --> Confgen:::orangeclass
Protogen --> B
B{{Protoconf}}:::greenclass --> | protobuf descriptor | Confgen
subgraph Output
O1("JSON")
click O1 href "https://developers.google.com/protocol-buffers/docs/proto3#json"
O2("Text")
click O2 href "https://developers.google.com/protocol-buffers/docs/text-format-spec"
O3("Bin")
click O3 href "https://developers.google.com/protocol-buffers/docs/encoding"
end
Confgen --> Output
classDef orangeclass fill:#f96;
classDef greenclass fill:#40E0D0;
类型
- Scalar(标量)
- Message(struct,消息)
- List(列表)
- Map(无序映射)
- Timestamp(时间戳)
- Duration(时长)
TODO
protoc 插件
- Golang
- C++
- C#/.NET
- Python
- Lua
- Javascript/Typescript/Node
- Java
Metadata
- metatable:描述 worksheet 元数据的 message。
- metafield:描述 caption 元数据的 message。
- captrow:caption 行,worksheet 中 caption 所在的精确行号。caption 中允许换行以提高可读性,转换时会被去除。
- descrow:description 行,worksheet 中描述所在的精确行号。
- datarow:data 行,数据的起始行号。
主流操作系统中的换行符:
| 操作系统 | 缩写 | 转义序列 |
|---|---|---|
| Unix (linux, OS X) | LF | \n |
| Microsoft Windows | CRLF | \r\n |
| classic Mac OS/OS X | CR | \r |
LF:Line Feed(换行),CR:Carriage Return(回车)。
Generator
- 通过 Excel(header)生成 protoconf:Excel -> protoconf
- 通过 protoconf 生成 Excel(header):protoconf -> Excel
Conversion
- Excel -> JSON(默认格式,人类可读)
- Excel -> protowire(体积小)
- Excel -> prototext(人类调试用)
- JSON -> Excel
- protowire -> Excel
- prototext -> Excel
Pretty Print
- Multiline:每个文本元素单独一行
- Indent:4 个空格字符
- JSON 支持
- prototext 支持
EmitUnpopulated
- JSON:
EmitUnpopulated指定是否输出未填充的字段。
标量类型
- 整数:int32、uint32、int64 和 uint64
- 浮点数:float 和 double
- bool
- string
- bytes
- datetime、date、time、duration
枚举
- enum:解析器接受三种枚举值形式:
- 枚举值编号
- 枚举值名称
- 枚举值别名(通过 EnumValueOptions 指定)
- enum:校验枚举值。
复合类型
- message:横向(行方向)布局,字段位于单元格中。
- message:简单 in-cell message,每个字段必须是标量类型。以逗号分隔的字段列表,例如:
1,test,3.0。列表大小无需与字段数相等,字段按顺序填充,未配置的字段使用标量类型的默认值。 - list:横向(行方向)布局,这是 list 的默认布局,每个元素可以是 message 或标量。
- list:纵向(列方向)布局,每个元素应为 message。
- list:简单 in-cell list,元素必须是标量类型。以逗号分隔的元素列表,例如:
1,2,3。 - list:可扩展或动态大小。
- list:智能识别任意位置的空元素。
- map:横向(行方向)布局。
- map:纵向(列方向)布局,这是 map 的默认布局。
- map:无序 map 或 hash map。
- map:简单 in-cell map,key 和 value 都必须是标量类型。以逗号分隔的
key:value对列表,例如:1:10,2:20,3:30。 - map:可扩展或动态大小。
- map:智能识别任意位置的空值。
- nesting:message、list 和 map 的无限嵌套。
默认值
每种标量类型的默认值与 protobuf 相同。
- 整数:
0 - 浮点数:
0.0 - bool:
false - string:
"" - bytes:
"" - in-cell message:每个字段的默认值与 protobuf 相同
- in-cell list:元素的默认值与 protobuf 相同
- in-cell map:key 和 value 的默认值与 protobuf 相同
- message:所有字段均有默认值
空值
- scalar:默认值与 protobuf 相同。
- message:若所有字段均为空,则不会生成该 message。
- list:若 list 大小为 0,则不会生成该 list。
- list:若 list 的元素(message 类型)为空,则不会追加该元素。
- map:若 map 大小为 0,则不会生成该 map。
- map:若 map 的 value(message 类型)为空,则不会插入该条目。
- nesting:递归地判断是否为空。
合并
- 合并多个 workbook
- 合并多个 worksheet
Workbook meta
workbook meta sheet @TABLEAU:
- 指定要解析的 sheet
- 为每个 sheet 指定解析器选项
| Sheet | Alias | Nameline | Typeline |
|---|---|---|---|
| Sheet1 | ExchangeInfo | 2 | 2 |
Datetime
- Timestamp:基于
google.protobuf.Timestamp,参考 JSON 映射 - Timezone:参考 ParseInLocation
- Datetime:Excel 格式:
yyyy-MM-dd HH:mm:ss,例如:2020-01-01 05:10:00 - Date:Excel 格式:
yyyy-MM-dd或yyyyMMdd,例如:2020-01-01或20200101 - Time:Excel 格式:
HH:mm:ss或HHmmss,例如:05:10:00或051000 - Duration:基于
google.protobuf.Duration,参考 JSON 映射 - Duration:Excel 格式:
"72h3m0.5s"形式,参考 golang duration 字符串格式
Transpose
- 对 worksheet 进行行列转置。
校验
- unique:检查 map key 的唯一性。
- range:
[left,right]。 - refer:
XXXConf.ID。待 tableauio/loader 支持。
错误信息
- 转换失败时报告清晰精确的错误信息,参考编程语言编译器的做法
- 使用 golang template 定义错误信息模板
- 多语言支持,重点支持英文和简体中文
性能
- 压力测试
- 每个 goroutine 处理一个 worksheet
- 多进程模型