c# stack json.net

c# - Problemas de indexación de JsonConvert.Deserializer



stack json.net (3)

Dado que este es un comportamiento conocido de Json.NET, como se señala en esta respuesta , se puede usar un JsonConverter personalizado al deserializar una pila que empuja los elementos en el orden correcto.

El siguiente conversor universal se puede usar con Stack<T> para cualquier 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(); } }

Luego agréguelo a JsonSerializerSettings para corregir el orden de las pilas al deserializar:

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

O marque la propiedad Stack<T> directamente con [JsonConverter(typeof(StackConverter))] :

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

Mientras jugaba con la colección Stack en C # me encontré con el siguiente problema. Exactamente no estoy seguro de por qué está sucediendo. Por favor, aclare el motivo y la alternativa a la solución.

Problema

Una clase que tiene Stack como propiedad. Por ejemplo, nombre esa clase como Progreso. T es de tipo de clase Artículo.

Ahora, cuando el usuario haga algún progreso, lo almacenaremos en la pila. Y si el usuario se va en el medio, entonces la próxima vez miraremos el artículo desde la pila, desde esa etapa. El siguiente fragmento de código dará una idea de lo que se está intentando ...

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; } } }

ahora la línea

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

debería volver

Plan C

pero esta volviendo

Plan B

Entonces, por qué se cambia el índice, cualquier pista o puntero será útil.


Parece que la pila se está serializando como una Lista. El problema es que esto no conserva el orden correcto al deconstruir la pila (los elementos se empujan en el orden inverso). Aquí hay una solución rápida a este problema:

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; } } }


Si intenta depurarlo, notará que el pedido de artículos se rompe después de la deserialización de la Stack .

La misma pregunta se hizo en el rastreador de problemas JSON.NET GitHub hace un mes.

La respuesta de JamesNK:

Me temo que esto es una limitación de una pila. Los resultados devueltos cuando se serializa y el orden opuesto para cuando se deserializa.