如何使用Newtonsoft.Json将异构JSON反序列化为指定C#对象列表?
解决Newtonsoft.Json异构JSON反序列化到指定C#结构的问题
问题背景
给定如下异构JSON响应:
{"a374e91a9f513c79a8961de7c494cf799bbdb35b":{"rd":[{"1":{"filename":"The Lion King (2019) BluRay 1080p x264 (nItRo)-XpoZ.mkv","filesize":1819821931}}]},"e999ddbb3e18613476546684e34a4a6b0cfec878":{"rd":[{"1":{"filename":"The.Lion.King.2019.1080p.BluRay.10bit.x265-HazMatt.mkv","filesize":4256678521}}]},"8bb877768a0780c9694767a655720927e6cda57e":{"rd":[]},"054139ba17b8fdd8df1538e1857c45240d5c9368":[]}}
需要将其映射到以下C#对象结构:
public class Item { public string Key { get; set; } public List<File> Files { get; set; } } public class File { public string Id { get; set; } public string FileName { get; set; } public long FileSize { get; set; } }
注意:JSON中类似rd的属性名不固定,可能会变化。
解决方案
由于JSON最外层是动态键的对象,且内层存在不确定属性名,无法直接反序列化为List<Item>,需通过以下步骤处理:
- 先将JSON反序列化为
Dictionary<string, JToken>,获取所有顶层动态键值对; - 遍历每个键值对,将键赋值给
Item.Key; - 处理每个值:
- 若值为数组(如最后一个键对应的
[]),则Files设为空列表; - 若值为对象,提取该对象的第一个值(忽略不确定的属性名),该值为文件数组;
- 遍历文件数组中的每个元素,提取动态键作为
File.Id,并映射filename和filesize到对应属性。
- 若值为数组(如最后一个键对应的
完整代码示例
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Linq; // 定义目标类 public class Item { public string Key { get; set; } public List<File> Files { get; set; } = new List<File>(); // 初始化空列表避免空引用 } public class File { public string Id { get; set; } public string FileName { get; set; } public long FileSize { get; set; } } // 反序列化逻辑 string jsonResponse = "你的JSON字符串"; var jsonDict = JsonConvert.DeserializeObject<Dictionary<string, JToken>>(jsonResponse); var items = new List<Item>(); foreach (var kvp in jsonDict) { var item = new Item { Key = kvp.Key }; if (kvp.Value.Type == JTokenType.Array) { items.Add(item); continue; } // 取对象中的第一个值(忽略不确定的属性名,如rd) var fileArrayToken = kvp.Value.Children().FirstOrDefault()?.Value<JArray>(); if (fileArrayToken == null) { items.Add(item); continue; } foreach (var fileToken in fileArrayToken) { // 提取动态键作为File.Id var fileKvp = fileToken.Children<JProperty>().FirstOrDefault(); if (fileKvp == null) continue; var fileData = fileKvp.Value.ToObject<FileData>(); item.Files.Add(new File { Id = fileKvp.Name, FileName = fileData.Filename, FileSize = fileData.Filesize }); } items.Add(item); } // 辅助类,用于临时映射文件属性 private class FileData { [JsonProperty("filename")] public string Filename { get; set; } [JsonProperty("filesize")] public long Filesize { get; set; } }
说明
- 用
Dictionary<string, JToken>接收顶层动态键,解决异构键的问题; - 通过
JToken的类型判断和遍历子元素,避开不确定的内层属性名; - 辅助类
FileData用于临时映射JSON中的filename和filesize,避免直接操作JToken的繁琐; - 初始化
Item.Files为空列表,避免后续操作出现空引用异常。
内容的提问来源于stack exchange,提问作者Magik
相关产品推荐
相关产品推荐

