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

多JSON文件反序列化及多关联关系映射优化方案咨询

多JSON文件反序列化与关联映射优化方案

场景与问题

目标对象模型

public class Foo
{
    public string Id { get; set; }
    public string Name { get; set; }
}

public class Bar
{
    public string Id { get; set; }
    public Foo Foo { get; set; }
    public DateTime At { get; set; }
}

public class Baz
{
    public string Id { get; set; }
    public DateTime At { get; set; }
    public List<Bar> Bars { get; set; } = new List<Bar>();
}

public class Qux
{
    public string Id { get; set; }
    public string Name { get; set; }
    public List<Baz> Baz { get; set; } = new List<Baz>();
}

待处理的JSON文件

Foo.json

[
  {
    "Id": "1",
    "Name": "AAA"
  },
  {
    "Id": "2",
    "Name": "BBB"
  }
]

Bar.json

[
  {
    "Id": "1",
    "FooId": "1",
    "BazId": "1",
    "At": "2022-01-01T10:00:00"
  },
  {
    "Id": "2",
    "FooId": "2",
    "BazId": "1",
    "At": "2022-01-02T10:00:00"
  }
]

Baz.json

[
  {
    "Id": "1",
    "Quxd": "1",
    "At": "2022-01-01T10:00:00"
  },
  {
    "Id": "2",
    "QuxId": "1",
    "At": "2022-01-02T10:00:00"
  }
]

注:Baz.json中第一个对象的关联字段应为QuxId,推测是笔误,需兼容处理

Qux.json

[
  {
    "Id": "1",
    "Name": "CCC"
  },
  {
    "Id": "2",
    "Name": "DDD"
  }
]

当前痛点

JSON设计存在缺陷但无法修改,现有方案是为每个JSON创建对应DTO类,反序列化后通过嵌套循环映射关联关系,效率低下,需要更优实现方式。


优化解决方案

方案1:字典索引+批量映射(推荐)

核心思路是用字典以Id为键构建全局索引,将嵌套循环的O(n²)时间复杂度降至接近O(n),大幅提升映射效率。

步骤1:定义匹配JSON的DTO类

// Foo和Qux的DTO与目标类结构一致,可直接复用原类
public class BarDto
{
    public string Id { get; set; }
    public string FooId { get; set; }
    public string BazId { get; set; }
    public DateTime At { get; set; }
}

public class BazDto
{
    public string Id { get; set; }
    public string QuxId { get; set; }
    public DateTime At { get; set; }
    // 兼容JSON中的笔误字段
    public string Quxd { get; set; }
}

步骤2:反序列化并构建索引字典

// 异步读取并反序列化所有JSON文件
var foos = JsonSerializer.Deserialize<List<Foo>>(await File.ReadAllTextAsync("Foo.json"));
var barDtos = JsonSerializer.Deserialize<List<BarDto>>(await File.ReadAllTextAsync("Bar.json"));
var bazDtos = JsonSerializer.Deserialize<List<BazDto>>(await File.ReadAllTextAsync("Baz.json"));
var quxes = JsonSerializer.Deserialize<List<Qux>>(await File.ReadAllTextAsync("Qux.json"));

// 构建O(1)查找的索引字典
var fooIndex = foos.ToDictionary(f => f.Id);
var quxIndex = quxes.ToDictionary(q => q.Id);
var bazIndex = new Dictionary<string, Baz>();

步骤3:批量完成关联映射

// 先初始化Baz并关联到对应的Qux
foreach (var bazDto in bazDtos)
{
    var baz = new Baz
    {
        Id = bazDto.Id,
        At = bazDto.At,
        Bars = new List<Bar>()
    };
    bazIndex[bazDto.Id] = baz;

    // 兼容JSON中的笔误字段
    var quxId = bazDto.QuxId ?? bazDto.Quxd;
    if (quxIndex.TryGetValue(quxId, out var targetQux))
    {
        targetQux.Baz.Add(baz);
    }
}

// 再初始化Bar并关联到对应的Foo和Baz
foreach (var barDto in barDtos)
{
    var bar = new Bar
    {
        Id = barDto.Id,
        At = barDto.At,
        Foo = fooIndex[barDto.FooId]
    };
    bazIndex[barDto.BazId].Bars.Add(bar);
}

方案2:自定义JsonConverter,反序列化时直接关联

如果不想额外维护DTO类,可以自定义JSON转换器,在反序列化过程中直接利用已加载的索引完成关联,减少后续映射步骤。

示例:Bar类的自定义转换器

public class BarConverter : JsonConverter<Bar>
{
    private readonly Dictionary<string, Foo> _fooLookup;
    private readonly Dictionary<string, Baz> _bazLookup;

    public BarConverter(Dictionary<string, Foo> fooLookup, Dictionary<string, Baz> bazLookup)
    {
        _fooLookup = fooLookup;
        _bazLookup = bazLookup;
    }

    public override Bar Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var jsonDoc = JsonDocument.ParseValue(ref reader);
        var root = jsonDoc.RootElement;

        var bar = new Bar
        {
            Id = root.GetProperty("Id").GetString(),
            At = root.GetProperty("At").GetDateTime(),
            Foo = _fooLookup[root.GetProperty("FooId").GetString()]
        };

        // 直接关联到对应的Baz
        var bazId = root.GetProperty("BazId").GetString();
        _bazLookup[bazId].Bars.Add(bar);

        return bar;
    }

    public override void Write(Utf8JsonWriter writer, Bar value, JsonSerializerOptions options)
    {
        throw new NotImplementedException("无需实现序列化逻辑");
    }
}

使用方式

// 先加载依赖的基础数据并构建索引
var foos = JsonSerializer.Deserialize<List<Foo>>(await File.ReadAllTextAsync("Foo.json"));
var fooIndex = foos.ToDictionary(f => f.Id);

var quxes = JsonSerializer.Deserialize<List<Qux>>(await File.ReadAllTextAsync("Qux.json"));
var quxIndex = quxes.ToDictionary(q => q.Id);

var bazDtos = JsonSerializer.Deserialize<List<BazDto>>(await File.ReadAllTextAsync("Baz.json"));
var bazIndex = new Dictionary<string, Baz>();
foreach (var bazDto in bazDtos)
{
    var baz = new Baz { Id = bazDto.Id, At = bazDto.At, Bars = new List<Bar>() };
    bazIndex[bazDto.Id] = baz;

    var quxId = bazDto.QuxId ?? bazDto.Quxd;
    if (quxIndex.TryGetValue(quxId, out var targetQux))
    {
        targetQux.Baz.Add(baz);
    }
}

// 配置转换器并反序列化Bar
var serializerOptions = new JsonSerializerOptions();
serializerOptions.Converters.Add(new BarConverter(fooIndex, bazIndex));
var bars = JsonSerializer.Deserialize<List<Bar>>(await File.ReadAllTextAsync("Bar.json"), serializerOptions);

内容的提问来源于stack exchange,提问作者Ash

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 15:51:32