Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite conversion loop when using custom JsonConverter

Tags:

json

c#

json.net

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?

like image 357
TorbenJ Avatar asked Sep 25 '15 13:09

TorbenJ


1 Answers

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

like image 145
bort Avatar answered Oct 17 '22 01:10

bort