[英]Serialize/Deserialize derived class as base class
For example I have the following classes: 例如,我有以下课程:
public abstract class Device
{
}
public class WindowsDevice: Device
{
}
public class AndroidDevice: Device
{
}
Now I want to serialize/deserialize WindowsDevice and AndroidDevice as XML: 现在,我想将WindowsDevice和AndroidDevice序列化/反序列化为XML:
public static string Serialize(object o, Type[] additionalTypes = null)
{
var serializer = new XmlSerializer(o.GetType(), additionalTypes);
using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter, o);
return stringWriter.ToString();
}
}
This will produce the following output: 这将产生以下输出:
<?xml version="1.0" encoding="utf-8"?>
<WindowsDevice xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</WindowsDevice>
But now I am unable to deserialize this, because in my application I don't know if the XML is WindowsDevice or AndroidDevice, so I have to deserialize as typeof(Device). 但是现在我无法反序列化,因为在我的应用程序中我不知道XML是WindowsDevice还是AndroidDevice,因此我必须反序列化为typeof(Device)。 But then I will get an exception that "WindowsDevice" was unexpected in the XML.
但是然后我会得到一个例外,即XML中的“ WindowsDevice”是意外的。
I tried XmlInclude and extraTypes without any success. 我尝试了XmlInclude和extraTypes,但没有成功。
What I dont understand is, that if I have the following sample class: 我不明白的是,如果我有以下示例类:
public class SampleClass
{
public List<Device> Devices {get;set}
}
and if I serialize SampleClass and use XmlInclude or extraTypes I exactly get what I want: 如果我序列化SampleClass并使用XmlInclude或extraTypes,我将得到我想要的东西:
<Devices>
<Device xsi:type="WindowsDevice"></Device>
</Devices>
But I don't have that class and I don't have a list of Devices. 但是我没有该课程,也没有设备列表。 I only want to serialize/deserialize WindowsDevice and AndroidDevice but on Deserialize I don't know whether it is AndroidDevice or WindowsDevice so I have to use typeof(Device) and want to get the correct sublass AndroidDevice or WindowsDevice, so instead of:
我只想序列化/反序列化WindowsDevice和AndroidDevice,但是在反序列化时,我不知道它是AndroidDevice还是WindowsDevice,因此我必须使用typeof(Device)并想要获取正确的子类AndroidDevice或WindowsDevice,所以而不是:
<WindowsDevice></WindowsDevice>
I want to have: 我希望有:
<Device xsi:type="WindowsDevice"></Device>
How can this be done? 如何才能做到这一点?
Your problem is that you are constructing your XmlSerializer
inconsistently during serialization and deserialization. 您的问题是在序列化和反序列化过程中不一致地构造
XmlSerializer
。 You need to construct it using the same Type
argument in both cases, specifically the base type typeof(Device)
. 在两种情况下,都需要使用相同的
Type
参数构造它,特别是基本类型typeof(Device)
。 Thus I'd suggest you replace your existing completely general serialization method with one specific for a Device
: 因此,我建议您使用特定于
Device
方法替换现有的完全通用的序列化方法:
public static class DeviceExtensions
{
public static string SerializeDevice<TDevice>(this TDevice o) where TDevice : Device
{
// Ensure that [XmlInclude(typeof(TDevice))] is present on Device.
// (Included for clarity -- actually XmlSerializer will make a similar check.)
if (!typeof(Device).GetCustomAttributes<XmlIncludeAttribute>().Any(a => a.Type == o.GetType()))
{
throw new InvalidOperationException("Unknown device type " + o.GetType());
}
var serializer = new XmlSerializer(typeof(Device)); // Serialize as the base class
using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter, o);
return stringWriter.ToString();
}
}
public static Device DeserializeDevice(this string xml)
{
var serial = new XmlSerializer(typeof(Device));
using (var reader = new StringReader(xml))
{
return (Device)serial.Deserialize(reader);
}
}
}
Then, apply [XmlInclude(typeof(TDevice))]
to Device
for all possible subtypes: 然后,将
[XmlInclude(typeof(TDevice))]
应用于所有可能的子类型的Device
:
[XmlInclude(typeof(WindowsDevice))]
[XmlInclude(typeof(AndroidDevice))]
public abstract class Device
{
}
Then both types of devices can now be serialized and deserialized successfully while retaining their type, because XmlSerializer
will include an "xsi:type"
attribute to explicitly indicate the type: 然后,这两种类型的设备现在都可以成功进行序列化和反序列化,同时保留它们的类型,因为
XmlSerializer
将包含一个"xsi:type"
属性来明确指示类型:
<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="WindowsDevice" />
Or 要么
<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="AndroidDevice" />
Updates 更新
So the issue was, that I serialized with typeof(WindowsDevice) instead of typeof(Device)? 所以问题是,我使用typeof(WindowsDevice)而不是typeof(Device)进行了序列化?
Yes. 是。
Any ideas for a solution which will work, if I have to use typeof(WindowsDevice)? 如果必须使用typeof(WindowsDevice),对可以解决问题的解决方案有什么想法吗? Cause I have hundreds of classes and don't want to use hundreds of different XmlSerializer initializations...
因为我有数百个类,并且不想使用数百个不同的XmlSerializer初始化...
This is more of an architectural question than a howto question. 这不仅仅是一个架构问题,而不是一个howto问题。 One possibility would be to introduce a custom attribute that you can apply to a class to indicate that any subtypes of that class should always be serialized as the attributed base type.
一种可能是引入自定义属性 ,您可以将其应用于类,以指示该类的任何子类型都应始终序列化为属性基类型。 All appropriate
[XmlInclude(typeof(TDerivedType))]
attributes will also be required: 还需要所有适当的
[XmlInclude(typeof(TDerivedType))]
属性:
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class XmlBaseTypeAttribute : System.Attribute
{
}
[XmlInclude(typeof(WindowsDevice))]
[XmlInclude(typeof(AndroidDevice))]
[XmlBaseType]
public abstract class Device
{
}
Then modify your universal XML serialization code to look up the type hierarchy of the object being serialized for an [XmlBaseType]
attribute, and (de)serialize as that type: 然后修改您的通用XML序列化代码,以为
[XmlBaseType]
属性[XmlBaseType]
要序列化的对象的类型层次结构,并(反)序列化为该类型:
public static class XmlExtensions
{
static Type GetSerializedType(this Type type)
{
var serializedType = type.BaseTypesAndSelf().Where(t => Attribute.IsDefined(t, typeof(XmlBaseTypeAttribute))).SingleOrDefault();
if (serializedType != null)
{
// Ensure that [XmlInclude(typeof(TDerived))] is present on the base type
// (Included for clarity -- actually XmlSerializer will make a similar check.)
if (!serializedType.GetCustomAttributes<XmlIncludeAttribute>().Any(a => a.Type == type))
{
throw new InvalidOperationException(string.Format("Unknown subtype {0} of type {1}", type, serializedType));
}
}
return serializedType ?? type;
}
public static string Serialize(this object o)
{
var serializer = new XmlSerializer(o.GetType().GetSerializedType());
using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter, o);
return stringWriter.ToString();
}
}
public static T Deserialize<T>(this string xml)
{
var serial = new XmlSerializer(typeof(T).GetSerializedType());
using (var reader = new StringReader(xml))
{
return (T)serial.Deserialize(reader);
}
}
}
Of course this means that if your code tries to deserialize XML it expects to contain a WindowsDevice
, it might actually get back an AndroidDevice
depending upon the contents of the XML. 当然,这意味着如果您的代码尝试反序列化XML,则它期望包含
WindowsDevice
,则实际上可能会取回AndroidDevice
具体取决于XML的内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.