You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

迁移至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
  • 值分两种情况:
    • 多条目时是包含多个JObjectJArray
    • 单条目时是一个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

火山引擎 最新活动