JsonConvert.Deserializer索引问题

时间:2016-08-25 05:11:57

标签: c# stack json.net

在C#中使用Stack集合时遇到了以下问题。确切地说,我不确定为什么会这样。请详细说明解决方案的原因和替代方案。

问题 -

具有Stack属性的类。例如,将类命名为Progress。 T属于类型项目。

现在,只要用户取得任何进展,我们将以堆栈形式存储。如果用户介于两者之间,那么下次我们将从该阶段中查看堆栈中的项目。下面的代码片段将介绍正在尝试的内容...

using static System.Console;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace StackCollection
{
    class Program
    {
        static void Main(string[] args)
        {
            Progress progress = new Progress();

            progress.Items.Push(new Item { PlanID = null, PlanName = "Plan A" });

            var jsonString = JsonConvert.SerializeObject(progress);
            var temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            temp.Items.Push(new Item { PlanID = null, PlanName = "Plan B" });

            jsonString = JsonConvert.SerializeObject(temp);
            temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            temp.Items.Push(new Item { PlanID = null, PlanName = "Plan C" });

            jsonString = JsonConvert.SerializeObject(temp);
            temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            WriteLine(temp.Items.Peek().PlanName);

            ReadLine();
        }
    }

    class Progress
    {
        public Stack<Item> Items { get; set; }

        public Progress()
        {
            Items = new Stack<Item>();
        }
    }

    class Item
    {
        public string PlanID { get; set; }

        public string PlanName { get; set; }
    }
}

现在行 -

WriteLine(temp.Items.Peek().PlanName);

应该返回

  

计划C

但它正在返回

  

计划B

因此,为什么要更改索引,任何线索或指针都会有所帮助。

4 个答案:

答案 0 :(得分:3)

由于这是Json.NET的已知行为,如this answer所述,在反序列化按正确顺序推送项目的堆栈时,可以使用custom JsonConverter

以下通用转换器可与"package/include/your/annotation/component/deeper/config"一起用于任何Stack<T>

T

然后将其添加到/// <summary> /// Converter for any Stack<T> that prevents Json.NET from reversing its order when deserializing. /// </summary> public class StackConverter : JsonConverter { // Prevent Json.NET from reversing the order of a Stack<T> when deserializing. // https://github.com/JamesNK/Newtonsoft.Json/issues/971 static Type StackParameterType(Type objectType) { while (objectType != null) { if (objectType.IsGenericType) { var genericType = objectType.GetGenericTypeDefinition(); if (genericType == typeof(Stack<>)) return objectType.GetGenericArguments()[0]; } objectType = objectType.BaseType; } return null; } public override bool CanConvert(Type objectType) { return StackParameterType(objectType) != null; } object ReadJsonGeneric<T>(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var list = serializer.Deserialize<List<T>>(reader); var stack = existingValue as Stack<T> ?? (Stack<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator(); for (int i = list.Count - 1; i >= 0; i--) stack.Push(list[i]); return stack; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; try { var parameterType = StackParameterType(objectType); var method = GetType().GetMethod("ReadJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); var genericMethod = method.MakeGenericMethod(new[] { parameterType }); return genericMethod.Invoke(this, new object[] { reader, objectType, existingValue, serializer }); } catch (TargetInvocationException ex) { // Wrap the TargetInvocationException in a JsonSerializerException throw new JsonSerializationException("Failed to deserialize " + objectType, ex); } } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 以在反序列化时更正堆栈的顺序:

JsonSerializerSettings

或使用[JsonConverter(typeof(StackConverter))]直接标记var settings = new JsonSerializerSettings { Converters = new[] { new StackConverter() } }; var jsonString = JsonConvert.SerializeObject(progress, settings); var temp = JsonConvert.DeserializeObject<Progress>(jsonString, settings); 属性:

Stack<T>

答案 1 :(得分:1)

如果您尝试调试它,那么您会注意到Stack反序列化后项目顺序被破坏了。

一个月前关于JSON.NET GitHub问题跟踪器的问题has been asked

JamesNK的回答:

  

我担心这是Stack的限制。序列化时返回结果,反序列化时返回相反的顺序。

答案 2 :(得分:1)

似乎堆栈被序列化为List。问题是在解构堆栈时这不会保留正确的顺序(项目实际上是以相反的顺序推送)。以下是此问题的快速解决方法:

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;

namespace StackCollection
{
    class Program
    {
        static void Main(string[] args)
        {
            Progress progress = new Progress();

            progress.Items.Push(new Item { PlanID = null, PlanName = "Plan A" });

            var jsonString = JsonConvert.SerializeObject(progress);
            var temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            temp.Items.Push(new Item { PlanID = null, PlanName = "Plan B" });

            jsonString = JsonConvert.SerializeObject(temp);
            temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            temp.Items.Push(new Item { PlanID = null, PlanName = "Plan C" });

            jsonString = JsonConvert.SerializeObject(temp);
            temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            WriteLine(temp.Items.Peek().PlanName);

            ReadLine();
        }
    }

    class Progress
    {
        [JsonIgnore]
        public Stack<Item> Items { get; set; }

        public List<Item> ItemList { get; set; }

        [OnSerializing]
        internal void OnSerializing(StreamingContext context)
        {
            ItemList = Items?.ToList();
        }

        [OnDeserialized]
        internal void OnDeserialized(StreamingContext context)
        {
            ItemList?.Reverse();
            Items = new Stack<Item>(ItemList ?? Enumerable.Empty<Item>());
        }

        public Progress()
        {
            Items = new Stack<Item>();
        }
    }

    class Item
    {
        public string PlanID { get; set; }

        public string PlanName { get; set; }
    }
}

答案 3 :(得分:1)

在Visual Studio 2019中,此C#可以工作:

List<string> ls = null;
Stack<string> ss = null;
if (json != null)
{
    ls = JsonConvert.DeserializeObject<List<string>>(json);
    ss = new Stack<string>(ls);
}

(这是对here答案的编辑,该答案最初在列表上有一个错误的Reverse方法调用,导致与预期结果相反。)