自定义JsonConverter实现继承类列表的序列化与反序列化问题
问题描述
类结构
// 父类 public class Circle { public decimal Area { get; set; } = 0.2m; } // 子类 public class GreenCircle : Circle { public string Color { get; set; } = "Green"; } public class RedCircle : Circle { public string Color { get; set; } = "Red"; } public class BlueCircle : Circle { public string Color { get; set; } = "Blue"; } // 待转换类 public class Shapes { public string ShapeType { get; set; } = "Circle"; public List<Circle> ShapeList { get; set; } = new List<Circle> { new RedCircle(), new BlueCircle(), new GreenCircle(), }; }
目标JSON格式
{ "shapeType": "circle", "shapeList": [ {"name": "RedCircle", "color": "red", "area": 0.2}, {"name": "BlueCircle", "color": "blue", "area": 0.2}, {"name": "GreenCircle", "color": "green", "area": 0.2} ] }
疑问
- 如何反序列化包含普通字段和数组字段的JSON?
- 是否可以直接将列表反序列化为子类对象?(即列表中包含
RedCircle、BlueCircle和GreenCircle对象,而非3个Circle对象)
解决方案
要实现需求,确实需要自定义JsonConverter处理多态序列化/反序列化,以下是完整实现方案:
1. 自定义Circle类型的JsonConverter
该转换器负责单个Circle子类的序列化与反序列化,通过JSON中的name字段识别具体子类:
public class CircleConverter : JsonConverter<Circle> { // 序列化:将Circle子类转为指定格式的JSON对象 public override void WriteJson(JsonWriter writer, Circle value, JsonSerializer serializer) { JObject obj = new JObject(); // 写入类型名称 obj.Add("name", value.GetType().Name); // 写入Area字段 obj.Add("area", value.Area); // 写入Color字段(仅子类处理) if (value is GreenCircle green) obj.Add("color", green.Color.ToLower()); else if (value is RedCircle red) obj.Add("color", red.Color.ToLower()); else if (value is BlueCircle blue) obj.Add("color", blue.Color.ToLower()); obj.WriteTo(writer); } // 反序列化:根据name字段创建对应子类实例并填充属性 public override Circle ReadJson(JsonReader reader, Type objectType, Circle existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject obj = JObject.Load(reader); string name = obj["name"].ToString(); // 根据name创建对应子类实例 Circle circle = name switch { "RedCircle" => new RedCircle(), "BlueCircle" => new BlueCircle(), "GreenCircle" => new GreenCircle(), _ => new Circle() }; // 填充Area字段 circle.Area = obj["area"].ToObject<decimal>(); // 填充Color字段(仅子类处理) if (circle is RedCircle red) red.Color = obj["color"].ToString().ToUpper(); else if (circle is BlueCircle blue) blue.Color = obj["color"].ToString().ToUpper(); else if (circle is GreenCircle green) green.Color = obj["color"].ToString().ToUpper(); return circle; } }
2. 配置序列化/反序列化选项
通过JsonSerializerOptions添加自定义转换器,并处理属性名的大小写转换:
// 序列化示例 Shapes shapes = new Shapes(); var serializeOptions = new JsonSerializerOptions { Converters = { new CircleConverter() }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 自动转换为小驼峰命名 WriteIndented = true }; string json = JsonSerializer.Serialize(shapes, serializeOptions); // 反序列化示例 string jsonStr = @"{""shapeType"":""circle"",""shapeList"":[{""name"":""RedCircle"",""color"":""red"",""area"":0.2},{""name"":""BlueCircle"",""color"":""blue"",""area"":0.2},{""name"":""GreenCircle"",""color"":""green"",""area"":0.2}]}"; var deserializeOptions = new JsonSerializerOptions { Converters = { new CircleConverter() }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; Shapes deserializedShapes = JsonSerializer.Deserialize<Shapes>(jsonStr, deserializeOptions); // 验证反序列化结果:输出各元素的类型名称 foreach (var item in deserializedShapes.ShapeList) { Console.WriteLine(item.GetType().Name); // 依次输出RedCircle、BlueCircle、GreenCircle }
疑问解答
反序列化包含普通字段和数组字段的JSON:
只需配置正确的序列化选项(如小驼峰命名匹配),并为数组元素类型添加自定义转换器,JsonSerializer会自动处理普通字段(如ShapeType)和数组字段(如ShapeList)的反序列化,无需额外编写普通字段的处理逻辑。将列表反序列化为子类对象:
通过自定义JsonConverter<Circle>,在ReadJson方法中根据name字段创建对应的子类实例并填充属性,最终ShapeList中的元素会是RedCircle、BlueCircle、GreenCircle的实例,而非基类Circle。
内容的提问来源于stack exchange,提问作者Razero
相关产品推荐
相关产品推荐

