在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
因此,为什么要更改索引,任何线索或指针都会有所帮助。
答案 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方法调用,导致与预期结果相反。)