Excel定义数据类型后C#构建Unreal数据结构的Map映射问题
问题背景
我持有用于定义数据类型并存储对应类型实际数据的Excel文件,文件内分别设置独立工作表存储类、枚举定义与业务数据。我计划通过C#读取表格数据后导出为JSON格式供Unreal引擎使用,原本拟构建树状数据结构承载解析后的数据,但实现过程中遇到问题。
Excel结构示例
类型定义表结构参考如下:
Struct Type Name Person String Name int Age TMap<string, Person> Children
列映射规则
Excel内有名为define的工作表,作用是将数据列映射至对应类成员,映射规则示例如下:
A B 1 Name Person::Name::Value 2 Age Person::Age 3 ChildName Person::Children$Key::Value 4 Child Person::Children$Value::Value
目前我已经可以基于Excel中的类型定义完成类结构的构建,但在将实际数据映射到对应结构时,Map类的设计出现障碍:Map结构需要同时支持简单类型与复杂类型,我最初设计采用string -> Node的字典存储子节点,但由于Map的键可能为用户自定义类型、对象等非字符串类型,因此我设计了MapEntryNode结构承载键值对;但基于前述define工作表做路径解析时,我无法找到合理逻辑确定应当从Map的子节点集合中取出哪个目标节点,因为映射路径中仅标注了数据类型名称,未指定对应成员信息。
初步编写的基础类承载代码如下:
public class AddChildNode { public string Name; public string Value; public string Type; public DataNode Key; public DataNode ValueNode; } public class DataNode { public string Name = string.Empty; public string Type = string.Empty; public string Value { get; set; } = string.Empty; public virtual bool IsNode(string key) { return key == Name; } public virtual void AddChild(AddChildNode childData) { } public virtual bool GetChild(string key, out DataNode outChild) { outChild = null; return false; } } public class TypeNode : DataNode { public Dictionary<string, DataNode> Children { get; set; } = new Dictionary<string, DataNode>(); public override void AddChild(AddChildNode childObjectData) { Children.Add(childObjectData.Name, new DataNode { Name = childObjectData.Name, Value = childObjectData.Value }); } public override bool GetChild(string key, out DataNode outChild) { return Children.TryGetValue(key, out outChild); } } public class ArrayNode : DataNode { public Dictionary<string, DataNode> Children { get; set; } = new Dictionary<string, DataNode>(); public override void AddChild(AddChildNode childObjectData) { Children.Add(childObjectData.Name, new DataNode { Name = childObjectData.Name, Value = childObjectData.Value }); } public override bool GetChild(string key, out DataNode outChild) { return Children.TryGetValue(key, out outChild); } } public class MapNode : DataNode { public Dictionary<string, MapEntryNode> Children { get; set; } = new Dictionary<string, MapEntryNode>(); public string KeyType { get; set; } public string ValueType { get; set; } public MapEntryNode Params { get; set; } public MapNode(string name, string keyType, string ValueType) { this.Name = name; this.KeyType = keyType; this.ValueType = ValueType; Type = "Map"; Params = new MapEntryNode(); } public override void AddChild(AddChildNode childObjectData) { Params.AddChild(childObjectData); } public override bool GetChild(string key, out DataNode outChild) { return Params.GetChild(key, out outChild); } } public class MapEntryNode : DataNode { public Dictionary<string, DataNode> Children { get; set; } = new Dictionary<string, DataNode>(); public DataNode Key { get; set; } public DataNode ValueNode { get; set; } public override bool GetChild(string key, out DataNode outChild) { outChild = null; if (key == ValueNode.Name) { outChild = ValueNode; return true; } return false; } public override void AddChild(AddChildNode childObjectData) { Children.Add(childObjectData.Key.Name, childObjectData.ValueNode); } }
实现思路与优化建议
- 先拆分类型模板和数据实例两层逻辑,现有代码把两者混在了一起:类型构建阶段只生成结构模板,不需要存储实际值;数据填充阶段基于模板生成实例节点,再存储具体业务值,能避免很多逻辑混乱。
- 路径解析不要硬编码按名称查子节点,遇到Map类型节点时,路径里的
$Key、$Value是固定语义标记,不是普通成员名:- 解析到
Children$Key路径段时,直接返回当前MapEntry模板的Key节点模板,不需要去子节点字典做名称匹配 - 解析到
Children$Value路径段时,直接返回当前MapEntry模板的Value节点模板
- 解析到
- 修正Map实例的存储逻辑,不要用字符串做Key的字典存MapEntry,现有
Dictionary<string, MapEntryNode>完全无法支持非字符串键的需求:数据填充阶段,每读取一行映射到Map的记录,就新建一个独立的MapEntry实例,先填充Key实例的值,再填充Value实例的值,最后把这个Entry加入Map实例的Entry集合,Key直接用解析出来的强类型对象,不要强制转字符串。 - 精简现有类结构,去掉重复代码:
TypeNode和ArrayNode的子节点操作逻辑完全一致,可以抽一个通用的ContainerNode基类继承DataNode,把通用的Children字典、AddChild、GetChild逻辑放到基类里,类/结构体、数组、Map三类容器都继承这个基类即可。 - 针对Unreal导出场景做适配:UE的JSON序列化对Map类型有固定格式要求,导出时简单类型键直接序列化为标准键值对,自定义结构体键要匹配UE的解析格式,不要自定义结构,避免导入UE时出现解析失败。
额外注意:路径解析时要维护层级上下文缓存,比如解析
Person::Children$Key::Value路径时,进入Children节点后要标记当前正在操作的MapEntry实例,不要每次都去Children集合做全局查找,避免同一行数据的Key和Value被加到不同Entry里,造成数据错位。
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

