Newtonsoft JSON.Net Deserialization Error Where Fields in JSON Change Order

Newtonsoft JSON.net deserialization error where fields in JSON change order

Set JsonSerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead.

According to the documentation:

This sample deserializes JSON with MetadataPropertyHandling set to ReadAhead so that metadata properties do not need to be at the start of an object.

 string json = @"{
'Name': 'James',
'Password': 'Password1',
'$type': 'MyNamespace.User, MyAssembly'
}";

object o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
// $type no longer needs to be first
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
});

Note that this setting will impact performance.

Finally, when using TypeNameHandling, do take note of this caution from the Newtonsoft docs:

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json.

JsonConvert.SerializeObject changes the sort order of fields in JSON

It turns out that JsonConvert.SerializeObject doesn't guarantee the default order of fields. To specify an explicit sorting, you can use the DefaultContractResolver

Thanks Andy for the idea!

Implementation of custom DefaultContractResolver:

 public class OrderedContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return base.CreateProperties(type, memberSerialization).OrderBy(p=>p.PropertyName).ToList();
}
}

Usage example:

 var jsonSerializerSettings = new JsonSerializerSettings  {ContractResolver = new OrderedContractResolver()};

var str = JsonConvert.SerializeObject(token, jsonSerializerSettings);

Newtonsoft JSON.NET Deserialization error

You must browse through Response<> and MyEntity and see how collections are initialized. This tells us that one of the collections in some class is created using Where method from linq. You can reproduce this error by executing this code:

class MyEntity
{
public MyEntity()
{
Data = new List<string>().Where(x => true);
}

public IEnumerable<string> Data { get; set; }

}

class Program
{
static void Main(string[] args)
{
string data = @"[{""Data"":[""a"",""b""]}]";
var j = JsonConvert.DeserializeObject<IEnumerable<MyEntity>>(data);
}
}

Another possibility is to have metadata in json. Then you have 2 solutions:

  • Set TypeNameHandling to TypeNameHandling.None (as somebody mentioned in comment);
  • Replace not working types in string with working ones

Using TypeNameHandling.None may lead to wrong deserialization for exmaple when you have IEnumerable<BaseType> and that list contains subtype of BaseType.

In that case you should go for second option. Basically you should replace any type that is not deserializing and replace it for example with List.

Sample code:

class MyEntity
{
public IEnumerable<string> Data { get; set; }
}

class Program
{
static void Main(string[] args)
{
IList<MyEntity> entities = new MyEntity[] {
new MyEntity { Data = new [] { "1", "2" }.Where(x => x != string.Empty) },
new MyEntity { Data = new [] { "A", "B" }.AsQueryable().Where(x => x != string.Empty) },
new MyEntity { Data = new List<string> { "A", "B" } },
};

string data = JsonConvert.SerializeObject(entities, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
data = Regex.Replace(data, "\"\\$type\":\\s+\"System.Linq.Enumerable\\+WhereArrayIterator(.+?), System.Core\",", "\"$type\": \"System.Collections.Generic.List$1, mscorlib\",", RegexOptions.Singleline);

var j = JsonConvert.DeserializeObject<IEnumerable<MyEntity>>(data, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
}
}

Error Deserializing JSON Object/Array using newtonsoft

Look at Camilo Martinez answer on this discussion :
Deserializing JSON when sometimes array and sometimes object

Basically, you'll need to add the JsonConverter attribute to your BookingInfo property and handle the conversion in a JsonConverter implementation.

public class FlightOptionsList
{
public List<FlightOption> FlightOption { get; set; }
}

public class FlightOption
{
public string LegRef { get; set; }
public string Destination { get; set; }
public string Origin { get; set; }
public Option Option { get; set; }
}

public class Option
{
public string Key { get; set; }
public string TravelTime { get; set; }
[JsonConverter(typeof(SingleValueArrayConverter<BookingInfo>))]
public List<BookingInfo> BookingInfo { get; set; }
public Connection Connection { get; set; }
}

public class BookingInfo
{
public string BookingCode { get; set; }
public string BookingCount { get; set; }
public string CabinClass { get; set; }
public string FareInfoRef { get; set; }
public string SegmentRef { get; set; }
}

public class Connection
{
public string SegmentIndex { get; set; }
}

And here's the converter :

public class SingleValueArrayConverter<T> : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object retVal = new Object();
if (reader.TokenType == JsonToken.StartObject)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T>() { instance };
}
else if (reader.TokenType == JsonToken.StartArray)
{
retVal = serializer.Deserialize(reader, objectType);
}
return retVal;
}

public override bool CanConvert(Type objectType)
{
return true;
}
}

json.net deserialization throwing exception when json is loaded from project resource

After /u/dbc's comment that the byte sequence indicated that the encoding of the resource file was UTF-8-BOM, I solved it this way:

  • I went to the source file in disk that was treated as a resource in my project
  • I edited it with Notepad++
  • Encoding -> Convert to UTF-8

After that, the exact same code posted in the original post worked perfectly.

JSON deserialization error when property name is missing

The problem here is the JSON response is actually an array of mixed types. The first element of the array is a string, and the second element is an array of event objects. You will need a custom JsonConverter to deserialize this JSON.

Here is the code you would need for the converter:

class ServiceResponceConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(ServiceResponce));
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray ja = JArray.Load(reader);
ServiceResponce resp = new ServiceResponce();

resp.Events = ja[1].ToObject<Event[]>(serializer);

return resp;
}

public override bool CanWrite => false;

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

Then, add a [JsonConverter] attribute to the ServiceResponce class to tie it to the converter:

[JsonConverter(typeof(ServiceResponceConverter))]
public class ServiceResponce
{
public Event[] Events { get; set; }
}

Now you can deserialize to the ServiceResponce class as normal and it will work properly.

Optional: If you also want to capture the "Successful Request: 96 Results" string from the response, add

public string ResultString { get; set; }

to the ServiceResponce class and add the following line to the the ReadJson method of the converter:

resp.ResultString = (string)ja[0];

Working demo here: https://dotnetfiddle.net/opPUmX

Is array order preserved when deserializing using json.net?

It depends on what collection you're using.

Any class implementing IEnumerable/IEnumerable<T> can be serialized as a JSON array. Json.NET processes collection sequentally, that is, it will serialize array items in the order the collection returns them from GetEnumerator and will add items to the collection in the order they're deserialized from JSON file (using either Add method in case of mutable collections, and constructor with collection argument in case of immutable collections).

That means that if the collection preserves the order of items (T[], List<T>, Collection<T>, ReadOnlyCollection<T> etc.), the order will be preserved when serializing and deserializing. However, if a collection doesn't preserve the order of items (HashSet<T> etc.), the order will be lost.

The same logic applies to JSON objects. For example, the order will be lost when serializing Dictionary<TKey, TValue>, because this collection doesn't preserve the order of items.

Getting error deserialising JSON to ListT

Your Json is not a List<Product>, its an object with a single property called records which is a List<Product>.

So your actual C# model is this:

public class RootObject
{
public List<Product> records { get; set; }
}

And you deserialize like this:

RootObject productsRoot = JsonConvert.DeserializeObject<RootObject>(content);


Related Topics



Leave a reply



Submit