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

如何用JsonSerializer.DeserializeAsyncEnumerable反序列化大JSON到属性名不匹配的ProtoBuf类

解决方案

方法一:修正自定义NamingPolicy的映射方向

你的CustomNamingPolicy未生效的核心原因是映射方向搞反了:
System.Text.Json在反序列化时,会将JSON中的字段名传入ConvertName方法,转换后去匹配.NET属性名。你之前的代码是把属性名映射到JSON字段名(适用于序列化),但反序列化需要把JSON字段名映射到属性名。

修改后的NamingPolicy代码:

public class CustomNamingPolicy : JsonNamingPolicy
{
    private readonly Dictionary<string, string> NameMapping = new Dictionary<string, string>()
    {
        // JSON字段名 → .NET属性名
        ["TrackingData"] = nameof(OASISLevel2TrackingPacket.EntityTracking.TrackingDatas)
    };

    public override string ConvertName(string name)
    {
        return NameMapping.GetValueOrDefault(name, name);
    }
}

使用方式不变,反序列化时就能将JSON中的TrackingData字段匹配到TrackingDatas属性。

⚠️ 注意:如果Protobuf生成的类为TrackingDatas属性添加了[JsonPropertyName("TrackingData")]特性,那么PropertyNamingPolicy会被忽略(特性优先级更高),此时需要用方法二。

方法二:自定义JsonConverter手动处理字段映射

当属性带有[JsonPropertyName]特性时,需要编写自定义转换器绕过特性限制,手动映射JSON字段到.NET属性:

public class EntityTrackingConverter : JsonConverter<OASISLevel2TrackingPacket.EntityTracking>
{
    public override OASISLevel2TrackingPacket.EntityTracking? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var entityTracking = new OASISLevel2TrackingPacket.EntityTracking();
        
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject)
                return entityTracking;

            if (reader.TokenType != JsonTokenType.PropertyName)
                continue;

            var jsonPropertyName = reader.GetString()!;
            reader.Read();

            switch (jsonPropertyName)
            {
                case "TrackingData":
                    // 反序列化JSON中的TrackingData到TrackingDatas属性
                    var trackingData = JsonSerializer.Deserialize<TrackingData>(ref reader, options);
                    if (entityTracking.TrackingDatas == null)
                        entityTracking.TrackingDatas = new List<TrackingData>();
                    entityTracking.TrackingDatas.Add(trackingData);
                    break;
                default:
                    // 其他属性使用默认反序列化逻辑
                    var property = typeToConvert.GetProperty(jsonPropertyName, BindingFlags.Public | BindingFlags.Instance);
                    if (property != null)
                    {
                        var value = JsonSerializer.Deserialize(ref reader, property.PropertyType, options);
                        property.SetValue(entityTracking, value);
                    }
                    else
                    {
                        // 跳过未知字段
                        reader.Skip();
                    }
                    break;
            }
        }

        return entityTracking;
    }

    public override void Write(Utf8JsonWriter writer, OASISLevel2TrackingPacket.EntityTracking value, JsonSerializerOptions options)
    {
        // 若不需要序列化,可抛出异常或留空
        throw new NotImplementedException();
    }
}

然后在反序列化选项中注册转换器:

var options = new JsonSerializerOptions()
{
    Converters = { new EntityTrackingConverter() }
};

// 后续异步反序列化代码不变
using (FileStream? fileStream = new FileStream("hugefile.json", FileMode.Open))
{
    IAsyncEnumerable<Person?> people = JsonSerializer.DeserializeAsyncEnumerable<Person?>(fileStream, options);
    await foreach (Person? person in people)
    {
        Console.WriteLine($"Hello, my name is {person.Name}!");
    }
}

方法三:反射修改特性(不推荐)

可以通过反射修改TrackingDatas属性的[JsonPropertyName]特性值,但这种方式依赖.NET内部实现,版本兼容性差,仅作为备选:

// 获取TrackingDatas属性的JsonPropertyName特性
var property = typeof(OASISLevel2TrackingPacket.EntityTracking).GetProperty(nameof(OASISLevel2TrackingPacket.EntityTracking.TrackingDatas))!;
var jsonPropAttr = property.GetCustomAttributes(typeof(JsonPropertyNameAttribute), false).FirstOrDefault() as JsonPropertyNameAttribute;

if (jsonPropAttr != null)
{
    // 修改特性的Name字段(需通过反射访问私有字段)
    var nameField = typeof(JsonPropertyNameAttribute).GetField("_name", BindingFlags.NonPublic | BindingFlags.Instance)!;
    nameField.SetValue(jsonPropAttr, "TrackingData");
}

内容的提问来源于stack exchange,提问作者F.A

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:54:58