使用JsonConverterAttribute时,自定义继承JsonConverter失败

时间:2014-08-20 12:02:34

标签: c# json.net

我正在尝试反序列化派生类型,我想使用自定义属性Type来区分派生类型。

[
  {
    "Type": "a",
    "Height": 100
  },
  {
    "Type": "b",
    "Name": "Joe"
  }
]

我遇到的解决方案是创建自定义JsonConverter。在ReadJson上,我读取Type属性并通过ToObject<T>函数实例化该类型。一切正常,直到我使用JsonConverterAttributeReadJson方法无限循环,因为该属性也应用于子类型。

如何防止将此属性应用于子类型?

[JsonConverter(typeof(TypeSerializer))]
public abstract class Base
{
    private readonly string type;

    public Base(string type)
    {
        this.type = type;
    }

    public string Type { get { return type; } }
}

public class AType : Base
{
    private readonly int height;

    public AType(int height)
        : base("a")
    {
        this.height = height;
    }

    public int Height { get { return height; } }
}

public class BType : Base
{
    private readonly string name;

    public BType(string name)
        : base("b")
    {
        this.name = name;
    }

    public string Name { get { return name; } }
}

public class TypeSerializer : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Base);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var j = JObject.Load(reader);

        var type = j["Type"].ToObject<string>();

        if (type == "a")
            // Infinite Loop! StackOverflowException
            return j.ToObject<AType>(); 
        if (type == "b")
            return j.ToObject<BType>();

        throw new NotImplementedException(type);
    }
}

[TestFixture]
public class InheritanceSerializeTests
{
    [Test]
    public void Deserialize()
    {
        var json = @"{""Type"":""a"", ""Height"":100}";
        JObject.Parse(json).ToObject<Base>(); // Crash
    }
}

4 个答案:

答案 0 :(得分:4)

我当前正在处理的项目遇到了一个非常类似的问题:我想创建一个自定义JsonConverter并通过属性将其映射到我的实体,但随后代码被困在一个无限循环中。

我的案例中的诀窍是使用serializer.Populate代替JObject.ToObject(即使我愿意,也无法使用.ToObject;我使用的是版本3.5.8 ,其中此功能不存在)。以下是我的ReadJson方法示例:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    JContainer lJContainer = default(JContainer);

    if (reader.TokenType == JsonToken.StartObject)
    {
        lJContainer = JObject.Load(reader);
        existingValue = Convert.ChangeType(existingValue, objectType);
        existingValue = Activator.CreateInstance(objectType);

        serializer.Populate(lJContainer.CreateReader(), existingValue);
    }

    return existingValue;
}

答案 1 :(得分:3)

[JsonConverter(typeof(TypeSerializer))]类中移除Base属性,在Deserialize测试中替换以下行:

JObject.Parse(json).ToObject<Base>(); // Crash

这一个:

var obj = JsonConvert.DeserializeObject<Base>(json, new TypeSerializer());

更新1 此更新与问题提问者的评论相符:

[JsonConverter(typeof(TypeSerializer))]属性保留为Base类。使用以下行进行反序列化:

var obj = JsonConvert.DeserializeObject<Base>(json);

并修改ReadJson方法,如下所示:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var j = JObject.Load(reader);

    if (j["Type"].ToString() == "a")
        return new AType(int.Parse(j["Height"].ToString()));

    return new BType(j["Name"].ToString());
}

答案 2 :(得分:1)

JsonConverters从基类继承。当前没有选择将JsonConverter限制为仅基类。您可以覆盖它。

在Newtonsoft.Json 12.0.3上测试

public class DisabledConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }

    public override bool CanRead => false;
    public override bool CanWrite => false;
}

然后在派生类上覆盖JsonConverter。

[JsonConverter(typeof(DisabledConverter))]
public class AType : Base
...

[JsonConverter(typeof(DisabledConverter))]
public class BType : Base
...

详细信息

这仅适用于代码:

if (type == "a")
    return j.ToObject<AType>(); 
if (type == "b")
    return j.ToObject<BType>();

调用.ToObject时,它将尝试使用基类上定义的转换器(再次)将对象反序列化。这就是造成无限循环的原因。

您需要在派生类上重写JsonConverter。

CanRead => false和CanWrite => false将禁用该类的自定义JsonConverter,强制.ToObject调用在Newtonsoft.Json内部使用默认逻辑,而不是TypeSerializer类。

答案 3 :(得分:0)

我也有类似的问题,并且遇到了无限循环。

我正在使用的Api可能返回错误响应或预期的类型。 我解决问题的方式与其他人使用serializer.Populate突出显示的方式相同。

public class Error{
   public string error_code { get; set; }
   public string message { get; set; }
}

[JsonConverter(typeof(CustomConverter<Success>))]
public class Success{
   public Guid Id { get; set; }
}

public class CustomConverter<T> : JsonConverter where T : new() {
   public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
   {
      JObject jObject = JObject.Load(reader);
      if (jObject.ContainsKey("error_code")) {
          return jObject.ToObject(typeof(ProvisoErrorResponse));
      }

      var instance = new T();
      serializer.Populate(jObject.CreateReader(), instance);
      return instance;
   }
}

然后由HttpClient使用,如下所示:

using (var response = await _httpClient.GetAsync(url))
{
   return await response.Content.ReadAsAsync<Success>();
}

为什么会发生此循环?我认为我们正在谬误。 我最初尝试调用base.ReadJson认为我是在重写现有功能,而实际上有许多JsonConverters且我们的自定义转换器不重写任何内容,因为基类没有实际方法。最好将基类视为接口。 之所以会发生循环,是因为我们注册的转换器是引擎认为最适合要转换类型的转换器。除非我们可以在运行时从转换器列表中删除自己的转换器,否则在自定义转换器中调用引擎进行反序列化将创建无限递归。