You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Newtonsoft JsonConverter迁移至System.Text.Json的JsonConverter<T>

问题描述

我定义了如下接口:

public interface IApiResult<TResult> : IApiResult
{
    TResult Result { get; set; }
}

以及对应的具体实现类:

public class ApiResult<TResult> : ApiResult, IApiResult<TResult>
{
    public ApiResult( TResult result ) : base() {
        Result = result;
    }

    public TResult Result { get; set; }
}

此前使用Newtonsoft.Json库时,我通过如下JsonConverter实现多态序列化与反序列化:

public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) {
    var obj = JObject.Load( reader );
    var apiResult = new ApiResult.ApiResult().As<IApiResult>();
    //Check if is an IApiResult<T>
    if (obj.TryGetValue( nameof( ApiResult<object>.Result ), StringComparison.InvariantCultureIgnoreCase, out var jResult )) {
        //Retrieve the result property type in order to create the proper apiResult object
        var prop = objectType.GetProperty( nameof( ApiResult<object>.Result ) );
        var value = jResult.ToObject( prop.PropertyType, serializer );
        var rType = typeof( ApiResult<> ).MakeGenericType( prop.PropertyType );
        apiResult = Activator.CreateInstance( rType ).As<IApiResult>();
        prop.SetValue( apiResult, value );
    }
    //Set the messages
    var jMessages = obj.GetValue( nameof( ApiResult.ApiResult.Messages ), StringComparison.InvariantCultureIgnoreCase ).ToObject<JObject[]>();
    apiResult.Messages = DeserializeReasons( jMessages );
    return apiResult;
}

现在需要将这段代码迁移至System.Text.Json,最大的问题在于JObject.TryGetValue方法——它能直接返回反序列化后的对象,让我识别类型并通过反射确定ApiResult<T>的类型,但当前的UTF8JsonReader只能逐令牌读取,无法复现此前的行为。


解决方案

方法一:使用JsonNode(.NET 6及以上版本推荐)

JsonNode是System.Text.Json在.NET 6中新增的API,功能和Newtonsoft的JObject类似,可直接加载整个JSON并按需访问属性,完美替代原有逻辑:

public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    var jsonNode = JsonNode.Parse(ref reader);
    if (jsonNode is not JsonObject jsonObj)
    {
        throw new JsonException("Invalid JSON format for IApiResult");
    }

    IApiResult apiResult = new ApiResult();
    var resultPropName = nameof(ApiResult<object>.Result);

    // 检查并处理Result属性(忽略大小写)
    if (jsonObj.TryGetPropertyValue(resultPropName, StringComparison.InvariantCultureIgnoreCase, out var resultNode))
    {
        var resultProp = typeToConvert.GetProperty(resultPropName);
        if (resultProp == null)
        {
            throw new InvalidOperationException($"Type {typeToConvert.Name} does not contain a {resultPropName} property");
        }

        // 反序列化Result值
        var resultValue = resultNode.Deserialize(resultProp.PropertyType, options);
        // 创建对应的ApiResult<T>实例
        var genericType = typeof(ApiResult<>).MakeGenericType(resultProp.PropertyType);
        apiResult = (IApiResult)Activator.CreateInstance(genericType);
        resultProp.SetValue(apiResult, resultValue);
    }

    // 处理Messages属性
    var messagesPropName = nameof(ApiResult.Messages);
    if (jsonObj.TryGetPropertyValue(messagesPropName, StringComparison.InvariantCultureIgnoreCase, out var messagesNode))
    {
        var messagesArray = messagesNode.Deserialize<JsonElement[]>(options);
        apiResult.Messages = DeserializeReasons(messagesArray);
    }

    return apiResult;
}

方法二:使用JsonDocument(兼容.NET 5及更早版本)

如果项目仍在使用.NET 5或更早版本,可通过JsonDocument读取整个JSON内容后解析,逻辑和原有JObject用法对齐:

public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    using var doc = JsonDocument.ParseValue(ref reader);
    var root = doc.RootElement;

    IApiResult apiResult = new ApiResult();
    var resultPropName = nameof(ApiResult<object>.Result);

    // 查找Result属性(忽略大小写)
    if (root.TryGetProperty(resultPropName, StringComparison.InvariantCultureIgnoreCase, out var resultElement))
    {
        var resultProp = typeToConvert.GetProperty(resultPropName);
        if (resultProp == null)
        {
            throw new InvalidOperationException($"Type {typeToConvert.Name} does not contain a {resultPropName} property");
        }

        // 反序列化Result值
        var resultValue = JsonSerializer.Deserialize(resultElement, resultProp.PropertyType, options);
        // 创建ApiResult<T>实例
        var genericType = typeof(ApiResult<>).MakeGenericType(resultProp.PropertyType);
        apiResult = (IApiResult)Activator.CreateInstance(genericType);
        resultProp.SetValue(apiResult, resultValue);
    }

    // 处理Messages属性
    var messagesPropName = nameof(ApiResult.Messages);
    if (root.TryGetProperty(messagesPropName, StringComparison.InvariantCultureIgnoreCase, out var messagesElement))
    {
        var messagesArray = JsonSerializer.Deserialize<JsonElement[]>(messagesElement, options);
        apiResult.Messages = DeserializeReasons(messagesArray);
    }

    return apiResult;
}

注意事项

  • 需调整DeserializeReasons方法的入参类型,适配JsonElement[]或JsonNode[](原方法接收JObject数组)。
  • 若需要实现序列化逻辑(Write方法),可反向使用JsonNode/JsonDocument将ApiResult实例的属性写入JSON。
  • .NET 5及更早版本的TryGetPropertyValue不支持StringComparison参数,需手动遍历属性名进行大小写匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 01:45:32