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:
Remove JsonConverter(typeof(ItemConverter))
from Item
. This will allow item.ToObject<Item>()
to work properly.
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)