自定义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源码生成器来自动生成大部分重复代码,只需要提供每个类型的差异化配置(比如属性映射、特殊处理逻辑)。
思路:
- 定义一个配置类或特性,标记每个需要生成转换器的类型,以及属性的映射规则、特殊处理方法名。
- 编写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
相关产品推荐
相关产品推荐

