在 C# 中玩堆栈集合时,我遇到了以下问题。正是我不确定为什么会发生这种情况。请说明原因和解决方案的替代方案。
问题 -
具有 Stack 作为属性的类。例如,将该类命名为 Progress。T 是 Item 类类型。
现在,每当用户取得任何进展时,我们都会将其存储在堆栈中。如果用户在两者之间离开,那么下次我们将从该阶段从堆栈中查看该项目。下面的代码片段将给出正在尝试的内容的想法......
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; }
}
}
Run Code Online (Sandbox Code Playgroud)
现在这条线 -
WriteLine(temp.Items.Peek().PlanName);
Run Code Online (Sandbox Code Playgroud)
应该回来
C计划
但它正在返回
B计划
因此,为什么要更改索引,任何线索或指针都会有所帮助。
由于这是 Json.NET 的已知行为,如本答案所述,在反序列化以正确顺序推送项目的堆栈时,可以使用自定义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();
}
}
Run Code Online (Sandbox Code Playgroud)
然后将其添加到JsonSerializerSettings反序列化时更正堆栈的顺序:
var settings = new JsonSerializerSettings { Converters = new[] { new StackConverter() } };
var jsonString = JsonConvert.SerializeObject(progress, settings);
var temp = JsonConvert.DeserializeObject<Progress>(jsonString, settings);
Run Code Online (Sandbox Code Playgroud)
或者Stack<T>直接用标记属性[JsonConverter(typeof(StackConverter))]:
class Progress
{
[JsonConverter(typeof(StackConverter))]
public Stack<Item> Items { get; set; }
public Progress()
{
Items = new Stack<Item>();
}
}
Run Code Online (Sandbox Code Playgroud)