C# 反序列化器索引问题

C# 反序列化器索引问题,c#,stack,json.net,C#,Stack,Json.net,在C#中处理堆栈集合时,我遇到了以下问题。确切地说,我不知道为什么会这样。请说明原因和解决方案的替代方案 问题- 以堆栈为属性的类。例如,将该类命名为Progress。T是类类型项 现在,每当用户取得任何进展时,我们都将存储在堆栈中。如果用户在这两个阶段之间离开,那么下次我们将从堆栈中窥视该项,以便从该阶段开始。下面的代码片段将给出正在尝试的内容的想法 using static System.Console; using System.Collections.Generic; using New

在C#中处理堆栈集合时,我遇到了以下问题。确切地说,我不知道为什么会这样。请说明原因和解决方案的替代方案

问题-

以堆栈为属性的类。例如,将该类命名为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; }
    }
}
应该回来

方案C

但它正在回归

方案B


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

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

一个月前,JSON.NET GitHub issue tracker上也出现了同样的问题

詹姆斯纳的回答是:

恐怕这是一个堆栈的限制。序列化时返回的结果与反序列化时返回的结果的顺序相反


堆栈似乎正在序列化为列表。问题在于,在解构堆栈时,这并没有保持正确的顺序(项目实际上是按相反的顺序推送的)。以下是解决此问题的快速方法:

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; }
    }
}
使用系统;
使用静态系统控制台;
使用System.Collections.Generic;
使用System.Linq;
使用System.Runtime.Serialization;
使用Newtonsoft.Json;
命名空间堆栈集合
{
班级计划
{
静态void Main(字符串[]参数)
{
进度=新的进度();
progress.Items.Push(新项{PlanID=null,PlanName=“Plan A”});
var jsonString=JsonConvert.SerializeObject(进度);
var temp=JsonConvert.DeserializeObject(jsonString);
temp.Items.Push(新项{PlanID=null,PlanName=“Plan B”});
jsonString=JsonConvert.SerializeObject(temp);
temp=JsonConvert.DeserializeObject(jsonString);
temp.Items.Push(新项{PlanID=null,PlanName=“planc”});
jsonString=JsonConvert.SerializeObject(temp);
temp=JsonConvert.DeserializeObject(jsonString);
WriteLine(temp.Items.Peek().PlanName);
ReadLine();
}
}
班级进步
{
[JsonIgnore]
公共堆栈项{get;set;}
公共列表项列表{get;set;}
[正在序列化]
内部void正在序列化(StreamingContext上下文)
{
ItemList=项目?.ToList();
}
[已序列化]
内部已序列化(StreamingContext上下文)
{
ItemList?.Reverse();
Items=新堆栈(ItemList??Enumerable.Empty());
}
公共进步()
{
Items=新堆栈();
}
}
类项目
{
公共字符串PlanID{get;set;}
公共字符串PlanName{get;set;}
}
}

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

以下通用转换器可与任何
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();
    }
}
或者直接用以下标记
堆栈
属性:

课程进度
{
[JsonConverter(类型(堆栈转换器))]
公共堆栈项{get;set;}
公共进步()
{
Items=新堆栈();
}
}

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

List ls=null;
堆栈ss=null;
if(json!=null)
{
ls=JsonConvert.DeserializeObject(json);
ss=新堆栈(ls);
}

(这是对来自的答案的编辑,它最初在列表上有一个错误的反向方法调用,导致了与期望结果相反的结果。)

也有一些调查显示,JSON字符串具有正确的序列,但在最后一个temp=JsonConvert.DeserializeObject(jsonString)之后;索引移动。感谢指针,在堆栈中再观察1次,每当一个项目被推到索引0时,它就会被推到索引0。所以,当我们运行类似foreach(var item in _temp){在LIFO中开始弹出}的循环时,但是当我们使用JSONConvert反序列化时,我认为它试图反序列化为Queue-FIFO。不确定,只是胡乱猜测。是的,我同意项目按相反顺序推送。让我试试这个解决办法。
/// <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();
    }
}
var settings = new JsonSerializerSettings { Converters = new[] { new StackConverter() } };
var jsonString = JsonConvert.SerializeObject(progress, settings);
var temp = JsonConvert.DeserializeObject<Progress>(jsonString, settings);
class Progress
{
    [JsonConverter(typeof(StackConverter))]
    public Stack<Item> Items { get; set; }

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