簡體   English   中英

序列化/反序列化派生類為基類

[英]Serialize/Deserialize derived class as base class

例如,我有以下課程:

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(Device)。 但是然后我會得到一個例外,即XML中的“ WindowsDevice”是意外的。

我嘗試了XmlInclude和extraTypes,但沒有成功。

我不明白的是,如果我有以下示例類:

public class SampleClass
{
    public List<Device> Devices {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 在兩種情況下,都需要使用相同的Type參數構造它,特別是基本類型typeof(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);
        }
    }
}

然后,將[XmlInclude(typeof(TDevice))]應用於所有可能的子類型的Device

[XmlInclude(typeof(WindowsDevice))]
[XmlInclude(typeof(AndroidDevice))]
public abstract class Device
{
}

然后,這兩種類型的設備現在都可以成功進行序列化和反序列化,同時保留它們的類型,因為XmlSerializer將包含一個"xsi:type"屬性來明確指示類型:

<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:type="WindowsDevice" />

要么

<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:type="AndroidDevice" />

樣品提琴

更新

所以問題是,我使用typeof(WindowsDevice)而不是typeof(Device)進行了序列化?

是。

如果必須使用typeof(WindowsDevice),對可以解決問題的解決方案有什么想法嗎? 因為我有數百個類,並且不想使用數百個不同的XmlSerializer初始化...

這不僅僅是一個架構問題,而不是一個howto問題。 一種可能是引入自定義屬性 ,您可以將其應用於類,以指示該類的任何子類型都應始終序列化為屬性基類型。 還需要所有適當的[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
{
}

然后修改您的通用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);
        }
    }
}

當然,這意味着如果您的代碼嘗試反序列化XML,則它期望包含WindowsDevice ,則實際上可能會取回AndroidDevice具體取決於XML的內容。

樣本小提琴2

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM