Introduction
If you have to deserialize a JSON stream into a concrete class that has interface properties, Json.NET helps you to serialize the object adding a $type
property to the JSON, which allows him to know which concrete type it needs to deserialize the object into.
Using the Code
I have been facing the same problem these days, and I came to this easy, fast and readable solution:
Let's say that we use Json.NET to serialize/deserialize objects sent-to/received-from a Web Service, and we want to serialize/deserialize classes that have interface properties.
Serializing will give us no issues (using the standard settings), but when we will try to deserialize the model, we will get the following error:
Quote:
Could not create an instance of type Namespace.Intarface. Type is an interface or abstract class and cannot be instantiated.
- Serializing a class that implements an interface is simple.
- Deserializing JSON to one of many possible classes that implement an interface is not.
- Json.NET allows us to solve this problem by simply adding an extra settings during the serialization process.
We just need to add an extra setting during the serialization:
var indented = Formatting.Indented;
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
};
string serialized = JsonConvert.SerializeObject(wizardConf, indented, settings);
And then apply the same setting during the deserialization:
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
};
YourObject obj = JsonConvert.DeserializeObject<yourobject>(JsonString, settings);
So, let's say we got to serialize an object containing two list of Interfaces:
public class Class1 : IInterface1
{
public void method1()
{
}
}
public class Class2 : IInterface2
{
public void method2()
{
}
}
public class ObjToSerialize
{
public List<IInterface1> list1;
public List<IInterface2> list2;
public ObjToSerialize()
{
list1 = new List<IInterface1>();
list2 = new List<IInterface2>();
}
}
And at runtime, we added one object to each list:
ObjToSerialize obj = new ObjToSerialize();
obj.list1.Add(new Class1());
obj.list2.Add(new Class2());
By adding the TypeNameHandling
flag, the serialized class will appear as follows:
{
"$type": "Namespace.ObjToSerialize, AssemblyName",
"list1": {
"$type": "System.Collections.Generic.List`1[[Namespace.IInterface1, AssemblyName]], mscorlib",
"$values": [
{
"$type": "Namespace.Class1, AssemblyName"
}
]
},
"list2": {
"$type": "System.Collections.Generic.List`1[[Namespace.IInterface2, AssemblyName]], mscorlib",
"$values": [
{
"$type": "Namespace.Class2, AssemblyName"
}
]
}
}
The resulting JSON file basically tells everything we need to know.
It says that the ObjToSerialize
contains two properties, each of them is a List of Interfaces, whose implementation is Class1
in the first case and Class2
in the second case.
The deserializer will take this string
and easily reconstruct the object as it has all the details of the interface implementation.
Quote:
The TypeNameHandling
flag adds a $type
property to the JSON.
The $type
is a fully-qualified type which allows Json.NET to know which concrete type it needs to deserialize the object into. This allows you to deserialize an object while still fulfilling an interface or abstract base class.
The downside, however, is that this is very Json.NET-specific.