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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 17:56:24