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

自定义JsonConverter重复代码消除方案咨询

解决方案:消除JsonConverter重复代码的几种可行方案

针对你遇到的Utf8JsonReader作为ref struct无法在委托中传递的问题,这里有几个实用的方案来消除50个自定义转换器的重复代码:

1. 抽象类+模板方法模式(最直接的替代方案)

放弃用委托传递差异化逻辑,转而在抽象基类中定义抽象方法,让子类实现这些方法来处理局部初始化、属性解析、最终对象构造的差异化部分。因为抽象方法可以直接接收ref Utf8JsonReader参数,完全避开ref struct的委托限制。

示例代码:

抽象基类

public abstract class JsonConverterEx<T> : JsonConverter<T>
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
        {
            throw new JsonException("Expected StartObject token");
        }

        // 调用子类实现的初始化逻辑
        var instance = InitializeInstance(ref reader, options);

        reader.Read(); // 跳过StartObject
        while (reader.TokenType != JsonTokenType.EndObject)
        {
            if (reader.TokenType != JsonTokenType.PropertyName)
            {
                throw new JsonException("Expected PropertyName token");
            }

            var propertyName = reader.GetString();
            reader.Read();

            // 调用子类实现的属性处理逻辑
            ProcessProperty(ref reader, propertyName, instance, options);

            reader.Read();
        }

        // 调用子类实现的最终构造逻辑
        return FinalizeInstance(ref reader, instance, options);
    }

    // 子类需实现的抽象方法:初始化实例(或返回空实例)
    protected abstract T InitializeInstance(ref Utf8JsonReader reader, JsonSerializerOptions options);

    // 子类需实现的抽象方法:处理单个属性
    protected abstract void ProcessProperty(ref Utf8JsonReader reader, string propertyName, T instance, JsonSerializerOptions options);

    // 子类需实现的抽象方法:最终调整/返回实例
    protected abstract T FinalizeInstance(ref Utf8JsonReader reader, T instance, JsonSerializerOptions options);

    // 公共的Write逻辑也可以在这里提取,同理
    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        WriteProperties(writer, value, options);
        writer.WriteEndObject();
    }

    protected abstract void WriteProperties(Utf8JsonWriter writer, T value, JsonSerializerOptions options);
}

子类实现示例

public class UserConverter : JsonConverterEx<User>
{
    protected override User InitializeInstance(ref Utf8JsonReader reader, JsonSerializerOptions options)
    {
        // 差异化初始化:比如从某个特殊属性提前初始化,或直接new
        return new User();
    }

    protected override void ProcessProperty(ref Utf8JsonReader reader, string propertyName, User instance, JsonSerializerOptions options)
    {
        // 差异化属性处理
        switch (propertyName)
        {
            case "id":
                instance.Id = reader.GetInt32();
                break;
            case "name":
                instance.Name = reader.GetString();
                break;
            // 其他属性处理...
        }
    }

    protected override User FinalizeInstance(ref Utf8JsonReader reader, User instance, JsonSerializerOptions options)
    {
        // 差异化最终构造:比如校验必填属性,或计算派生字段
        if (string.IsNullOrEmpty(instance.Name))
        {
            throw new JsonException("Name is required");
        }
        return instance;
    }

    protected override void WriteProperties(Utf8JsonWriter writer, User value, JsonSerializerOptions options)
    {
        writer.WriteNumber("id", value.Id);
        writer.WriteString("name", value.Name);
        // 其他属性写入...
    }
}

2. 提取通用逻辑为静态方法/扩展方法

把所有转换器都用到的公共逻辑(比如Token校验、通用类型读取)提取成静态方法或扩展方法,在子类中直接调用,减少重复代码。

示例代码:

public static class JsonReaderHelpers
{
    public static void EnsureTokenType(ref Utf8JsonReader reader, JsonTokenType expectedType)
    {
        if (reader.TokenType != expectedType)
        {
            throw new JsonException($"Expected token {expectedType}, got {reader.TokenType}");
        }
    }

    public static void SkipToEndObject(ref Utf8JsonReader reader)
    {
        while (reader.TokenType != JsonTokenType.EndObject)
        {
            reader.Skip();
        }
    }

    // 其他通用方法:比如读取枚举、处理多态类型的公共判断逻辑等
}

然后在子类中直接调用:

public class OrderConverter : JsonConverter<Order>
{
    public override Order Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        JsonReaderHelpers.EnsureTokenType(ref reader, JsonTokenType.StartObject);
        var order = new Order();

        reader.Read();
        while (reader.TokenType != JsonTokenType.EndObject)
        {
            JsonReaderHelpers.EnsureTokenType(ref reader, JsonTokenType.PropertyName);
            var propName = reader.GetString();
            reader.Read();

            // 差异化属性处理
            switch(propName)
            {
                case "orderNumber":
                    order.OrderNumber = reader.GetString();
                    break;
                // ...
            }

            reader.Read();
        }

        return order;
    }

    // Write方法同理...
}

3. 代码生成器(适合大量转换器场景)

因为你有50个转换器,手动写抽象类子类还是有点繁琐,可以用T4模板或Roslyn源码生成器来自动生成大部分重复代码,只需要提供每个类型的差异化配置(比如属性映射、特殊处理逻辑)。

思路:

  1. 定义一个配置类或特性,标记每个需要生成转换器的类型,以及属性的映射规则、特殊处理方法名。
  2. 编写T4模板或Roslyn生成器,读取这些配置,自动生成继承自JsonConverter<T>的完整转换器代码,包含公共的Read/Write框架,只把差异化部分留空或调用你手动实现的方法。

比如用T4模板的伪代码:

<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Reflection" #>

<# foreach(var type in GetTypesWithConverterAttribute()) { #>
public class <#= type.Name #>Converter : JsonConverter<<#= type.FullName #>>
{
    public override <#= type.FullName #> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        JsonReaderHelpers.EnsureTokenType(ref reader, JsonTokenType.StartObject);
        var instance = new <#= type.Name #>();

        reader.Read();
        while (reader.TokenType != JsonTokenType.EndObject)
        {
            JsonReaderHelpers.EnsureTokenType(ref reader, JsonTokenType.PropertyName);
            var propName = reader.GetString();
            reader.Read();

            switch(propName)
            {
                <# foreach(var prop in type.GetProperties()) { #>
                case "<#= prop.Name #>":
                    instance.<#= prop.Name #> = reader.Get<#= prop.PropertyType.Name #>();
                    break;
                <# } #>
                // 这里可以留空让你手动添加特殊属性处理
            }

            reader.Read();
        }

        return instance;
    }

    // Write方法同理生成...
}
<# } #>

这种方案能一次性生成大部分代码,你只需要补充特殊属性的处理逻辑即可,彻底消除重复样板。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 22:35:15