C#中JSON反序列化时高效展开嵌套属性的实现方法
如何高效反序列化嵌套JSON数组并展开属性到C#对象列表?
作为.NET新手,你需要将带嵌套properties字段的JSON数组,转换为没有嵌套字段的Fruit类实例列表,且不想用手动遍历赋值的方式,以下是两种更高效、易维护的方案,均基于.NET内置的System.Text.Json库实现:
方案一:自定义JSON转换器(推荐复杂场景)
通过实现JsonConverter<Fruit>,让序列化库自动处理嵌套属性的映射,无需手动遍历数组元素。
using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; public class Fruit { public string Name { get; set; } public string Color { get; set; } public string Flavor { get; set; } } // 自定义转换器,处理嵌套属性展开 public class FruitConverter : JsonConverter<Fruit> { public override Fruit Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException("JSON格式错误,预期对象起始标记"); } var fruit = new Fruit(); while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { return fruit; } if (reader.TokenType == JsonTokenType.PropertyName) { string propName = reader.GetString(); reader.Read(); switch (propName.ToLower()) { case "name": fruit.Name = reader.GetString(); break; case "properties": // 解析嵌套的properties对象 while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { if (reader.TokenType == JsonTokenType.PropertyName) { string nestedProp = reader.GetString(); reader.Read(); switch (nestedProp.ToLower()) { case "color": fruit.Color = reader.GetString(); break; case "flavor": fruit.Flavor = reader.GetString(); break; } } } break; } } } throw new JsonException("JSON格式不完整"); } // 若不需要序列化Fruit对象,可保留默认实现 public override void Write(Utf8JsonWriter writer, Fruit value, JsonSerializerOptions options) { throw new NotImplementedException(); } } class Program { static void Main() { string json = "[{\"name\":\"apple\",\"properties\":{\"color\":\"red\",\"flavor\":\"sweet\"}},{\"name\":\"orange\",\"properties\":{\"color\":\"orange\",\"flavor\":\"sour\"}}]"; var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, Converters = { new FruitConverter() } // 注册自定义转换器 }; // 直接反序列化为目标类型列表 List<Fruit> fruits = JsonSerializer.Deserialize<List<Fruit>>(json, options); foreach (var fruit in fruits) { Console.WriteLine($"Name: {fruit.Name}"); Console.WriteLine($"Color: {fruit.Color}"); Console.WriteLine($"Flavor: {fruit.Flavor}\n"); } } }
方案二:使用中间DTO类(推荐简单场景)
先定义与JSON结构完全匹配的中间数据传输对象(DTO),反序列化后通过LINQ投影转换为Fruit列表,代码简洁易读。
using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; public class Fruit { public string Name { get; set; } public string Color { get; set; } public string Flavor { get; set; } } // 中间DTO,完全匹配JSON的嵌套结构 public class FruitDto { public string Name { get; set; } public PropertiesDto Properties { get; set; } } public class PropertiesDto { public string Color { get; set; } public string Flavor { get; set; } } class Program { static void Main() { string json = "[{\"name\":\"apple\",\"properties\":{\"color\":\"red\",\"flavor\":\"sweet\"}},{\"name\":\"orange\",\"properties\":{\"color\":\"orange\",\"flavor\":\"sour\"}}]"; var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; // 先反序列化为DTO列表 List<FruitDto> fruitDtos = JsonSerializer.Deserialize<List<FruitDto>>(json, options); // 通过LINQ投影转换为目标类型 List<Fruit> fruits = fruitDtos.Select(dto => new Fruit { Name = dto.Name, Color = dto.Properties.Color, Flavor = dto.Properties.Flavor }).ToList(); foreach (var fruit in fruits) { Console.WriteLine($"Name: {fruit.Name}"); Console.WriteLine($"Color: {fruit.Color}"); Console.WriteLine($"Flavor: {fruit.Flavor}\n"); } } }
方案对比
- 自定义转换器:适用于复杂嵌套结构或需要自定义解析逻辑的场景,一次配置可重复使用,性能最优。
- 中间DTO类:代码简洁、学习成本低,适用于简单映射场景,性能略逊于转换器但远优于手动遍历。
这两种方案均利用System.Text.Json的内置优化,避免了手动操作JsonElement的繁琐,代码更易维护。
内容的提问来源于stack exchange,提问作者Juan Gamez
相关产品推荐
相关产品推荐

