JsonConvert.Deserializer indexing issues

后端 未结 4 614
情话喂你
情话喂你 2020-11-29 14:07

While playing around Stack collection in C# I encountered the following issue. Exactly I am not sure why it is happening. Please put some light on the reason and alternative

4条回答
  •  遥遥无期
    2020-11-29 14:18

    Since this is a known behavior of Json.NET, as noted by this answer, a custom JsonConverter can be used when deserializing a stack that pushes items on in the correct order.

    The following universal converter can be used with Stack for any T:

    /// 
    /// Converter for any Stack that prevents Json.NET from reversing its order when deserializing.
    /// 
    public class StackConverter : JsonConverter
    {
        // Prevent Json.NET from reversing the order of a Stack 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(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
            var list = serializer.Deserialize>(reader);
            var stack = existingValue as Stack ?? (Stack)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();
        }
    }
    

    Then add it to JsonSerializerSettings to correct the ordering of stacks when deserializing:

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

    Or mark the Stack property directly with [JsonConverter(typeof(StackConverter))]:

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

提交回复
热议问题