C#中如何配置DTO类以兼容两种结构集合的JSON序列化与反序列化
适配两种JSON结构的DTO实现方案
核心需求
基于Newtonsoft.Json构建单个Message DTO,同时满足三个要求:
- 反序列化兼容接口返回的两种
Attachments结构(直接数组 / 嵌套"Attachment"节点的对象) - 序列化输出完全符合入参要求的嵌套对象结构
- 无需额外维护两套请求/响应模型
结构差异说明
入参要求的JSON结构
{ "MessageText": "text", "Attachments": { "Attachment": [ { "FileName": "file1" }, { "FileName": "file2" } ] } }
接口返回的兼容JSON结构
- 直接数组结构:
{ "MessageText": "text", "Attachments": [ { "FileName": "file1" }, { "FileName": "file2" } ] }
- 嵌套节点结构(和入参结构一致,同样需要兼容解析):
{ "MessageText": "text", "Attachments": { "Attachment": [ { "FileName": "file1" }, { "FileName": "file2" } ] } }
现有实现的问题
当前实现的Attachments类直接继承List<Attachment>,会导致:
- 序列化时直接输出数组结构,不符合入参的嵌套对象要求
- 反序列化时遇到嵌套对象结构(带
Attachment节点),无法正确解析为列表
最终实现代码
1. 自定义JSON转换器
通过重写JsonConverter的读写逻辑,同时处理序列化输出和反序列化的结构兼容:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; public class AttachmentsJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(Attachments).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var attachments = new Attachments(); // 兼容两种返回结构 switch (reader.TokenType) { case JsonToken.StartArray: // 直接数组结构:直接反序列化 var arrayItems = serializer.Deserialize<List<Attachment>>(reader); attachments.AddRange(arrayItems); break; case JsonToken.StartObject: // 嵌套节点结构:提取"Attachment"字段后反序列化 var jObject = JObject.Load(reader); var nestedItems = jObject["Attachment"]?.ToObject<List<Attachment>>(); if (nestedItems != null) { attachments.AddRange(nestedItems); } break; } return attachments; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var attachments = (Attachments)value; // 统一输出入参要求的嵌套结构 writer.WriteStartObject(); writer.WritePropertyName("Attachment"); serializer.Serialize(writer, attachments.ToList()); writer.WriteEndObject(); } }
2. 修正后的DTO定义
给Attachments类绑定自定义转换器特性,无需修改其他业务属性:
public class Message { public string MessageText { get; set; } public Attachments Attachments { get; set; } = new Attachments(); } public class Attachment { public string FileName { get; set; } } [JsonConverter(typeof(AttachmentsJsonConverter))] public class Attachments : List<Attachment> { }
测试匹配说明
上述实现可以完全满足给定的3个单元测试用例:
TestMethodDeSerialization1:反序列化直接数组结构时,转换器识别StartArray令牌,直接解析为列表TestMethodDeSerialization2:反序列化嵌套对象结构时,转换器识别StartObject令牌,自动提取Attachment节点内容解析TestMethodSerialization:序列化时统一包装为带Attachment属性的对象,输出完全匹配入参Schema要求
内容的提问来源于stack exchange,提问作者Sybaris
相关产品推荐
相关产品推荐

