Question

I have the following situation (for this question):

  • A document class
  • One document can contain multiple items, of various types

Like this LINQPad program:

void Main()
{
    var doc = new Document();
    doc.Items.Add(new ItemA { Name = "Test Name" });
    doc.Items.Add(new ItemB { Value = 42 });

    string json = JsonConvert.SerializeObject(doc, Newtonsoft.Json.Formatting.Indented).Dump();
    JsonConvert.DeserializeObject<Document>(json).Dump();
}

[JsonObjectAttribute("doc")]
public class Document
{
    [JsonProperty("items")]
    public List<Item> Items = new List<Item>();
}

public abstract class Item { }

public class ItemA : Item
{
    [JsonProperty("name")]
    public string Name { get; set; }
}

public class ItemB : Item
{
    [JsonProperty("value")]
    public int Value { get; set; }
}

This program fails with this exception on the call to DeserializeObject:

JsonSerializationException: Could not create an instance of type UserQuery+Item. Type is an interface or abstract class and cannot be instantiated. Path 'items[0].name', line 4, position 14.

I tried adding the TypeHandling property to the JsonProperty of Items:

[JsonProperty("items", TypeNameHandling = TypeNameHandling.All)]
public List<Item> Items = new List<Item>();

but this produces this Json:

{
  "items": {
    "$type": "System.Collections.Generic.List`1[[UserQuery+Item, LINQPadQuery]], mscorlib",
    "$values": [

which is not quite what I wanted, as it specifies the type for the list, but not for each item, however I can't seem to figure out if and how this can be done.

By this I want the following type of Json:

{
  "items": [
    {
      "$type": "UserQuery+ItemA, LINQPadQuery",
      "name": "Test Name"
    },
    {
      "$type": "UserQuery+ItemB, LINQPadQuery",
      "value": 42
    }
  ]
}

Do I need to introduce a wrapper object with a simple property of type Item with that attribute on instead?

Was it helpful?

Solution

You are close. Try using ItemTypeNameHandling instead of TypeNameHandling:

[JsonProperty("items", ItemTypeNameHandling = TypeNameHandling.All)]
public List<Item> Items = new List<Item>();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top