使用自定义 JsonConverter 时的无限转换循环

Tor*_*enJ 7 c# json json.net

在我目前的项目我有我想转换的时候在一个无限循环结束的问题Item或任何其子类等ArmorItem
为了检测Item我必须反序列化的类型,我使用了一个JsonConverter名为ItemConverter.

项目.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

我通常是从网上获取json数据,直接使用WebResponse.GetResponseStream流来反序列化数据。

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

我知道为什么会发生这个循环,但我无法修复它。
但是我注意到当以不同的方式反序列化 json 数据时,问题不会发生。
Item通过删除JsonConverter属性为此进行了更改)

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

不幸的是,我无法使用流修复现有代码,并且我不想将传入的 json 数据临时存储到字符串中以便能够使用工作代码。
任何想法如何打破循环?

小智 5

另一种方法是使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

更多信息请访问https://gist.github.com/chrisoldwood/b604d69543a5fe5896a94409058c7a95


Ser*_*sev 2

简而言之,您需要告诉 Json.net 通过标准转换器反序列化您的 json,而不是您自定义的转换器。虽然有不止一种方法可以做到这一点,但这是我现在可以提供的一种:

  1. JsonConverter(typeof(ItemConverter))从......中去除Item。这将允许item.ToObject<Item>()正常工作。

  2. 现在您需要告诉外部反序列化使用转换器。要做到这一点:

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

    (其实你可以缓存设置)

  • 实际上我不喜欢这个解决方案,因为用属性注释类更干净。创建样板序列化代码以在对服务的每个异步调用上运行(因为 HttpClient 中使用了 Newtonsoft.Json)有点难看。它应该能够在自定义转换器中得到妥善处理。如果这是不可能的,那么框架中就有一个漏洞,最好填补一下 (2认同)