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

Ank*_*nha 4 .net c# serialization json json.net

我有一个用JsonConverter属性装饰的类来使用我的自定义转换器.自定义转换器的目的是CustomProperty使用一些自定义逻辑进行序列化.我没有编写代码来序列化所有原始属性,而是决定使用JObject.FromObject自动序列化属性,稍后会做类似的事情o.Remove("CustomProperty")然后添加自定义序列化成员o.
但是因为类是用JsonConverter属性装饰的,所以JObject.FromObject再次调用my ClassAJsonConverter会导致infinte递归调用.在调用时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)
   {
      .
      .
      .
   }
}
Run Code Online (Sandbox Code Playgroud)

注意:我遇到了JsonConverter中递归调用JsonSerializer但无法实现它.此外,我不想仅为这一用途添加依赖于AutoMapper.既然问题已经超过一年了,有没有人找到更好的方法呢?

Bri*_*ers 10

如果您使用[JsonConverter]属性修饰了类,则序列化程序的所有实例都将了解它.因此,如果您JObject.FromObject在转换器中使用,您将进入无限递归循环,即使您尝试将其传递给新的序列化程序实例.

有两种解决问题的方法:

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

例如:

string json = JsonConvert.SerializeObject(classA, new ClassAJsonConverter());
Run Code Online (Sandbox Code Playgroud)

如果您的序列化CustomProperty不依赖于其他成员ClassA,那么另一种选择是专门为CustomProp该类创建自定义转换器而不是ClassA.然后你的转换器不必担心其他属性; 它只需要担心CustomProp自己.


另一种可能的解决

我找到了一个可能对你有用的解决方案,但感觉有点hacky.我们的想法是在其中创建一个新JsonSerializer实例JsonConverter,然后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;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此解析器,您需要修改您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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 毕竟我找到了一种方法,但感觉相当hacky.看看你的想法; 我编辑了我的答案.我不确定是否推荐它! (3认同)