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
toTypeNameHandling.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
A Reusable Pattern to Convert Event into Task
When a Class Is Inherited from List<>, Xmlserializer Doesn't Serialize Other Attributes
Persist Data by Programming Against Interface
Is There Any Connection String Parser in C#
Get an Idatareader from a Typed List
Using Propertyinfo to Find Out the Property Type
C# How to Loop While Mouse Button Is Held Down
Can't Change Gameobject Color via Script
Get List of Zero Reference Codes in Visual Studio
Best Way to Convert Callback-Based Async Method to Awaitable Task