代码之家  ›  专栏  ›  技术社区  ›  Simon D

XML序列化可序列化对象的通用列表

  •  68
  • Simon D  · 技术社区  · 15 年前

    我可以序列化可序列化对象的通用列表而不必指定其类型。

    类似于下面破坏代码背后的意图:

    List<ISerializable> serializableList = new List<ISerializable>();
    
    XmlSerializer xmlSerializer = new XmlSerializer(serializableList.GetType());
    
    serializableList.Add((ISerializable)PersonList);
    
    using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
    {
        xmlSerializer.Serialize(streamWriter, serializableList);
    }
    

    编辑:

    对于那些想了解详细信息的人:当我尝试运行此代码时,xmlserializer[…]行上的错误为:

    无法序列化接口System.Runtime.Serialization.ISerializable。

    如果我改变 List<object> 我得到 "There was an error generating the XML document." . InnerException详细信息是 "{"The type System.Collections.Generic.List1[[Project1.Person, ConsoleFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] may not be used in this context."}"

    Person对象定义如下:

    [XmlRoot("Person")]
    public class Person
    {
        string _firstName = String.Empty;
        string _lastName = String.Empty;
    
        private Person()
        {
        }
    
        public Person(string lastName, string firstName)
        {
            _lastName = lastName;
            _firstName = firstName;
        }
    
        [XmlAttribute(DataType = "string", AttributeName = "LastName")]
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }
    
        [XmlAttribute(DataType = "string", AttributeName = "FirstName")]
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }
    }
    

    这个人只是一个 List<Person> .

    这只是为了测试,所以没有觉得细节太重要。关键是我有一个或多个不同的对象,所有这些对象都是可序列化的。我想将它们序列化为一个文件。我认为最简单的方法是将它们放在一个通用列表中并一次性序列化该列表。但这不管用。

    我尝试过 List<IXmlSerializable> 也一样,但失败的是

    System.Xml.Serialization.IXmlSerializable cannot be serialized because it does not have a parameterless constructor.

    很抱歉没有细节,但我是一个初学者,不知道需要什么细节。如果要求更多细节的人试图以一种让我了解需要哪些细节的方式作出回应,或者一个概述可能方向的基本答案,那将是很有帮助的。

    阿尔索 谢谢 到目前为止,我得到了两个答案——如果没有这些想法,我本可以花更多的时间阅读。令人惊讶的是,这个网站上的人是多么的有用。

    9 回复  |  直到 6 年前
        1
  •  70
  •   slavoo user3099232    9 年前

    我有一个关于动态绑定项的通用列表的解决方案。

    类PersonalList它是根元素

    [XmlRoot("PersonenListe")]
    [XmlInclude(typeof(Person))] // include type class Person
    public class PersonalList
    {
        [XmlArray("PersonenArray")]
        [XmlArrayItem("PersonObjekt")]
        public List<Person> Persons = new List<Person>();
    
        [XmlElement("Listname")]
        public string Listname { get; set; }
    
        // Konstruktoren 
        public PersonalList() { }
    
        public PersonalList(string name)
        {
            this.Listname = name;
        }
    
        public void AddPerson(Person person)
        {
            Persons.Add(person);
        }
    }
    

    类人它是一个单一的列表元素

    [XmlType("Person")] // define Type
    [XmlInclude(typeof(SpecialPerson)), XmlInclude(typeof(SuperPerson))]  
            // include type class SpecialPerson and class SuperPerson
    public class Person
    {
        [XmlAttribute("PersID", DataType = "string")]
        public string ID { get; set; }
    
        [XmlElement("Name")]
        public string Name { get; set; }
    
        [XmlElement("City")]
        public string City { get; set; }
    
        [XmlElement("Age")]
        public int Age { get; set; }
    
        // Konstruktoren 
        public Person() { }
    
        public Person(string name, string city, int age, string id)
        {
            this.Name = name;
            this.City = city;
            this.Age = age;
            this.ID = id;
        }
    }
    

    类特殊人继承人

    [XmlType("SpecialPerson")] // define Type
    public class SpecialPerson : Person
    {
        [XmlElement("SpecialInterests")]
        public string Interests { get; set; }
    
        public SpecialPerson() { }
    
        public SpecialPerson(string name, string city, int age, string id, string interests)
        {
            this.Name = name;
            this.City = city;
            this.Age = age;
            this.ID = id;
            this.Interests = interests;
        }
    }
    

    类超人继承人

    [XmlType("SuperPerson")] // define Type
    public class SuperPerson : Person
    {
        [XmlArray("Skills")]
        [XmlArrayItem("Skill")]
        public List<String> Skills { get; set; }
    
        [XmlElement("Alias")]
        public string Alias { get; set; }
    
        public SuperPerson() 
        {
            Skills = new List<String>();
        }
    
        public SuperPerson(string name, string city, int age, string id, string[] skills, string alias)
        {
            Skills = new List<String>();
    
            this.Name = name;
            this.City = city;
            this.Age = age;
            this.ID = id;
            foreach (string item in skills)
            {
                this.Skills.Add(item);   
            }
            this.Alias = alias;
        }
    }
    

    以及主要的测试源

    static void Main(string[] args)
    {
        PersonalList personen = new PersonalList(); 
        personen.Listname = "Friends";
    
        // normal person
        Person normPerson = new Person();
        normPerson.ID = "0";
        normPerson.Name = "Max Man";
        normPerson.City = "Capitol City";
        normPerson.Age = 33;
    
        // special person
        SpecialPerson specPerson = new SpecialPerson();
        specPerson.ID = "1";
        specPerson.Name = "Albert Einstein";
        specPerson.City = "Ulm";
        specPerson.Age = 36;
        specPerson.Interests = "Physics";
    
        // super person
        SuperPerson supPerson = new SuperPerson();
        supPerson.ID = "2";
        supPerson.Name = "Superman";
        supPerson.Alias = "Clark Kent";
        supPerson.City = "Metropolis";
        supPerson.Age = int.MaxValue;
        supPerson.Skills.Add("fly");
        supPerson.Skills.Add("strong");
    
        // Add Persons
        personen.AddPerson(normPerson);
        personen.AddPerson(specPerson);
        personen.AddPerson(supPerson);
    
        // Serialize 
        Type[] personTypes = { typeof(Person), typeof(SpecialPerson), typeof(SuperPerson) };
        XmlSerializer serializer = new XmlSerializer(typeof(PersonalList), personTypes); 
        FileStream fs = new FileStream("Personenliste.xml", FileMode.Create); 
        serializer.Serialize(fs, personen); 
        fs.Close(); 
        personen = null;
    
        // Deserialize 
        fs = new FileStream("Personenliste.xml", FileMode.Open); 
        personen = (PersonalList)serializer.Deserialize(fs); 
        serializer.Serialize(Console.Out, personen);
        Console.ReadLine();
    }
    

    重要的是不同类型的定义和包含。

        2
  •  21
  •   John Saunders Tony    9 年前

    Introducing XML Serialization :

    可以序列化的项

    可以使用 XML串行化器 班级:

    • 公共类的公共读/写属性和字段
    • 实现的类 ICollection IEnumerable
    • XmlElement 物体
    • XmlNode 物体
    • DataSet 物体

    特别地, ISerializable [Serializable] 属性无关紧要。


    既然你已经告诉我们你的问题是什么(“它不起作用”不是一个问题陈述),你就可以得到实际问题的答案,而不是猜测。

    当您序列化一个类型的集合,但实际上将序列化派生类型的实例集合时,您需要让序列化程序知道您将实际序列化哪些类型。这也适用于 object .

    你需要使用 XmlSerializer(Type,Type[]) 构造函数给出可能类型的列表。

        3
  •  5
  •   Tareq    6 年前

    如果不指定预期的类型,则无法序列化对象集合。必须将所需类型的列表传递给的构造函数 XmlSerializer (the extraTypes 参数):

    List<object> list = new List<object>();
    list.Add(new Foo());
    list.Add(new Bar());
    
    XmlSerializer xs = new XmlSerializer(typeof(object), new Type[] {typeof(Foo), typeof(Bar)});
    using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
    {
        xs.Serialize(streamWriter, list);
    }
    

    如果列表中的所有对象都继承自同一类,则还可以使用 XmlInclude 用于指定预期类型的属性:

    [XmlInclude(typeof(Foo)), XmlInclude(typeof(Bar))]
    public class MyBaseClass
    {
    }
    
        4
  •  4
  •   Andreas Grech    15 年前

    我认为最好将方法与泛型参数一起使用,如下所示:

    public static void SerializeToXml<T>(T obj, string fileName)
    {
        using (var fileStream = new FileStream(fileName, FileMode.Create))
        { 
            var ser = new XmlSerializer(typeof(T)); 
            ser.Serialize(fileStream, obj);
        }
    }
    
    public static T DeserializeFromXml<T>(string xml)
    {
        T result;
        var ser = new XmlSerializer(typeof(T));
        using (var tr = new StringReader(xml))
        {
            result = (T)ser.Deserialize(tr);
        }
        return result;
    }
    
        5
  •  3
  •   Ian    15 年前

    我认为Dreas的方法是可以的。但是,另一种方法是拥有一些静态助手方法,并在每个方法上实现IXMLSerializable,例如XMLwriter扩展方法和XMLreader方法。

    public static void SaveXmlSerialiableElement<T>(this XmlWriter writer, String elementName, T element) where T : IXmlSerializable
    {
       writer.WriteStartElement(elementName);
       writer.WriteAttributeString("TYPE", element.GetType().AssemblyQualifiedName);
       element.WriteXml(writer);
       writer.WriteEndElement();
    }
    
    public static T ReadXmlSerializableElement<T>(this XmlReader reader, String elementName) where T : IXmlSerializable
    {
       reader.ReadToElement(elementName);
    
       Type elementType = Type.GetType(reader.GetAttribute("TYPE"));
       T element = (T)Activator.CreateInstance(elementType);
       element.ReadXml(reader);
       return element;
    }
    

    如果确实要直接使用XmlSerializer类,请在手之前创建序列化程序集(如果可能),因为在定期构造新的XmlSerializer时,可能会造成很大的性能损失。

    对于收藏,您需要这样的东西:

    public static void SaveXmlSerialiazbleCollection<T>(this XmlWriter writer, String collectionName, String elementName, IEnumerable<T> items) where T : IXmlSerializable
    {
       writer.WriteStartElement(collectionName);
       foreach (T item in items)
       {
          writer.WriteStartElement(elementName);
          writer.WriteAttributeString("TYPE", item.GetType().AssemblyQualifiedName);
          item.WriteXml(writer);
          writer.WriteEndElement();
       }
       writer.WriteEndElement();
    }
    
        6
  •  2
  •   SwDevMan81    13 年前

    下面是我的项目中的一个Util类:

    namespace Utils
    {
        public static class SerializeUtil
        {
            public static void SerializeToFormatter<F>(object obj, string path) where F : IFormatter, new()
            {
                if (obj == null)
                {
                    throw new NullReferenceException("obj Cannot be Null.");
                }
    
                if (obj.GetType().IsSerializable == false)
                {
                    //  throw new 
                }
                IFormatter f = new F();
                SerializeToFormatter(obj, path, f);
            }
    
            public static T DeserializeFromFormatter<T, F>(string path) where F : IFormatter, new()
            {
                T t;
                IFormatter f = new F();
                using (FileStream fs = File.OpenRead(path))
                {
                    t = (T)f.Deserialize(fs);
                }
                return t;
            }
    
            public static void SerializeToXML<T>(string path, object obj)
            {
                XmlSerializer xs = new XmlSerializer(typeof(T));
                using (FileStream fs = File.Create(path))
                {
                    xs.Serialize(fs, obj);
                }
            }
    
            public static T DeserializeFromXML<T>(string path)
            {
                XmlSerializer xs = new XmlSerializer(typeof(T));
                using (FileStream fs = File.OpenRead(path))
                {
                    return (T)xs.Deserialize(fs);
                }
            }
    
            public static T DeserializeFromXml<T>(string xml)
            {
                T result;
    
                var ser = new XmlSerializer(typeof(T));
                using (var tr = new StringReader(xml))
                {
                    result = (T)ser.Deserialize(tr);
                }
                return result;
            }
    
    
            private static void SerializeToFormatter(object obj, string path, IFormatter formatter)
            {
                using (FileStream fs = File.Create(path))
                {
                    formatter.Serialize(fs, obj);
                }
            }
        }
    }
    
        7
  •  2
  •   Nate Barbettini    9 年前

    这是我发现的最简单的方法。应用 System.Xml.Serialization.XmlArray 归因于它。

    [System.Xml.Serialization.XmlArray] //This is the part that makes it work
    List<object> serializableList = new List<object>();
    
    XmlSerializer xmlSerializer = new XmlSerializer(serializableList.GetType());
    
    serializableList.Add(PersonList);
    
    using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
    {
        xmlSerializer.Serialize(streamWriter, serializableList);
    }
    

    序列化程序将以数组的形式接收它,并将列表的项作为子节点序列化。

        8
  •  0
  •   Sharunas Bielskis    9 年前

    knowTypeList parameter 让我们使用DataContractSerializer序列化几个已知类型:

    private static void WriteObject(
            string fileName, IEnumerable<Vehichle> reflectedInstances, List<Type> knownTypeList)
        {
            using (FileStream writer = new FileStream(fileName, FileMode.Append))
            {
                foreach (var item in reflectedInstances)
                {
                    var serializer = new DataContractSerializer(typeof(Vehichle), knownTypeList);
                    serializer.WriteObject(writer, item);
                }
            }
        }
    
        9
  •  0
  •   slavoo user3099232    9 年前

    如果可以更改XML输出要求,则可以始终使用二进制序列化—这更适合于处理异类对象列表。下面是一个例子:

    private void SerializeList(List<Object> Targets, string TargetPath)
    {
        IFormatter Formatter = new BinaryFormatter();
    
        using (FileStream OutputStream = System.IO.File.Create(TargetPath))
        {
            try
            {
                Formatter.Serialize(OutputStream, Targets);
            } catch (SerializationException ex) {
                //(Likely Failed to Mark Type as Serializable)
                //...
            }
    }
    

    使用如下:

    [Serializable]
    public class Animal
    {
        public string Home { get; set; }
    }
    
    [Serializable]
    public class Person
    {
        public string Name { get; set; }
    }
    
    
    public void ExampleUsage() {
    
        List<Object> SerializeMeBaby = new List<Object> {
            new Animal { Home = "London, UK" },
            new Person { Name = "Skittles" }
        };
    
        string TargetPath = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "Test1.dat");
    
        SerializeList(SerializeMeBaby, TargetPath);
    }