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