Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ignore $type during deserialization

Tags:

c#

json.net

I have 2 classes like this

class A: Base
{
    public string CommonField;
    public int IntField;
}

class B: Base
{
    public string CommonField;
    public double DoubleField;
}

I serialize A instance with type name handling

{
  "$type": "MyApp.A, MyApp",
  "CommonField": "SomeValue",
  "IntField": 123,
  "SomeBaseField": 321
}

What would be the easiest way to get B instance from this? Before you ask why, it's about importing A as B (they have base properties and those what names are matching as you see, so this operation is reasonable).

Trying to deserialize given json as B will throw

Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'MyApp.A, MyApp, Version=0.0.1.1, Culture=neutral, PublicKeyToken=null' is not compatible with 'MyApp.B, MyApp, Version=0.0.1.1, Culture=neutral, PublicKeyToken=null'. Path '$type', line 2, position 42.
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)

I could think of following:

  1. Deserialize json as A, create instance of B and copy all values (bad);
  2. Substitute (maybe even remove?) $type in json and try to deserialize it as B (better, still bad);
  3. Use SerializationBinder and deserialize json as B (good):


public class JsonABBinder : DefaultSerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        if (typeName == "MyApp.A")
             return typeof(B);
        return base.BindToType(assemblyName, typeName);
    }
}

Is there better way? In my scenario there are many such types and having hundereds of such binders (as well as reading json first as A to determine which binder to apply) is very tedious.

I am looking for a simple way to ignore $type and specify wanted type directly (generic method), because I know type I need, but I don't know which type is in json and I want to ignore it.

like image 288
Sinatr Avatar asked Aug 19 '16 09:08

Sinatr


2 Answers

You can use:

new JsonSerializer().TypeNameHandling = TypeNameHandling.None

This will ignore $type.

like image 108
Mike Sackton Avatar answered Oct 25 '22 02:10

Mike Sackton


It would be a good idea to pass in some settings when deseralizing the type:

private readonly JsonSerializerSettings _settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.None
};

Then use it like:

var json = JsonConvert.DeserializeObject<object>(jsonString, _settings);

Or even simpler

var json = JsonConvert.DeserializeObject<object>(jsonString, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.None
});
like image 31
Jamie Rees Avatar answered Oct 25 '22 02:10

Jamie Rees