You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 00:36:17