在我当前的项目中,我遇到的问题是,在尝试转换Item
或其任何子类(如ArmorItem
)时,我最终会陷入无限循环。
要检测我必须反序列化的Item
类型,请使用名为JsonConverter
的自定义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);
}
我通常从网上获取json数据并直接使用WebResponse.GetResponseStream
流来反序列化数据。
using (HttpWebResponse resp = (HttpWebResponse) req.GetResponse())
using (JsonTextReader reader = new JsonTextReader(new StreamReader(resp.GetResponseStream())))
{
return new JsonSerializer().Deserialize<Item>(reader);
}
我知道为什么会发生这种循环,但我无法修复它
但是我注意到,当以不同的方式反序列化json数据时,问题不会发生
(Item
已删除JsonConverter
属性
string json = "SOME JSON DATA HERE";
Item item = JsonConvert.DeserializeObject<Item>(json, new ItemConverter());
不幸的是我无法使用流修复现有代码,我不想将传入的json数据临时存储到字符串中以便能够使用工作代码。
任何想法如何打破循环?
答案 0 :(得分:1)
简而言之,您需要告诉Json.net通过标准转换器反序列化您的json,而不是自定义转换器。虽然有多种方法可以做到,但这是我现在可以提供的方式:
从JsonConverter(typeof(ItemConverter))
移除Item
。这将使item.ToObject<Item>()
正常工作。
现在您需要告诉外部反序列化以使用转换器。要做到这一点:
var settings = new JsonSerializerSettings()
{
Converters = new [] { new ItemConverter() }
};
return JsonSerializer.Create(settings).Deserialize<Item>(reader)
(实际上,您可以缓存设置)
答案 1 :(得分:0)
另一种方法是使用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;
}
}
更多信息,https://gist.github.com/chrisoldwood/b604d69543a5fe5896a94409058c7a95