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

基于System.Text.Json实现序列化反序列化非对称字段名问题排查

.NET 7+ System.Text.Json 实现不对称序列化(反序列化用第三方字段,序列化用模型字段)

问题背景

我正在构建一个供多应用调用的类库,该类库需访问第三方API,但其返回的JSON字段名极不直观。我需要配置类库的模型对象,使其反序列化第三方响应时用第三方字段名,消费应用序列化时用模型自身的字段名,同时必须避免创建重复模型。

约束条件

  • .NET 7+
  • 使用System.Text.Json
  • 消费应用无需感知第三方字段名,也无需每次序列化都传递自定义JsonSerializerOptions

当前问题

配置的AsymmetricJsonNamingConverterFactory被忽略,无法达到预期的不对称序列化效果。


问题根源分析

  1. 全局配置污染:原转换器的Write方法直接修改传入的JsonSerializerOptions,会影响所有共享该配置的序列化操作,且不符合System.Text.Json的设计规范。
  2. JsonPropertyName属性冲突:模型上的[JsonPropertyName]会同时作用于序列化和反序列化,导致序列化时依然输出第三方字段名,而非模型属性名。
  3. DI配置未覆盖场景:若消费端是ASP.NET Core应用,默认使用Microsoft.AspNetCore.Mvc.JsonOptions而非普通JsonSerializerOptions,原配置无法生效;控制台/桌面应用若直接使用默认序列化方法,也无法获取DI中配置的选项。

解决方案

1. 重构转换器,实现独立的序列化/反序列化逻辑

重新实现AsymmetricJsonNamingConverter<T>,创建独立的配置副本处理序列化和反序列化,同时忽略JsonPropertyName对序列化的影响:

using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace MyLibrary.Model.Serialization;

public class AsymmetricJsonNamingConverter<T> : JsonConverter<T>
{
    private readonly JsonSerializerOptions _deserializeOptions;
    private readonly JsonSerializerOptions _serializeOptions;

    public AsymmetricJsonNamingConverter()
    {
        // 反序列化:保留JsonPropertyName的映射,兼容第三方API字段
        _deserializeOptions = new JsonSerializerOptions();
        
        // 序列化:自定义TypeInfo解析器,强制使用模型属性原名
        _serializeOptions = new JsonSerializerOptions
        {
            WriteIndented = true,
            TypeInfoResolver = new DefaultJsonTypeInfoResolver
            {
                Modifiers = { ModifySerializationTypeInfo }
            }
        };
    }

    private static void ModifySerializationTypeInfo(JsonTypeInfo typeInfo)
    {
        if (typeInfo.Kind != JsonTypeInfoKind.Object) return;
        
        foreach (var property in typeInfo.Properties)
        {
            // 替换为模型属性的原名,忽略JsonPropertyName的配置
            property.Name = property.PropertyInfo!.Name;
        }
    }

    public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return JsonSerializer.Deserialize<T>(ref reader, _deserializeOptions);
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, _serializeOptions);
    }
}

2. 简化转换器工厂实现

using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace MyLibrary.Model.Serialization;

public class AsymmetricJsonNamingConverterFactory : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert)
    {
        return typeToConvert.GetCustomAttribute<UseAsymmetricPropertyNamesAttribute>() != null;
    }

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
    {
        return (JsonConverter)Activator.CreateInstance(
            typeof(AsymmetricJsonNamingConverter<>).MakeGenericType(typeToConvert))!;
    }
}

3. 完善DI配置,覆盖所有场景

针对不同类型的消费应用,配置对应的Json选项:

using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using MyLibrary.Model.Serialization;

namespace MyLibrary.Extensions;

public static class IServiceCollectionMyLibraryExtensions
{
    public static IServiceCollection AddMyLibraryConnector(this IServiceCollection services)
    {
        // 配置ASP.NET Core Web应用的JsonOptions
        services.AddControllers()
            .AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.Converters.Add(new AsymmetricJsonNamingConverterFactory());
                options.JsonSerializerOptions.WriteIndented = true;
            });

        // 配置全局JsonSerializerOptions,供非Web场景使用
        services.Configure<JsonSerializerOptions>(options =>
        {
            options.Converters.Add(new AsymmetricJsonNamingConverterFactory());
            options.WriteIndented = true;
        });

        // 注册封装的序列化工具,让消费端无需手动处理Options
        services.AddScoped<JsonSerializationHelper>();

        // 其他DI配置(如API客户端、验证逻辑等)
        return services;
    }
}

4. 提供封装的序列化工具(可选)

为消费端提供无需关注底层配置的序列化方法:

using System.Text.Json;
using Microsoft.Extensions.Options;

namespace MyLibrary.Helpers;

public class JsonSerializationHelper
{
    private readonly JsonSerializerOptions _options;

    public JsonSerializationHelper(IOptions<JsonSerializerOptions> options)
    {
        _options = options.Value;
    }

    public string Serialize<T>(T value)
    {
        return JsonSerializer.Serialize(value, _options);
    }

    public T? Deserialize<T>(string json)
    {
        return JsonSerializer.Deserialize<T>(json, _options);
    }
}

验证效果

  • 反序列化第三方响应:自动映射FID→Id、CTRY22CD→CountryCode、CTRY22NM→CountryName。
  • 序列化模型:输出字段名为Id、CountryCode、CountryName,完全符合消费端预期。
  • 消费端体验:Web应用直接返回模型即可自动序列化;非Web应用注入JsonSerializationHelper即可完成序列化/反序列化,无需感知第三方字段细节。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 04:02:05