簡體   English   中英

XmlSerializer序列化通用接口列表

[英]XmlSerializer serialize generic List of interface

我正在嘗試使用XmlSerializer保留List(T),其中T是接口。 串行器不喜歡接口。 我很好奇,是否有一種簡單的方法可以使用XmlSerializer輕松地序列化異構對象列表。 這就是我要去的地方:

    public interface IAnimal
    {
        int Age();
    }
    public class Dog : IAnimal
    {
        public int Age()
        {
            return 1;
        }
    }
    public class Cat : IAnimal
    {
        public int Age()
        {
            return 1;
        }
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var animals = new List<IAnimal>
        {
            new Dog(),
            new Cat()
        };

        var x = new XmlSerializer(animals.GetType());
        var b = new StringBuilder();
        var w = XmlTextWriter.Create(b, new XmlWriterSettings { NewLineChars = "\r\n", Indent = true });
        //FAIL - cannot serialize interface. Does easy way to do this exist?
        x.Serialize(w, animals);
        var s = b.ToString();    
    }

您也可以使用XmlSerializer,但是您需要包括所有可能出現在要序列化的對象圖中的類型,這限制了可擴展性並降低了可維護性。 您可以通過使用XmlSerializer的構造函數的重載來實現:

var x = new XmlSerializer(animals.GetType(), new Type[] { typeof(Cat), typeof(Dog) });

另外,使用XmlSerializer時,有幾個注意事項, 此處概述所有這些(MSDN) ,例如,在“動態生成的程序集”標題下查找。

XmlSerializer無法處理接口,因為它在反序列化時不知道要創建哪種類型。 為了解決這個問題,您需要通過實現IXmlSerializable接口自己處理序列化的這一部分。 這使您可以記錄類型,以便您可以重新創建(反序列化)它。

下面的ListOfIAnimal類顯示了我如何繼承和擴展通用列表List<IAnimal>來實現所需的接口。 我壓縮了您的舊類,並為每個類添加了一個額外的非接口字段,以便可以看到具體的類已正確地序列化和反序列化了。

與您的代碼相比,我只是使用新類型ListOfIAnimal代替List<IAnimal> ,其他更改只是一些重構。

它的完整代碼,只需將其復制到自己的.cs文件中,然后調用第一個函數即可逐步執行。

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Serialiser
{
    static class SerialiseInterface
    {
        public static void SerialiseAnimals()
        {
            String finalXml;

            // Serialize
            {
                var animals = new ListOfIAnimal{
                    new Dog() { Age = 5, Teeth = 30 },
                    new Cat() { Age = 6, Paws = 4 }
                };

                var xmlSerializer = new XmlSerializer(animals.GetType());
                var stringBuilder = new StringBuilder();
                var xmlTextWriter = XmlTextWriter.Create(stringBuilder, new XmlWriterSettings { NewLineChars = "\r\n", Indent = true });
                xmlSerializer.Serialize(xmlTextWriter, animals);
                finalXml = stringBuilder.ToString();
            }

            // Deserialise
            {
                var xmlSerializer = new XmlSerializer(typeof(ListOfIAnimal));
                var xmlReader = XmlReader.Create(new StringReader(finalXml));
                ListOfIAnimal animals = (ListOfIAnimal)xmlSerializer.Deserialize(xmlReader);
            }
        }
    }

    public class ListOfIAnimal : List<IAnimal>, IXmlSerializable
    {
        public ListOfIAnimal() : base() { }

        #region IXmlSerializable
        public System.Xml.Schema.XmlSchema GetSchema() { return null; }

        public void ReadXml(XmlReader reader)
        {
            reader.ReadStartElement("ListOfIAnimal");
            while (reader.IsStartElement("IAnimal"))
            {
                Type type = Type.GetType(reader.GetAttribute("AssemblyQualifiedName"));
                XmlSerializer serial = new XmlSerializer(type);

                reader.ReadStartElement("IAnimal");
                this.Add((IAnimal)serial.Deserialize(reader));
                reader.ReadEndElement(); //IAnimal
            }
            reader.ReadEndElement(); //ListOfIAnimal
        }

        public void WriteXml(XmlWriter writer)
        {
            foreach (IAnimal animal in this)
            {
                writer.WriteStartElement("IAnimal");
                writer.WriteAttributeString("AssemblyQualifiedName", animal.GetType().AssemblyQualifiedName);
                XmlSerializer xmlSerializer = new XmlSerializer(animal.GetType());
                xmlSerializer.Serialize(writer, animal);
                writer.WriteEndElement();
            }
        }
        #endregion
    }

    public interface IAnimal { int Age { get; set; } }
    public class Dog : IAnimal { public int Age { get; set;} public int Teeth { get; set;} }
    public class Cat : IAnimal { public int Age { get; set;} public int Paws { get; set;} }
}

我考慮過將反序列化作為練習留給讀者,但沒有它,代碼將不會非常有用。

您必須使用XmlSerializer嗎? 這是XmlSerializer的已知問題。

您可以使用BinaryFormatter保存到流:

BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, animals);

另一種選擇是使用WCF的DataContractSerializer並使用KnownType屬性提供類型。

您可以使用ExtendedXmlSerializer

var serializer = new ExtendedXmlSerializer();
var xml = serializer.Serialize(animals);

您的xml如下所示:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfIAnimal>
    <Dog type="Model.Dog" />
    <Cat type="Model.Cat" />
</ArrayOfIAnimal>

一種簡單的方法是將[Serializable()]裝飾添加到您的類中,然后將IList更改為List,然后查看是否可行。

如果您使用界面,請參閱webturner的答案。

暫無
暫無

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

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