JsonConvert.Deserializer索引问题

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

问题 –

具有Stack作为属性的类。 例如,将类命名为Progress。 T是类型Item。

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

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(jsonString); temp.Items.Push(new Item { PlanID = null, PlanName = "Plan B" }); jsonString = JsonConvert.SerializeObject(temp); temp = JsonConvert.DeserializeObject(jsonString); temp.Items.Push(new Item { PlanID = null, PlanName = "Plan C" }); jsonString = JsonConvert.SerializeObject(temp); temp = JsonConvert.DeserializeObject(jsonString); WriteLine(temp.Items.Peek().PlanName); ReadLine(); } } class Progress { public Stack Items { get; set; } public Progress() { Items = new Stack(); } } class Item { public string PlanID { get; set; } public string PlanName { get; set; } } } 

现在的线 –

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

应该回来

计划C.

但它正在回归

B计划

那么,为什么索引被改变,任何线索或指针都会有所帮助。

看起来堆栈被序列化为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(jsonString); temp.Items.Push(new Item { PlanID = null, PlanName = "Plan B" }); jsonString = JsonConvert.SerializeObject(temp); temp = JsonConvert.DeserializeObject(jsonString); temp.Items.Push(new Item { PlanID = null, PlanName = "Plan C" }); jsonString = JsonConvert.SerializeObject(temp); temp = JsonConvert.DeserializeObject(jsonString); WriteLine(temp.Items.Peek().PlanName); ReadLine(); } } class Progress { [JsonIgnore] public Stack Items { get; set; } public List ItemList { get; set; } [OnSerializing] internal void OnSerializing(StreamingContext context) { ItemList = Items?.ToList(); } [OnDeserialized] internal void OnDeserialized(StreamingContext context) { ItemList?.Reverse(); Items = new Stack(ItemList ?? Enumerable.Empty()); } public Progress() { Items = new Stack(); } } class Item { public string PlanID { get; set; } public string PlanName { get; set; } } } 

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

以下通用转换器可与Stack用于任何T

 ///  /// Converter for any Stack that prevents Json.NET from reversing its order when deserializing. ///  public class StackConverter : JsonConverter { // Prevent Json.NET from reversing the order of a Stack 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(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var list = serializer.Deserialize>(reader); var stack = existingValue as Stack ?? (Stack)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以在反序列化时更正堆栈的顺序:

 var settings = new JsonSerializerSettings { Converters = new[] { new StackConverter() } }; var jsonString = JsonConvert.SerializeObject(progress, settings); var temp = JsonConvert.DeserializeObject(jsonString, settings); 

或者直接使用[JsonConverter(typeof(StackConverter))]标记Stack属性:

 class Progress { [JsonConverter(typeof(StackConverter))] public Stack Items { get; set; } public Progress() { Items = new Stack(); } } 

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

一个月前,JSON.NET GitHub问题跟踪器也提出了同样的问题。

JamesNK的答案:

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