如何用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
相关产品推荐
相关产品推荐

