基于System.Text.Json实现序列化反序列化非对称字段名问题排查
.NET 7+ System.Text.Json 实现不对称序列化(反序列化用第三方字段,序列化用模型字段)
问题背景
我正在构建一个供多应用调用的类库,该类库需访问第三方API,但其返回的JSON字段名极不直观。我需要配置类库的模型对象,使其反序列化第三方响应时用第三方字段名,消费应用序列化时用模型自身的字段名,同时必须避免创建重复模型。
约束条件
- .NET 7+
- 使用
System.Text.Json - 消费应用无需感知第三方字段名,也无需每次序列化都传递自定义
JsonSerializerOptions
当前问题
配置的AsymmetricJsonNamingConverterFactory被忽略,无法达到预期的不对称序列化效果。
问题根源分析
- 全局配置污染:原转换器的
Write方法直接修改传入的JsonSerializerOptions,会影响所有共享该配置的序列化操作,且不符合System.Text.Json的设计规范。 - JsonPropertyName属性冲突:模型上的
[JsonPropertyName]会同时作用于序列化和反序列化,导致序列化时依然输出第三方字段名,而非模型属性名。 - 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
相关产品推荐
相关产品推荐

