问题
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