.NET 6中如何结合源生成STJ解析器、手写转换器与多JsonSerializerContext?
.NET 6下System.Text.Json源生成上下文与手写转换器的整合方案
一、自定义组合型JsonTypeInfoResolver(替代.NET 8的JsonTypeInfoResolver.Combine)
.NET 6没有内置的多解析器组合能力,我们可以自行实现一个按优先级尝试的组合解析器:
public class CompositeJsonTypeInfoResolver : IJsonTypeInfoResolver { private readonly IJsonTypeInfoResolver[] _resolvers; public CompositeJsonTypeInfoResolver(params IJsonTypeInfoResolver[] resolvers) { _resolvers = resolvers ?? throw new ArgumentNullException(nameof(resolvers)); } public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options) { foreach (var resolver in _resolvers) { var typeInfo = resolver.GetTypeInfo(type, options); if (typeInfo != null) { return typeInfo; } } return null; } }
二、整合源生成上下文与手写转换器
1. 源生成上下文处理
假设类库中有多个负责不同类型的源生成上下文:
[JsonSourceGenerationOptions(WriteIndented = false)] [JsonSerializable(typeof(PollingRequest))] [JsonSerializable(typeof(PollingResponse))] public partial class PollingApiClientContext : JsonSerializerContext { } [JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)] [JsonSerializable(typeof(WebhookEvent))] public partial class WebhookContext : JsonSerializerContext { }
2. 手写转换器处理
先实现自定义转换器逻辑:
public class CustomPayloadConverter : JsonConverter<CustomPayload> { public override CustomPayload? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var doc = JsonDocument.ParseValue(ref reader); var root = doc.RootElement; return new CustomPayload { Id = root.GetProperty("payload_id").GetString(), Data = root.GetProperty("payload_data").GetString() }; } public override void Write(Utf8JsonWriter writer, CustomPayload value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteString("payload_id", value.Id); writer.WriteString("payload_data", value.Data); writer.WriteEndObject(); } }
再创建一个仅针对指定类型返回转换器的解析器:
public class CustomConverterResolver : IJsonTypeInfoResolver { private readonly Dictionary<Type, JsonConverter> _converters = new() { { typeof(CustomPayload), new CustomPayloadConverter() } }; public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options) { if (_converters.TryGetValue(type, out var converter)) { var typeInfo = JsonSerializerOptions.Default.GetTypeInfo(type); typeInfo.Converters.Add(converter); return typeInfo; } return null; } }
三、封装类库配置扩展
提供扩展方法,让使用者可以将类库配置合并到自身的JsonSerializerOptions中,且仅作用于类库类型:
public static class JsonSerializerOptionsExtensions { public static JsonSerializerOptions AddLibraryConfiguration(this JsonSerializerOptions options) { var compositeResolver = new CompositeJsonTypeInfoResolver( new CustomConverterResolver(), PollingApiClientContext.Default, WebhookContext.Default ); // 枚举类库所有需要自定义处理的类型 var libraryTypes = new[] { typeof(PollingRequest), typeof(PollingResponse), typeof(WebhookEvent), typeof(CustomPayload) }; foreach (var type in libraryTypes) { options.TypeInfoResolverChain.Insert(0, new TargetedTypeResolver(type, compositeResolver)); } return options; } // 仅针对特定类型生效的解析器包装类 private class TargetedTypeResolver : IJsonTypeInfoResolver { private readonly Type _targetType; private readonly IJsonTypeInfoResolver _innerResolver; public TargetedTypeResolver(Type targetType, IJsonTypeInfoResolver innerResolver) { _targetType = targetType; _innerResolver = innerResolver; } public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options) { if (type == _targetType || (type.IsGenericType && type.GetGenericTypeDefinition() == _targetType.GetGenericTypeDefinition())) { return _innerResolver.GetTypeInfo(type, options); } return null; } } }
四、使用者集成方式
1. 独立轮询客户端场景
使用者可在自身配置基础上添加类库专属配置:
var userOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, // 使用者自定义全局配置 }; userOptions.AddLibraryConfiguration(); // 序列化/反序列化类库类型时使用该配置 var response = JsonSerializer.Deserialize<PollingResponse>(json, userOptions);
2. ASP.NET Core Webhook场景
在Program.cs中整合到控制器的Json配置:
builder.Services.AddControllers() .AddJsonOptions(options => { // 使用者的全局序列化约定 options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; // 添加类库专属配置 options.JsonSerializerOptions.AddLibraryConfiguration(); });
这样类库的源生成转换器、手写转换器只会作用于自身提供的类型,不会干扰使用者其他类型的序列化规则。
内容的提问来源于stack exchange,提问作者Aleksey Usatov
相关产品推荐
相关产品推荐

