基于Newtonsoft,将含多结构嵌套JSON字符串的数组反序列化为类的最佳方法
解决方案:处理嵌套字符串JSON的反序列化问题
首先得明确为什么直接把memo定义为类类型数组会失败:你的JSON里memo的每个元素是被引号包裹的字符串形式的JSON,而非直接的JSON对象。Newtonsoft.Json没法自动把字符串直接转成类实例——类型不匹配,所以直接反序列化肯定会报错。
至于是否必须用两步式处理?答案是不需要手动分步处理,我们可以通过更优雅的方式让反序列化一步完成,当然分步处理也是可行的,下面给你几种不同场景下的最佳实践:
1. 自定义JsonConverter:一步完成自动解析
这是最推荐的方案,通过自定义转换器,让Newtonsoft.Json在反序列化主对象时,自动把memo里的每个字符串解析为对应的类实例。
步骤1:定义实体类
先根据JSON结构定义好对应的强类型类:
public class RootObject { public string x { get; set; } public string y { get; set; } // 给memo属性添加自定义转换器标记 [JsonConverter(typeof(MemoItemConverter))] public Braums[] memo { get; set; } public string z { get; set; } } public class Braums { // 包含两种可能的子结构,适配不同的memo元素 public PrintingData printing_data { get; set; } public CancellationReasons cancellation_reasons { get; set; } } public class PrintingData { public string customerFirstName { get; set; } public int orderNumber { get; set; } public string customerLastName { get; set; } } public class CancellationReasons { public string who { get; set; } public string when { get; set; } public string why { get; set; } }
步骤2:实现自定义JsonConverter
这个转换器负责把字符串形式的JSON解析为Braums实例:
public class MemoItemConverter : JsonConverter { public override bool CanConvert(Type objectType) { // 指定该转换器处理Braums类型 return objectType == typeof(Braums); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 读取JSON中的字符串值 var memoString = (string)reader.Value; // 把字符串反序列化为Braums对象(允许单引号适配你的示例格式) return JsonConvert.DeserializeObject<Braums>(memoString, new JsonSerializerSettings { AllowSingleQuotes = true }); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 如果需要把对象序列化回字符串形式的JSON,实现反向逻辑 var braums = (Braums)value; var jsonString = JsonConvert.SerializeObject(braums); writer.WriteValue(jsonString); } }
步骤3:使用转换器反序列化
现在只需要一次反序列化操作就能得到完整的强类型对象:
var json = @"{""x"": ""testx"", ""y"": ""testy"", ""memo"": [ ""{'braums': {'printing_data': {'customerFirstName': 'xxxxxx', 'orderNumber': 4495, 'customerLastName': 'xxxxxx'}}}"", ""{'braums': {'cancellation_reasons': {'who': 'xxxxxxx', 'when': '11/18/2020 8:02 PM', 'why': 'called to cancel'}}}"", ""z"": ""testz"" }"; var root = JsonConvert.DeserializeObject<RootObject>(json, new JsonSerializerSettings { AllowSingleQuotes = true });
2. 反序列化后处理:简单直观的分步方案
如果不想写自定义转换器,也可以先把memo反序列化为字符串数组,之后再手动解析每个元素:
定义带后处理属性的类
public class RootObject { public string x { get; set; } public string y { get; set; } // 先存储原始字符串数组 public string[] memo { get; set; } // 用[JsonIgnore]标记,避免序列化/反序列化时被处理 [JsonIgnore] public Braums[] ParsedMemo { get; set; } }
执行后处理
var root = JsonConvert.DeserializeObject<RootObject>(json, new JsonSerializerSettings { AllowSingleQuotes = true }); // 遍历原始字符串数组,逐个解析为Braums对象 root.ParsedMemo = root.memo.Select(s => JsonConvert.DeserializeObject<Braums>(s, new JsonSerializerSettings { AllowSingleQuotes = true })).ToArray();
3. 封装解析函数:灵活处理不同结构
如果memo里的Braums结构差异很大,你可以封装一个通用函数,根据字符串内容判断解析为对应的子类:
定义子类和解析函数
// 定义不同结构的Braums子类 public class PrintingBraums { public PrintingData printing_data { get; set; } } public class CancellationBraums { public CancellationReasons cancellation_reasons { get; set; } } // 封装解析函数 public static object ParseMemoItem(string memoString) { var settings = new JsonSerializerSettings { AllowSingleQuotes = true }; // 先解析为JObject,判断结构类型 var jObj = JsonConvert.DeserializeObject<JObject>(memoString, settings); var braumsObj = jObj["braums"] as JObject; if (braumsObj?.ContainsKey("printing_data") == true) { return braumsObj.ToObject<PrintingBraums>(JsonSerializer.Create(settings)); } else if (braumsObj?.ContainsKey("cancellation_reasons") == true) { return braumsObj.ToObject<CancellationBraums>(JsonSerializer.Create(settings)); } return null; }
使用函数解析
var root = JsonConvert.DeserializeObject<RootObject>(json, new JsonSerializerSettings { AllowSingleQuotes = true }); var parsedItems = root.memo.Select(ParseMemoItem).ToArray();
总结
- 如果需要强类型且结构相对固定,自定义JsonConverter是最佳方案,代码更整洁,无需手动分步处理。
- 如果结构不固定或者想快速实现,后处理或封装函数也是可行的选择。
- 你最初的两步式处理是可行的,但可以通过上述方式优化得更优雅。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

