如何序列化 - 反序列化3种对象类型中的任何一种,其中一种包含抽象类?

时间:2013-03-18 15:37:39

标签: c# .net json.net

我通过网络发送3个.net对象:

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

这就是我序列化的方式(所有类型都相同):

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

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

ParentObject是一个抽象类,有两个子类。它有一个属性来获取它所代表的子类型。

public enum EntityType {Child1, Child2};

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

我想根据收到的3个对象中的哪一个调用3个不同的函数。

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..

ParentObject在DeserializeObject()中导致问题,因为它说“无法创建类型为ParentObject的实例.Type是一个接口或抽象类,无法实例化”。所以我想我可能需要在http://james.newtonking.com/projects/json/help/index.html?topic=html/CustomCreationConverter.htm

使用CustomCreationConverter

由于CustomCreationConverter在反序列化期间需要类型,但我不会检查类型,直到 反序列化后才检查类型。

有任何解决问题的建议吗?

1 个答案:

答案 0 :(得分:0)

如果我使用这样定义的对象:

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;
    }
}

然后我可以高兴地将派生类(ChildClassChildClass2)反序列化为ParentObject,如下所示:

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);

message看起来像这样:

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

本文中的关键是使用TypeNameHandling = TypeNameHandling.Auto进行序列化和反序列化。使用TypeNameHandling.Arrays创建一条如下所示的消息:

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

请注意,列表项的类型不包括在内,只包括列表的类型,因此会出现错误。

修改

我认为以这种方式工作的最简单方法是定义一个像这样的简单类,它作为你正在序列化的对象的一个​​薄包装器:

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

然后代码看起来像这样(注意对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");
}

我采用这种方法的原因是Json.Net将负责几乎所有的工作。只需调用非泛型JsonConvert.DeserializeObject方法即可,但您需要执行其他工作,因为此方法返回JContainer,而不是object