简体   繁体   中英

Infinite conversion loop when using custom JsonConverter

In my current project I have the problem that I end up in an infinite loop when trying to convert an Item or any of its subclasses like ArmorItem .
To detect which type of Item I have to deserialize I use a custom JsonConverter called ItemConverter .

Item.cs:

[JsonObject(MemberSerialization.OptIn), JsonConverter(typeof(ItemConverter))]
public class Item
{
    [JsonProperty("id")] public int Id { get; }
    [JsonProperty("type")] public string ItemType { get; }

    [JsonConstructor]
    public Item(int id, string itemType)
    {
        Id = id;
        ItemType = itemType;
    }
}

ArmorItem.cs

[JsonObject(MemberSerialization.OptIn)]
public sealed class ArmorItem : Item
{
    [JsonProperty("defense")] public int Defense { get; }

    [JsonConstructor]
    public ArmorItem(int id, string itemType, int defense) : base(id, itemType)
    {
        Defense = defense;
    }
}

ItemConverter.cs

public sealed class ItemConverter : 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)
    {
        JObject item = JObject.Load(reader);

        switch (item["type"].Value<string>())
        {
            case "Armor":
                return item.ToObject<ArmorItem>();
            default:
                return item.ToObject<Item>();
        }
    }

    public override bool CanConvert(Type objectType)
        => typeof (Item).IsAssignableFrom(objectType);
}

I'm usually getting the json data from the web and directly use the WebResponse.GetResponseStream stream to deserialize the data.

using (HttpWebResponse resp = (HttpWebResponse) req.GetResponse())
using (JsonTextReader reader = new JsonTextReader(new StreamReader(resp.GetResponseStream())))
{
    return new JsonSerializer().Deserialize<Item>(reader);
}

I know why this loop occurs but I can't fix it.
However I noticed when deserializing the json data in a different way the problem doesn't occur.
( Item was altered for this by removing the JsonConverter attribute)

string json = "SOME JSON DATA HERE";
Item item = JsonConvert.DeserializeObject<Item>(json, new ItemConverter());

Unfortunately I cannot fix the existing code using streams and I don't want to temporarily store the incoming json data into a string to be able to use the working code.
Any ideas how to break the loop?

In short, you need to tell Json.net to deserialize your json via standard converter, not your custom one. While there's more than one way to do it, this is the one I can offer right now:

  1. Remove JsonConverter(typeof(ItemConverter)) from Item . This will allow item.ToObject<Item>() to work properly.

  2. Now you need to tell your outer deserialization to use the converter. To do that:

     var settings = new JsonSerializerSettings() { Converters = new [] { new ItemConverter() } }; return JsonSerializer.Create(settings).Deserialize<Item>(reader) 

    (actually, you can cache the settings)

Another way is to use serializer.Populate() :

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

    switch (item["type"].Value<string>())
    {
        case "Armor":
            var armorItem = new ArmorItem();
            serializer.Populate(item.CreateReader(), armorItem);
            return armorItem;
        default:
            var defaultItem = new Item();
            serializer.Populate(item.CreateReader(), defaultItem);
            return defaultItem;
    }
}

More infos at https://gist.github.com/chrisoldwood/b604d69543a5fe5896a94409058c7a95

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM