50

Using version 7.0.1 Beta3, I'm trying to serialize/deserialize a complex POCO with properties that are arrays of abstract classes. These arrays could contain instance of classes that are derived from abstract ones.

At serialization, everything seems OK. The Json fragment below shows that the type information is set correctly.

The Json fragment:

 "Items": 
 [
     {
         "$type": "IVXB_TS, ...",
         "inclusive": true,
         "value": "20091231"
     }
 ]

But a deserialization it fails with the following error:

Could not create an instance of type QTY. Type is an interface or abstract class and cannot be instantiated.

The class hierarchy is the following :

[System.Xml.Serialization.XmlIncludeAttribute(typeof(IVXB_TS))]
public abstract partial class ANY : object, System.ComponentModel.INotifyPropertyChanged
{
}

[System.Xml.Serialization.XmlIncludeAttribute(typeof(IVXB_TS))]
public abstract partial class QTY : ANY
{
}

[System.Xml.Serialization.XmlIncludeAttribute(typeof(IVXB_TS))]
public partial class TS : QTY
{
}

public partial class IVXB_TS : TS
{
}

The Items property :

[System.Xml.Serialization.XmlElementAttribute("high", typeof(IVXB_TS))]
[System.Xml.Serialization.XmlElementAttribute("low", typeof(IVXB_TS))]
public QTY[] Items

The type information in the Json fragment seems to not be used. Is this a deserialization configuration issue?

Furkan Ekinci
  • 2,472
  • 3
  • 29
  • 39

3 Answers3

68

The solution to this problem is to configure the deserializer to use the type information in the json. It is not used by default.

Serialization is done this way:

Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Converters.Add(new Newtonsoft.Json.Converters.JavaScriptDateTimeConverter());
serializer.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
serializer.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;
serializer.Formatting = Newtonsoft.Json.Formatting.Indented;

using (StreamWriter sw = new StreamWriter(fileName))
using (Newtonsoft.Json.JsonWriter writer = new Newtonsoft.Json.JsonTextWriter(sw))
{
    serializer.Serialize(writer, obj, typeof(MyDocumentType));
}

At deserialization, the settings for TypeNameHandling must be set:

MyDocumentType  obj = Newtonsoft.Json.JsonConvert.DeserializeObject<MyDocumentType>(File.ReadAllText(fileName), new Newtonsoft.Json.JsonSerializerSettings 
{ 
    TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto,
    NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
});
Furkan Ekinci
  • 2,472
  • 3
  • 29
  • 39
  • 5
    This helped solve the problem for me. An additional note if you are deserializing from a database or external data source: make sure the $type field is the first key in your dictionary/json string. – Ian Newland Sep 13 '21 at 02:48
  • Please note that setting `TypeNameHandling` to anything other than `None` without also setting a proper `SerializationBinder` will result in a security vulnerability: https://stackoverflow.com/questions/39565954/typenamehandling-caution-in-newtonsoft-json – pooya13 Jul 04 '23 at 18:55
16

In my case, setting the TypeNameHandling to Auto didn't fix the problem, but setting it to All fixed the issue. As the TypeNameHandling = TypeNameHandling.All seemed a bit overkill to me, I chose Auto again, but this time, I passed the type of my root object to the JsonConvert.SerializeObject function as well:

var settings = new JsonSerializerSettings{ TypeNameHandling = TypeNameHandling.Auto };

var json = JsonConvert.SerializeObject(obj, typeof(ObjType), settings);

var deserializedObj = JsonConvert.DeserializeObject<ObjType>(json, settings);
Amir Mahdi Nassiri
  • 1,190
  • 13
  • 21
-2

If you specify Null Value Handling you won't get this error.

    new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore
    }));

You could also set the [JsonIgnore] flag

Image of the JsonIgnore flag

Both of these options worked fine for the use case that brought me here.

Type name handling wasn't necessary.