excelexporter
July 10, 2026 · View on GitHub
适用于游戏项目的Excel配置表导出工具
Excel导出
- 数据结构定义在proto文件中
- 解析proto文件,获取proto中的message的结构信息
- 解析Excel配置表,列名就是proto中定义的message的字段名
- 导出为proto对应的json或pb格式(protobuf序列化后的二进制数据,以便于更高效的加载)
- 支持批量导出,在一个excel里配置所有需要导出的配置表,可以批量导出并生成加载代码,把加载代码放到项目中, 可以一个接口就完成加载所有数据,并支持并发,热更新,增量加载
- 支持配置表关联检查
- 支持不同的配置表合并导出到同一个文件里
- 测试用例在tool/export_test.go
项目导入
- 加载导出的json或pb数据,直接反序列化成proto的message对象
- 测试用例在example/import_test.go
命令行
excelexporter -config=.\exporter.yaml
config: 配置文件
#Excel导入目录(excel所在目录)
DataImportPath: "./data/excel"
#导出格式: json、pb
ExportFormats:
- "json"
- "pb"
#数据导出目录,和ExportFormats一一对应
DataExportPath:
- "./data/json"
- "./data/pb"
#可选项:导出md5文件完整路径,和ExportFormats一一对应
Md5ExportPath:
- "./data/json/md5.json"
- "./data/pb/md5.json"
#proto所在目录
ProtoPath: "./proto"
#需要解析的proto文件
ProtoFiles:
- "export.proto"
- "cfg.proto"
#代码模板目录
CodeTemplatePath: "./template/"
#代码模板
CodeTemplateFiles:
- "data_mgr.go.template"
#代码导出目录 NOTE:和CodeTemplateFiles的数量要一致
CodeExportFiles:
- "./cfg/data_mgr.go"
#导出分组标记 c s cs
ExportGroup: "s"
#默认的分组标记
DefaultGroup: "cs"
#导出总表的文件名
ExportAllExcelFile: "all.xlsx"
#导出总表的sheet名
ExportAllSheet: "ExportCfg"
导出总表(all.xlsx)
all.xlsx是一个导出总表(也叫索引表/配置注册表),它本身不包含具体的游戏配置数据,而是作为一个"元数据表", 记录了所有需要导出的Excel配置表的信息。程序通过解析这个总表,自动遍历并导出所有引用的配置Excel文件。
总表由配置文件中的ExportAllExcelFile和ExportAllSheet指定文件名和Sheet名,默认为all.xlsx中的ExportCfg Sheet。
总表的列定义
每一行代表一个配置表的导出定义,支持以下列:
| 列名 | 必填 | 说明 |
|---|---|---|
| Excel | 是 | 对应的Excel文件名(如itemcfg.xlsx) |
| Sheet | 是 | Excel中的Sheet名 |
| Message | 否 | 对应的protobuf Message名,不填则默认使用Sheet名 |
| Group | 否 | 分组标记(c/s/cs),用于按服务端/客户端筛选导出 |
| MgrType | 否 | 管理器类型: map(默认)/slice/object,详见下方说明 |
| MapKey | 否 | MgrType=map时的key字段名,不填则使用第一个非注释列 |
| CodeComment | 否 | 代码注释 |
| Merge | 否 | 合并名称,用于将多个Sheet的数据合并到同一个导出文件 |
总表Excel示例
---------------------------------------------------------------------------------------------
| Excel | Sheet | Message | Group | MgrType | MapKey | CodeComment |
---------------------------------------------------------------------------------------------
| itemcfg.xlsx | ItemCfg | ItemCfg | cs | map | CfgId | 物品配置 |
---------------------------------------------------------------------------------------------
| levelcfg.xlsx | LevelExp | LevelExp | cs | slice | | 等级经验 |
---------------------------------------------------------------------------------------------
| global.xlsx | GlobalCfg | GlobalCfg | cs | object | | 全局配置 |
---------------------------------------------------------------------------------------------
| questcfg.xlsx | QuestCfg | QuestCfg | cs | map | CfgId | 任务配置 |
---------------------------------------------------------------------------------------------
工作流程
- 程序读取all.xlsx总表,解析每行注册信息
- 根据Group列和配置文件中的ExportGroup进行分组过滤
- 打开每行Excel列指定的Excel文件,按Sheet列读取数据
- 根据MgrType将数据转换为对应格式(map/slice/object)
- 导出为JSON/PB格式文件
- 根据代码模板生成数据管理器代码
- 执行引用检查(Ref Check)
管理器类型(MgrType)
配置表支持3种管理器类型,通过总表的MgrType列指定:
map(默认)
- 以某一列的值作为key,构建
map[key]RowData的数据结构 - 适用于有唯一标识(如CfgId)的配置表,如物品配置、任务配置等
- 需要通过MapKey列指定key字段名(不填则默认使用第一个非注释列)
- 导出的JSON格式为
{"1": {...}, "2": {...}}
slice
- 所有行数据按顺序组成一个数组
[]RowData - 不需要key列,每一行直接作为数组的一个元素
- 适用于没有自然主键的列表配置,如等级经验表
- 导出的JSON格式为
[{...}, {...}]
object
- 采用Key-Value模式,Excel表中必须有
key列和value列 - key列填写proto字段名,value列填写对应的值
- 最终导出为一个单独的JSON对象,而不是数组或map
- 适用于全局参数配置(如服务器参数、系统常量等),只有一"组"数据,用Key-Value方式编辑更直观
三种类型对比:
| 特性 | map | slice | object |
|---|---|---|---|
| JSON输出 | {"1": {...}} | [{...}] | {field: value} |
| 需要key列 | 是 | 否 | 是(key=value模式) |
| 典型场景 | 有唯一ID的配置表 | 无自然主键的列表配置 | 全局参数配置 |
简单示例1:
- 由于proto文件中已经定义了数据结构,所以excel里只需要列名和proto定义的字段名一致,就可以知道字段的类型信息, 所以excel里不需要再指定字段类型,假设物品的proto定义如下
message Item {
int32 Id = 1;
int32 Type = 2;
string Name = 3;
}
那么物品配置表的excel格式就可以这样:
-------------------------
| Id | Type | Name |
-------------------------
| 1 | 1 | 物品1 |
-------------------------
| 2 | 1 | 物品2 |
-------------------------
| 3 | 2 | 物品3 |
-------------------------
示例2:字段也是message结构
message Test {
int32 Id = 1;
Item Item1 = 2;
Item Item2 = 3;
repeated Item Items = 4;
}
配置表的excel格式就可以这样:
-------------------------------------------------------------
| Id | Item1 | Item2 | Items |
| | #Field=no | #Field=full | #Field=Id_Type |
-------------------------------------------------------------
| 1 | 1_1_物品1 | Id_1#Type_1#Name_物品1 | 1_1;2_1 |
-------------------------------------------------------------
| 2 | 2_1_物品2 | Id_2#Type_1#Name_物品2 | 2_1;3_2 |
-------------------------------------------------------------
| 3 | 3_2_物品3 | Id_3#Type_2#Name_物品3 | 3_2;1_1 |
-------------------------------------------------------------
一个单元格要配置一个message的数据,就需要在一个单元格里填写多个字段的数据
- #Field=no 表示Item1不需要填写字段名,以_作为分隔符,按照字段顺序进行赋值,适用于字段少的结构简单的message, 缺点是兼容性差,当message的字段做了更新,可能导致解析异常
- #Field=full 表示Item2需要填写字段名,以Field1_v1#Field2_v2的格式,按照字段名进行赋值,填写麻烦但是兼容性强, 当message增删了字段或者调整了字段的顺序,也不影响字段的解析
- #Field=Field1_Field2_FieldN 是前2种格式的结合,既简洁又保留了兼容性,解析时会按照表头指定的字段名进行赋值, 且单元格不需要再每行填写字段名
示例3: 单元格使用json格式
-----------------------------------------------------------------------
| Id | Item1 | Item2 | Items |
| | #Field=no | #Format=json | #Field=Id_Type |
-----------------------------------------------------------------------
| 1 | 1_1_物品1 | {"Id":1,"Type":1,"Name":"物品1"} | 1_1;2_1 |
-----------------------------------------------------------------------
| 2 | 2_1_物品2 | {"Id":2,"Type":1,"Name":"物品2"} | 2_1;3_2 |
-----------------------------------------------------------------------
| 3 | 3_2_物品3 | {"Id":3,"Type":2,"Name":"物品3"} | 3_2;1_1 |
-----------------------------------------------------------------------
示例4: 单元格关联检查
格式: #Ref=要关联检查的配置表名,假如有一个配置表Item,下面的Item2配置了#Ref=Item,在导表时,将会自动关联检查Item2中配置的Id是否在Item中存在,如果不存在,将会输出错误信息
-------------------------------------------------------------
| Id | Item1 | Item2 | Items |
| | #Field=no | #Field=full#Ref=Item | #Field=Id_Type |
-------------------------------------------------------------
| 1 | 1_1_物品1 | Id_1#Type_1#Name_物品1 | 1_1;2_1 |
-------------------------------------------------------------
| 2 | 2_1_物品2 | Id_2#Type_1#Name_物品2 | 2_1;3_2 |
-------------------------------------------------------------
| 3 | 3_2_物品3 | Id_3#Type_2#Name_物品3 | 3_2;1_1 |
-------------------------------------------------------------
示例5: 多列合并为数组 (#Merge)
对于proto中定义的repeated字段,支持在Excel中使用多列来配置,每列只编辑单个元素,导表时自动合并为数组。 格式: 列名#Merge,多列使用相同的列名即可自动合并。
例如proto定义如下:
message AddElemArg {
int32 CfgId = 1; // 配置id
int32 Num = 2; // 数量
}
message QuestCfg {
int32 CfgId = 1;
repeated AddElemArg Rewards = 2; // 任务奖励
}
Excel配置格式:
---------------------------------------------------------------
| CfgId | Rewards | Rewards | Rewards |
| | #Merge#Field=no | #Merge#Field=no | #Merge#Field=no |
---------------------------------------------------------------
| 1 | 1_100 | 2_50 | 3_200 |
---------------------------------------------------------------
| 2 | 10_500 | | 20_100 |
---------------------------------------------------------------
导出后的JSON:
{
"CfgId": 1,
"Rewards": [
{"CfgId": 1, "Num": 100},
{"CfgId": 2, "Num": 50},
{"CfgId": 3, "Num": 200}
]
}
特点:
- 每个Rewards#Merge列只编辑单个AddElemArg元素
- 空单元格会被自动忽略,不参与合并
- 支持与#Field、#Format等其他标记组合使用
- 适用于repeated字段元素较多、结构较复杂的场景
示例6: 展开字段(点号语法)
当proto字段本身是message时,支持将子字段拆分为多列配置,列名格式为父字段.子字段,导出时自动合并为父message。
例如proto定义如下:
message BaseInfo {
int32 CfgId = 1;
int32 Type = 2;
}
message ComboClass {
int32 Id = 1;
string Name = 2;
BaseInfo Base = 3;
}
Excel配置格式:
-------------------------------------------
| Id | Name | Base.CfgId | Base.Type |
-------------------------------------------
| 1 | ComboA | 1001 | 1 |
-------------------------------------------
| 2 | ComboB | 1002 | 2 |
-------------------------------------------
示例7: 分组导出(##group)
可在表中增加##group行控制列导出分组,并结合配置文件中的ExportGroup和DefaultGroup生效。
Excel配置格式:
------------------------------------------------
| ##var | Id | Name | ServerOnly | ClientOnly |
------------------------------------------------
| ##group | cs | cs | s | c |
------------------------------------------------
| | 1 | A | 100 | icon_a |
------------------------------------------------
说明:
- 当
ExportGroup=s时,会导出Id/Name/ServerOnly - 当
ExportGroup=c时,会导出Id/Name/ClientOnly - 当列未显式配置分组时,使用
DefaultGroup
示例8: 显式列定义行(##var)
当首行不是字段名(例如有说明或注释)时,可以用##var明确指定“这一行是列定义行”。
Excel配置格式:
-----------------------------------------
| # 这是说明行,不会参与数据解析 |
-----------------------------------------
| ##var | Id | Type | Name |
-----------------------------------------
| | 1 | 1 | 物品1 |
-----------------------------------------
示例9: 分隔符规则与json便捷写法
常见分隔规则:
- repeated/map第一层元素: 使用
;或换行分隔 - 嵌套repeated/map元素: 使用
,分隔(避免和外层冲突)
示例:
Elems: 1_10;2_20;3_30
Nested: 1,2,3
#Format=json示例:
---------------------------------------
| Item | Item |
| #Format=json | #Format=json |
---------------------------------------
| {"Id":1,"Type":2} | "Id":2,"Type":3 |
---------------------------------------
说明:
- 建议优先使用标准JSON(带
{}),可读性更好 - 在支持的场景下可省略最外层
{}
示例10: 自定义分隔符(#Sep)
默认情况下,message字段值之间使用_作为分隔符(如1_100),但当字段值本身包含_时(如物品名称),会导致解析错误。可以通过#Sep指定自定义分隔符来解决。
格式: #Sep=分隔符,例如#Sep=|表示使用|作为分隔符。
proto定义:
message ItemNum {
int32 CfgId = 1;
int32 Num = 2;
}
message QuestCfg {
int32 CfgId = 1;
repeated ItemNum Rewards = 2;
}
#Merge + 自定义分隔符
---------------------------------------------------------
| CfgId | Rewards | Rewards |
| | #Merge#Sep=| | #Merge#Sep=| |
---------------------------------------------------------
| 1 | 1|100 | 2|200 |
---------------------------------------------------------
| 2 | 3|50 | 4|300 |
---------------------------------------------------------
导出后的JSON:
[
{"CfgId": 1, "Rewards": [{"CfgId": 1, "Num": 100}, {"CfgId": 2, "Num": 200}]},
{"CfgId": 2, "Rewards": [{"CfgId": 3, "Num": 50}, {"CfgId": 4, "Num": 300}]}
]
说明:
#Sep仅影响当前列的第一层字段分隔符,嵌套层级仍使用默认的_分隔符- 适用于字段值中包含
_的场景,避免解析歧义 - 可与
#Field、#Merge等标记自由组合使用
示例11: slice格式配置表
当配置表没有自然的唯一主键时,可以使用slice格式,所有行数据按顺序组成一个数组。
proto定义:
message LevelExp {
int32 Level = 1; // 等级
int32 NeedExp = 2; // 升到该等级需要的经验值
}
在all.xlsx总表中注册(MgrType填写slice):
---------------------------------------------------------
| Excel | Sheet | Message | MgrType | MapKey |
---------------------------------------------------------
| levelcfg.xlsx | LevelExp | LevelExp| slice | |
---------------------------------------------------------
levelcfg.xlsx中的LevelExp Sheet配置:
----------------------------
| Level | NeedExp |
----------------------------
| 1 | 0 |
----------------------------
| 2 | 100 |
----------------------------
| 3 | 300 |
----------------------------
| 4 | 600 |
----------------------------
| 5 | 1000 |
----------------------------
导出后的JSON(LevelExp.json):
[
{"Level": 1, "NeedExp": 0},
{"Level": 2, "NeedExp": 100},
{"Level": 3, "NeedExp": 300},
{"Level": 4, "NeedExp": 600},
{"Level": 5, "NeedExp": 1000}
]
说明:
- slice格式不需要指定MapKey,每行直接作为数组的一个元素
- 所有特性(#Field、#Format、#Merge、#Sep、展开字段等)均适用于slice格式
- slice格式同样支持分组导出(##group)
示例12: object格式配置表(Key-Value模式)
object格式采用Key-Value模式,适用于全局配置(如服务器参数、系统常量等)。
Excel表中必须有key列和value列,key列填写proto字段名,value列填写对应的值。
proto定义:
message GlobalCfg {
string ServerName = 1; // 服务器名称
int32 MaxLevel = 2; // 最大等级
int32 MaxPlayerNum = 3; // 最大玩家数
float DropRate = 4; // 掉落倍率
bool EnablePvP = 5; // 是否开启PvP
}
在all.xlsx总表中注册(MgrType填写object):
---------------------------------------------------------
| Excel | Sheet | Message | MgrType | MapKey |
---------------------------------------------------------
| global.xlsx | GlobalCfg | GlobalCfg| object | |
---------------------------------------------------------
global.xlsx中的GlobalCfg Sheet配置:
-------------------------------
| key | value |
-------------------------------
| ServerName | 测试服务器 |
-------------------------------
| MaxLevel | 100 |
-------------------------------
| MaxPlayerNum | 5000 |
-------------------------------
| DropRate | 1.5 |
-------------------------------
| EnablePvP | true |
-------------------------------
导出后的JSON(GlobalCfg.json):
{
"ServerName": "测试服务器",
"MaxLevel": 100,
"MaxPlayerNum": 5000,
"DropRate": 1.5,
"EnablePvP": true
}
说明:
- key列填写proto中定义的字段名,value列填写该字段的值
- 程序会根据proto定义自动识别value列的数据类型(int/string/float/bool等)
- value列支持复杂类型(message、repeated、map等),可配合#Field、#Format等标记使用
- object格式也支持展开字段(点号语法),如key列填写
Base.CfgId可将子字段合并到父message中 - 导出的JSON是一个单独的对象(不是数组或map)
object格式的分组导出(group列)
object格式支持一个可选的group列,用于声明每个字段(key对应的字段)的导出分组(c/s/cs),结合配置文件中的ExportGroup和DefaultGroup进行过滤。
带group列的Excel配置示例(global.xlsx中的GlobalCfg Sheet):
------------------------------------------------
| key | value | group |
------------------------------------------------
| ServerName | 测试服务器 | cs |
------------------------------------------------
| MaxLevel | 100 | s |
------------------------------------------------
| MaxPlayerNum | 5000 | cs |
------------------------------------------------
| DropRate | 1.5 | c |
------------------------------------------------
| EnablePvP | true | s |
------------------------------------------------
说明:
- group列填写该字段(key对应的字段)的导出分组,可选值为
c、s、cs - 当配置文件
ExportGroup=s时,只有group为s或cs的字段会被导出,group为c的字段不会被导出(上例中DropRate不会被导出) - 当配置文件
ExportGroup=c时,只有group为c或cs的字段会被导出,group为s的字段不会被导出(上例中MaxLevel、EnablePvP不会被导出) - 当某行的group列为空时,使用配置文件中的
DefaultGroup作为该字段的分组 - 当表中不配置group列时,所有字段正常导出,不做任何过滤(向后兼容,已有的object配置表无需修改)