JsonConvert.Deserializer 索引问题
Posted
技术标签:
【中文标题】JsonConvert.Deserializer 索引问题【英文标题】:JsonConvert.Deserializer indexing issues 【发布时间】:2017-01-01 09:42:15 【问题描述】:在 C# 中玩 Stack 集合时,我遇到了以下问题。确切地说,我不确定为什么会这样。请说明原因和解决方案的替代方案。
问题 -
具有 Stack 作为属性的类。例如,将该类命名为 Progress。 T 是类类型 Item。
现在,每当用户取得任何进展时,我们都会将其存储在堆栈中。如果用户在两者之间离开,那么下次我们将从堆栈中窥视该项目,以便从那个阶段开始。下面的代码 sn-p 将给出正在尝试什么的想法......
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计划
那么,为什么要更改索引,任何线索或指针都会有所帮助。
【问题讨论】:
也很少有调查显示,JSON 字符串的顺序正确,但在最后一个 temp = JsonConvert.DeserializeObject 【参考方案1】:由于这是 Json.NET 的已知行为,如 this answer 所述,在反序列化以正确顺序推送项目的堆栈时可以使用 custom JsonConverter
。
以下通用转换器可与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
以更正反序列化时的堆栈顺序:
var settings = new JsonSerializerSettings Converters = new[] new StackConverter() ;
var jsonString = JsonConvert.SerializeObject(progress, settings);
var temp = JsonConvert.DeserializeObject<Progress>(jsonString, settings);
或者直接用[JsonConverter(typeof(StackConverter))]
标记Stack<T>
属性:
class Progress
[JsonConverter(typeof(StackConverter))]
public Stack<Item> Items get; set;
public Progress()
Items = new Stack<Item>();
【讨论】:
【参考方案2】:似乎堆栈正在被序列化为列表。问题是这在解构堆栈时不能保持正确的顺序(项目实际上是按相反的顺序推送的)。以下是此问题的快速解决方法:
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】:如果您尝试调试它,那么您会注意到在Stack
反序列化之后项目顺序被破坏。
一个月前 JSON.NET GitHub 问题跟踪器上的相同问题 has been asked。
JamesNK 的回答:
恐怕这是堆栈的限制。序列化时返回的结果与反序列化时的顺序相反。
【讨论】:
感谢指针,还有 1 次观察,在 Stack 中,每当一个项目被推送时,它都会被推送到索引 0。所以当我们像 foreach(var item in _temp) 一样运行 for 循环时,它开始弹出在 LIFO 但是当我们使用 JSONConvert 反序列化时,我认为它会尝试反序列化为 Queue - FIFO。不确定只是一个疯狂的猜测。【参考方案4】:在 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 方法调用,导致与预期结果相反。)
【讨论】:
以上是关于JsonConvert.Deserializer 索引问题的主要内容,如果未能解决你的问题,请参考以下文章