XmlSerializer序列化通用接口列表

Ste*_*eve 12 c# interface list xml-serialization xmlserializer

我正在尝试使用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();    
    }
Run Code Online (Sandbox Code Playgroud)

Ale*_*ven 13

您也可以使用XmlSerializer,但是您需要包含可能出现在您序列化的对象图中的所有可能类型,这会限制可扩展性并降低可维护性.你可以通过使用XmlSerializer的构造函数的重载来完成它:

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

此外,使用XmlSerializer时还有几个值得注意的问题,这里列出所有这些(MSDN) - 例如,在"动态生成的程序集"标题下查看.

  • @Steve,我建议创建一个名为Animal的抽象类来实现IAnimal.然后,您将能够序列化列表(假设您修改了XmlSerializer构造函数,并且Cat和Dog继承了抽象类而不是IAnimal.var animals = new List <IAnimals>();不需要更改. (6认同)

Ste*_*ner 7

XmlSerializer,因为它不知道要创建哪些类型deserialising时无法处理的接口.要解决这个问题,您需要通过实现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;} }
}
Run Code Online (Sandbox Code Playgroud)

我想把反序列化作为练习给读者,但如果没有它,代码就会非常有用.


Ali*_*tad 3

你必须使用吗XmlSerializer?这是 的一个已知问题XmlSerializer

您可以使用 BinaryFormatter 保存到流:

BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, animals);
Run Code Online (Sandbox Code Playgroud)

其他替代方法是使用 WCFDataContractSerializer并使用 KnownType 属性提供类型。