0

I have a Json serialization problem with the stack data structure. The order is reversed when I deserialize the stack data structure.

e.g.

before deserialization:

stack={1,2,3,4} 

after deserialization:

stack={4,3,2,1} 

I considered to solve this problem by make a custom Json Converter by using the method below.

JsonConvert.Deserializer indexing issues

/// <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 is below
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
            .AddJsonOptions(opt =>
            {
                opt.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
                //opt.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;
                opt.SerializerSettings.DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset;
                opt.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat;
                opt.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.RoundtripKind;
                opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                opt.SerializerSettings.ConstructorHandling = Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor;
                opt.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
                opt.SerializerSettings.Converters.Add(new StackConverter());
            });

But by this method I'm facing another error now.

Error:

Could not create an instance of type FTest.Total. Type is an interface or abstract class and cannot be instantiated. Path 'Basket.Totals[0].Value', line 17, position 16.

The Totals JSON is like this below.

"Totals":

 [ { "$id": "3", 

"$type": "FTest.PointTargetAmount, FTest.Basket", 

"Value": 0.0 }, ...... ] 

The Totals Class is like this below.

using System;
using System.Runtime.Serialization;
namespace FTest
{
    [DataContract(Name = "Total", IsReference = true)]
    public abstract class Total : IExtensibleDataObject
    {
        [DataMember]
        public virtual Decimal Value { get; set; }

        ExtensionDataObject IExtensibleDataObject.ExtensionData { get; set; }
    }
    [DataContract(IsReference = true)]
    public class PointTargetAmount : Total
    {
    },
....
}

please help me If you have any idea.

songqx
  • 1
  • 1
  • Why did you include the `C` tag? This has nothing to do with C. – torstenvl Jul 27 '23 at 02:51
  • 1
    How are you attempting to deserialize? Your previous question was tagged Json.NET so how are you invoking the serializer? What are your `JsonSerializerSettings`? Can you share a **fully compilable example** of how you are trying to deserialize -- i.e. a [mcve]? – dbc Jul 27 '23 at 03:20
  • I attach the custom Json Converter and the JsonSerializerSettings .I hope It will be helpful. – songqx Jul 27 '23 at 04:37
  • Should I upload the others example? – songqx Jul 27 '23 at 04:59
  • Why did you comment out `//opt.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;`? Enabling [``](https://www.newtonsoft.com/json/help/html/serializetypenamehandling.htm) will allow Json.NET to use the `"$type": "FTest.PointTargetAmount, FTest.Basket"` type metadata to construct a `PointTargetAmount` for the abstract type `Total`. – dbc Jul 27 '23 at 05:31
  • 1
    I try to uncomment the opt.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto,then the error is solved. Thank for you help @dbc. – songqx Jul 27 '23 at 09:49

0 Answers0