C# 将派生类序列化/反序列化为基类
例如,我有以下课程:C# 将派生类序列化/反序列化为基类,c#,xml,serialization,C#,Xml,Serialization,例如,我有以下课程: public abstract class Device { } public class WindowsDevice: Device { } public class AndroidDevice: Device { } 现在我想将WindowsDevice和AndroidDevice序列化/反序列化为XML: public static string Serialize(object o, Type[] additionalTypes = null) {
public abstract class Device
{
}
public class WindowsDevice: Device
{
}
public class AndroidDevice: Device
{
}
现在我想将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();
}
}
这将产生以下输出:
<?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>
但是现在我无法反序列化它,因为在我的应用程序中,我不知道XML是WindowsDevice还是AndroidDevice,所以我必须反序列化为typeof(设备)。但是我会得到一个异常,XML中的“WindowsDevice”是意外的
我尝试了XmlInclude和extraTypes,但没有成功
我不明白的是,如果我有以下示例类:
public class SampleClass
{
public List<Device> Devices {get;set}
}
公共类SampleClass
{
公共列表设备{get;set}
}
如果我序列化SampleClass并使用XmlInclude或extraTypes,我会得到我想要的:
<Devices>
<Device xsi:type="WindowsDevice"></Device>
</Devices>
但我没有这个类,也没有设备列表。我只想序列化/反序列化WindowsDevice和AndroidDevice,但在反序列化时,我不知道它是AndroidDevice还是WindowsDevice,因此我必须使用typeof(Device)并希望获得正确的子类AndroidDevice或WindowsDevice,因此,不要:
<WindowsDevice></WindowsDevice>
我想要:
<Device xsi:type="WindowsDevice"></Device>
如何做到这一点?您的问题是,在序列化和反序列化过程中,您构建的
XmlSerializer
不一致。在这两种情况下都需要使用相同的参数来构造它,特别是基本类型typeof(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);
}
}
}
然后,这两种类型的设备现在都可以成功序列化和反序列化,同时保留其类型,因为XmlSerializer
将包含一个属性来显式指示类型:
<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="WindowsDevice" />
然后修改您的通用XML序列化代码,以查找正在序列化的对象的类型层次结构中的[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);
}
}
}
公共静态类XmlExtensions
{
静态类型GetSerializedType(此类型)
{
var serializedType=type.BaseTypesAndSelf()。其中(t=>Attribute.IsDefined(t,typeof(xmlbasetypetattribute))).SingleOrDefault();
if(serializedType!=null)
{
//确保基类型上存在[xmlclude(typeof(TDerived))]
//(为了清楚起见,包含了XmlSerializer——实际上XmlSerializer将进行类似的检查。)
如果(!serializedType.GetCustomAttributes().Any(a=>a.Type==Type))
{
抛出新的InvalidOperationException(string.Format(“类型{1}的未知子类型{0}”,类型,serializedType));
}
}
返回serializedType??类型;
}
公共静态字符串序列化(此对象为o)
{
var serializer=新的XmlSerializer(o.GetType().GetSerializedType());
使用(var stringWriter=newstringwriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter,o);
返回stringWriter.ToString();
}
}
公共静态T反序列化(此字符串为xml)
{
var serial=新的XmlSerializer(typeof(T).GetSerializedType());
使用(var reader=newstringreader(xml))
{
返回(T)串行。反序列化(读取器);
}
}
}
当然,这意味着如果您的代码试图反序列化XML,它希望包含一个WindowsDevice
,它实际上可能会返回一个AndroidDevice
,具体取决于XML的内容
示例。开头标签没有告诉您需要了解的内容吗?告诉你物体是什么。你能不能先读一下标签,然后知道它是什么类型的?我有很多这样的类和一个通用的序列化/反序列化方法。我不想使用这种“肮脏”的变通方法。考虑重命名一个类,添加新类,等等。如果可能的话,我更喜欢干净的解决方案。如上所述,如果我使用列表,XmlSerializer可以实现我想要的功能,因此我想知道如何在类的单个实例上使用该机制。我尝试了XmlInclude和extraTypes,但没有成功。-你试了什么?我认为它应该可以工作。@dbc:[xmlclude(typeof(WindowsDevice)]公共抽象类Device{}和:new XmlSerializer(o.GetType(),new Type[]{typeof(WindowsDevice})谢谢!所以问题是,我用typeof(WindowsDevice)而不是typeof(Device)序列化了。如果我必须使用typeof(WindowsDevice),有没有可行的解决方案?因为我有数百个类,不想使用数百个不同的XmlSerializer初始化…在运行时,我有一个应传递给XmlSerializer的WindowsDevice Instance。我也不能总是使用传递类型的BaseType,因为有些类应序列化为派生类(因为我所有的数百个类都是其他类的子类)还有一些作为它们的基类。
<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="AndroidDevice" />
[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
{
}
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);
}
}
}