Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialization with inheritance Newtonsoft JSON.NET not working properly

(Using Newtonsoft JSON.NET)

Hi I've got a problem when deserializing a list of events where Event is the base type and e.g. ResourceEvent is a subclass. If I just do a serialize and then deserialize everything works ok and the list contains the ResourceEvents - however I store those using EF7 into a jsonb column in postgres.

When I get the JSON payload from postgres it has reordered (but JSON legal) the properties. When I try to deserialize using Newtonsoft JSON.net it only give me a list with base types Event and not the subclass ResourceEvent.

I've added the two highly stripped examples where the differences is where the "Type" property is on different places. The ordering of other properties has also been scrambeled in the classes. I've got no annotations on the classes at all.

This payload successfully deseralizes:

json
{
    "$type": "System.Collections.Generic.List`1[[Event, Tests]], mscorlib",
    "$values": [
        {
            "$type": "ResourceConfigurationEvent, Tests",
            /* Properties */
            "Resources": {
                /* Properties */                
            },
            "Type": 1            
        }
    ]
}

This payload unsuccessfully deserializes:

json
{
    "$type": "System.Collections.Generic.List`1[[Event, Tests]], mscorlib",
    "$values": [
        {
            "Type": 1,            
            "$type": "ResourceConfigurationEvent, Tests",
            /* Properties */
            "Resources": {
                /* Properties */                
            },
        }
    ]
}
like image 861
Mario Toffia Avatar asked Jun 01 '16 08:06

Mario Toffia


People also ask

How does Newtonsoft JSON deserialize work?

Newtonsoft. Json uses reflection to get constructor parameters and then tries to find closest match by name of these constructor parameters to object's properties. It also checks type of property and parameters to match. If there is no match found, then default value will be passed to this parameterized constructor.

Is Newtonsoft JSON obsolete?

Yet Newtonsoft. Json was basically scrapped by Microsoft with the coming of . NET Core 3.0 in favor of its newer offering designed for better performance, System.

Is Newtonsoft JSON compatible with .NET core?

Text. Json library is included in the runtime for . NET Core 3.1 and later versions. For other target frameworks, install the System.

Is JSON net the same as Newtonsoft?

By default, Newtonsoft. Json does case insensitive property name matching during deserialization whereas System. Text. Json does case sensitive matching (with exception in ASP.Net core where you don't need to do anything to achieve behavior like Newtonsoft.


1 Answers

Json.Net normally expects the $type metadata to be the first property of each object for best efficiency in deserialization. If the $type does not appear first, then Json.Net assumes it isn't there. That is why you are getting different results when the properties are reordered.

Fortunately, Json.Net provides a MetadataPropertyHandling setting to allow it to cope with this situation. If you set MetadataPropertyHandling to ReadAhead it should solve your problem. Note that this will have an impact on performance.

Here is a code sample from the documentation.

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
});

User u = (User)o;

Console.WriteLine(u.Name);
// James
like image 148
Brian Rogers Avatar answered Sep 19 '22 09:09

Brian Rogers