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.