迁移至System.Text.Json:如何直接转换Newtonsoft JObject/JArray?
问题
正在将解决方案从Newtonsoft迁移至System.Text.Json,但依赖的一个NuGet包使用Newtonsoft反序列化REST API返回的复杂对象,无法修改该包也不想自行编写API客户端。
NuGet包中定义的对象结构如下:
// ComplexResource定义在NuGet包中,使用Newtonsoft注解 public class ComplexResource { [JsonProperty("uuid")] public Guid Uuid; [JsonProperty("child_resources")] public Dictionary<string, object> ChildResources; }
其中ChildResources字典的规则:
- 键为类型名称(例如
PlanResource) - 值分两种情况:
- 多条目时是包含多个
JObject的JArray - 单条目时是一个
JObject
- 多条目时是包含多个
需要将字典中的值反序列化为项目中已用JsonPropertyName注解的对应类型(如PlanResource),目前只能通过先序列化再反序列化的方式实现,效率低下,希望找到更直接的转换方式,无需为每个资源类型编写自定义映射器。
当前低效实现示例:
// Step 0: 从字典中获取值 var complexResource = ... // 调用NuGet包返回ComplexResource var resourceData = complexResource.ChildResources["PlanResource"]; // Step 1: 用Newtonsoft重新序列化 var jsonString = JsonConvert.SerializeObject(resourceData); // Step 2: 用System.Text.Json反序列化 var actualResource = JsonSerializer.Deserialize<PlanResource>(jsonString); // Step 3: 正常操作PlanResource actualResource.LastUpdated = ...
解决方案
可以通过扩展方法将Newtonsoft的JToken直接转换为System.Text.Json的JsonElement,跳过中间的字符串序列化步骤,实现高效转换:
1. 编写JToken转JsonElement的扩展方法
using System.Text.Json; using Newtonsoft.Json.Linq; public static class JTokenExtensions { public static JsonElement ToJsonElement(this JToken token) { using var stream = new MemoryStream(); using var writer = new Utf8JsonWriter(stream); token.WriteTo(writer); writer.Flush(); stream.Position = 0; using var doc = JsonDocument.Parse(stream); return doc.RootElement.Clone(); } }
2. 高效转换并处理单/多条目
var complexResource = ... // 调用NuGet包获取ComplexResource var resourceData = complexResource.ChildResources["PlanResource"]; if (resourceData is JToken token) { var jsonElement = token.ToJsonElement(); // 根据JsonElement的类型判断是单条目还是多条目 if (jsonElement.ValueKind == JsonValueKind.Array) { // 反序列化为列表 var planResources = JsonSerializer.Deserialize<List<PlanResource>>(jsonElement); // 处理列表逻辑 } else { // 反序列化为单个对象 var planResource = JsonSerializer.Deserialize<PlanResource>(jsonElement); // 处理单个对象逻辑 planResource.LastUpdated = DateTime.UtcNow; } } // 可选:处理非JToken类型的基础值 else { var jsonElement = JsonSerializer.SerializeToElement(resourceData); // 后续处理逻辑 }
方案优势
- 避免了中间JSON字符串的生成与解析,直接在流层面完成转换,性能更优
- 无需为每个资源类型编写自定义映射逻辑,复用System.Text.Json已有的序列化配置
- 自动适配单条目(JObject)和多条目(JArray)的场景
内容的提问来源于stack exchange,提问作者debracey




