How to serialize-deserialize any of 3 object types, one of which contains an abstract class?

折月煮酒 提交于 2019-12-13 03:39:43

问题


I am sending 3 .net objects over the network:

 - List<int>
 - List<ParentObject>
 - string

This is how I'm serializing (same for all types):

JsonSerializerSettings JSsettings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Arrays
        };

        string message = JsonConvert.SerializeObject(listOfParents, JSsettings);
        //listOfParents is  of type List<ParentObject>

The ParentObject is an abstract class and has two child classes. It has a property to get which child type it represents.

public enum EntityType {Child1, Child2};

class ParentObject
{
  public EntityType et { get; set; }
  //..other members
}

I want to call 3 different functions based on which of the 3 objects is received.

Object genericObject = JsonConvert.DeserializeObject(message, new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto
            });

        if (genericObject is List<int>)
        {
          List<int> myList= (List<int>)genericObject;
          myfunction1(myList);
        }
        if (genericObject is List<ParentObject>)
        {
         //etc..

The ParentObject is causing problems at DeserializeObject() because it says "Could not create an instance of type ParentObject. Type is an interface or abstract class and cannot be instantiated". So I was thinking I might need to use CustomCreationConverter at http://james.newtonking.com/projects/json/help/index.html?topic=html/CustomCreationConverter.htm

That still wouldn't solve my problem since the CustomCreationConverter needs the type during deserialization whereas I don't check the type until after deserialization.

Any suggestions to solve the problem?


回答1:


If I use objects defined like this:

public enum EntityType { Child1, Child2 };

abstract class ParentObject
{
    public EntityType et { get; set; }
}

class ChildClass : ParentObject
{
    public int ChildClassProp { get; set; }

    public ChildClass()
    {
        this.et = EntityType.Child1;
    }
}

class ChildClass2 : ParentObject
{
    public int ChildClass2Prop { get; set; }

    public ChildClass2()
    {
        this.et = EntityType.Child2;
    }
}

Then I can happily deserialize derived classes(ChildClass and ChildClass2) to ParentObject like this:

JsonSerializerSettings JSsettings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects
};

List<ParentObject> list = new List<ParentObject>();
list.Add(new ChildClass() { ChildClassProp = 1 });
list.Add(new ChildClass2() { ChildClass2Prop = 2 });

string message = JsonConvert.SerializeObject(list,
      Newtonsoft.Json.Formatting.Indented, JSsettings);

list = JsonConvert.DeserializeObject<List<ParentObject>>(message, JSsettings);

Where message looks like this:

[
  {
    "$type": "ConsoleApplication4.ChildClass, ConsoleApplication4",
    "ChildClassProp": 1,
    "et": 0
  },
  {
    "$type": "ConsoleApplication4.ChildClass2, ConsoleApplication4",
    "ChildClass2Prop": 2,
    "et": 1
  }
]   

The key in this one was using TypeNameHandling = TypeNameHandling.Auto for both serialization and deserialization. Using TypeNameHandling.Arrays creates a message that looks like this:

{
  "$type": "System.Collections.Generic.List`1[[ConsoleApplication4.ParentObject, ConsoleApplication4]], mscorlib",
  "$values": [
    {
      "ChildClassProp": 1,
      "et": 0
    },
    {
      "ChildClass2Prop": 2,
      "et": 1
    }
  ]
}

Note that the types of the list items are not included, only the type of the list, hence the error you were getting.

Edit:

I think the easiest way to get this working the way you want is to define a simple class like this one that acts as a thin wrapper around the object you are serializing:

class ObjectContainer
{
    public object Data { get; set; }
}

Then the code would look like this (note the change to TypeNameHandling.Auto):

JsonSerializerSettings JSsettings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto
};

List<ParentObject> list = new List<ParentObject>();
list.Add(new ChildClass() { ChildClassProp = 1 });
list.Add(new ChildClass2() { ChildClass2Prop = 2 });

ObjectContainer container = new ObjectContainer()
{
    Data = list
};

string message = JsonConvert.SerializeObject(container,
      Newtonsoft.Json.Formatting.Indented, JSsettings);

var objectContainer = JsonConvert.DeserializeObject<ObjectContainer>(message, JSsettings);

if (objectContainer.Data is List<int>)
{
    Console.Write("objectContainer.Data is List<int>");
}
else if (objectContainer.Data is List<ParentObject>)
{
    Console.Write("objectContainer.Data is List<ParentObject>");
}
else if (objectContainer.Data is string)
{
    Console.Write("objectContainer.Data is string");
}

The reason I have gone for this approach is that Json.Net will take care of almost all the work. Simply calling the non-generic JsonConvert.DeserializeObject method is fine, but you would then need to do additional work as this method returns a JContainer, not an object.



来源:https://stackoverflow.com/questions/15480780/how-to-serialize-deserialize-any-of-3-object-types-one-of-which-contains-an-abs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!