使用JsonConverterAttribute装饰类时使用默认的JsonSerializer

本文关键字:默认 JsonSerializer JsonConverterAttribute 使用 | 更新日期: 2023-09-27 18:08:06

我有一个用JsonConverter属性装饰的类来使用我的自定义转换器。自定义转换器的目的是使用一些自定义逻辑来序列化CustomProperty。我没有编写代码来序列化所有的基本属性,而是决定使用JObject.FromObject来自动序列化属性,然后再做类似o.Remove("CustomProperty")的事情,然后将自定义序列化的成员添加到o。但是由于类是用JsonConverter属性装饰的,JObject.FromObject再次调用我的ClassAJsonConverter,这导致无限递归调用。在调用JObject.FromObject时,是否可以特别告诉json使用它的默认转换器而不是我的自定义转换器。

[Newtonsoft.Json.JsonConverter(typeof(ClassAJsonConverter))]
public class ClassA
{
   public string A {get; set;}
   public int B {get; set;}
    .
    //20 some properties
    .
   public CustomProp CustomProperty {get; set;}
}
public class ClassAJsonConverter : JsonConverter
{
   public override bool CanConvert(Type objectType)
   {
       return objectType == typeof(ClassA);
   }
   public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
   {
      .
      var o = JObject.FromObject(value);     //Here infinite recurrence occur
      .
   }
   public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
   {
      .
      .
      .
   }
}

注意:我在JsonConverter中遇到递归调用JsonSerializer,但无法实现它。另外,我不希望仅仅为这一次使用而向AutoMapper添加依赖项。既然这个问题已经提出一年多了,有没有人找到更好的方法?

使用JsonConverterAttribute装饰类时使用默认的JsonSerializer

如果您用[JsonConverter]属性装饰了您的类,那么序列化器的所有实例都将知道它。因此,如果你在转换器中使用JObject.FromObject,你将进入一个无限递归循环,即使你试图向它传递一个新的序列化器实例。

有两种方法可以解决这个问题:
  1. 手动处理类中每个字段的序列化,而不是调用JObject.FromObject,或者
  2. 从类声明中删除[JsonConverter]属性,并将转换器的实例传递给序列化器。这种方法依赖于CanConvert的正确实现(如您的问题所示),以便转换器仅应用于预期的类。
例如:

string json = JsonConvert.SerializeObject(classA, new ClassAJsonConverter());

如果CustomProperty的序列化不依赖于ClassA的其他成员,那么另一种选择是为CustomProp类而不是ClassA类创建一个专门的自定义转换器。这样,你的转换器就不用为其他属性操心了;它只需要担心CustomProp本身。


另一个可能的解决方案

我找到了一个可能对你有用的解决方案,但感觉有点粗糙。我们的想法是在JsonConverter中创建一个新的JsonSerializer实例,然后在该序列化器上使用一个特殊的ContractResolver,该序列化器在被要求解析当前转换器时拒绝了解它。这将允许您在转换器内部使用JObject.FromObject而不会进入递归循环,即使您将[JsonConverter]属性应用于您的类。这种方法的缺点是,您可能应用于外部序列化器的任何其他设置都不会自动携带到内部序列化器,因此如果需要保留这些设置,则需要手动复制这些设置。

下面是解析器的代码:
class JsonConverterExclusionResolver<T> : DefaultContractResolver
{
    protected override JsonConverter ResolveContractConverter(Type objectType)
    {
        JsonConverter conv = base.ResolveContractConverter(objectType);
        if (conv != null && conv.GetType() == typeof(T))
        {
            // if something asks for the converter we're excluding,
            // we never heard of it
            return null;
        }
        return conv;
    }
}
有了这个解析器,您需要修改您的ClassAJsonConverter,以便像这样使用它:
public class ClassAJsonConverter : JsonConverter
{
    private IContractResolver exclusionResolver = 
        new JsonConverterExclusionResolver<ClassAJsonConverter>();
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ClassA);
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JsonSerializer innerSerializer = new JsonSerializer();
        innerSerializer.ContractResolver = exclusionResolver;
        // (copy other settings from the outer serializer if needed)
        var o = JObject.FromObject(value, innerSerializer);
        // ...do your custom stuff here...
        o.WriteTo(writer);
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}