You need to enable JavaScript to run this app. ASP.NET Core 5:如何用System.Text.Json序列化Newtonsoft JToken
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 5:如何用System.Text.Json序列化Newtonsoft JToken

问题根源

System.Text.Json 对Json.NET的JObject类型没有内置兼容逻辑,会将其当作普通POCO遍历公开属性(如索引器、元素计数属性)序列化,完全忽略其树形JSON结构,最终输出无意义的嵌套空数组。

实现方案

完全匹配约束要求:

  • 不全局切换回Json.NET序列化
  • 不执行JObject转字符串、再解析为STJ JsonDocument的重复开销操作
  • 全流式逐节点写入,无中间大对象内存分配,适配大体积JObject的性能要求
    只需为System.Text.Json实现针对JToken类型的自定义转换器即可:

1. 编写自定义JToken转换器

直接递归遍历JToken的树形结构,逐节点写入STJ的Utf8JsonWriter,全程无中间序列化/解析步骤:

using System.Globalization;
using System.Numerics;
using System.Text.Json;
using System.Text.Json.Serialization;
using Newtonsoft.Json.Linq;

public class JTokenConverter : JsonConverter<JToken>
{
    public override bool CanConvert(Type typeToConvert)
    {
        return typeof(JToken).IsAssignableFrom(typeToConvert);
    }

    public override JToken? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // 当前场景仅需处理输出序列化,无需实现反序列化逻辑
        throw new NotImplementedException();
    }

    public override void Write(Utf8JsonWriter writer, JToken value, JsonSerializerOptions options)
    {
        switch (value.Type)
        {
            case JTokenType.Null:
                writer.WriteNullValue();
                break;
            case JTokenType.Boolean:
                writer.WriteBooleanValue((bool)((JValue)value).Value!);
                break;
            case JTokenType.Integer:
                var intVal = ((JValue)value).Value!;
                switch (intVal)
                {
                    case int i:
                        writer.WriteNumberValue(i);
                        break;
                    case long l:
                        writer.WriteNumberValue(l);
                        break;
                    case BigInteger bi:
                        writer.WriteNumberValue(bi);
                        break;
                }
                break;
            case JTokenType.Float:
                var floatVal = ((JValue)value).Value!;
                switch (floatVal)
                {
                    case float f:
                        writer.WriteNumberValue(f);
                        break;
                    case double d:
                        writer.WriteNumberValue(d);
                        break;
                    case decimal dec:
                        writer.WriteNumberValue(dec);
                        break;
                }
                break;
            case JTokenType.String:
            case JTokenType.Date:
            case JTokenType.Guid:
            case JTokenType.Uri:
            case JTokenType.TimeSpan:
                writer.WriteStringValue(Convert.ToString(((JValue)value).Value, CultureInfo.InvariantCulture));
                break;
            case JTokenType.Array:
                writer.WriteStartArray();
                foreach (var item in (JArray)value)
                {
                    Write(writer, item, options);
                }
                writer.WriteEndArray();
                break;
            case JTokenType.Object:
                writer.WriteStartObject();
                foreach (var prop in (JObject)value.Properties())
                {
                    writer.WritePropertyName(prop.Name);
                    Write(writer, prop.Value, options);
                }
                writer.WriteEndObject();
                break;
            case JTokenType.Property:
                var prop = (JProperty)value;
                writer.WritePropertyName(prop.Name);
                Write(writer, prop.Value, options);
                break;
            default:
                writer.WriteStringValue(value.ToString());
                break;
        }
    }
}

2. 注册转换器到ASP.NET Core的STJ配置

仅在默认的System.Text.Json序列化选项中注入该转换器,不修改全局序列化逻辑,也不会切换为Json.NET处理所有接口输出:

// 在Startup.ConfigureServices中配置MVC序列化选项
services.AddControllers()
    .AddJsonOptions(jsonOptions =>
    {
        jsonOptions.JsonSerializerOptions.Converters.Add(new JTokenConverter());
    });
效果验证

配置完成后运行测试用例:

  • 输出JSON与原始JObject内容完全一致,满足JToken.DeepEquals校验要求
  • 整个序列化过程无中间字符串分配、无重复JSON解析开销,大体积JObject的序列化性能与原生STJ处理基本持平
  • 对非JToken类型的序列化逻辑完全无侵入,不影响原有项目的STJ配置

内容的提问来源于stack exchange,提问作者dbc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.11 21:15:05