XML-сериализация общего списка сериализуемых объектов

можно ли сериализовать общий список сериализуемых объектов без указания их типа.

что-то вроде намерения за сломанным кодом ниже:

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

Edit:

для тех, кто хотел узнать подробности: когда я пытаюсь запустить этот код, это ошибки на XMLSerializer[...] строка с:

не удается сериализовать систему интерфейса.Во время выполнения.Сериализация.Интерфейс 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 ответов


у меня есть решение для общего списка с динамическими привязанными элементами.

класс 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);
    }
}

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

класс SpecialPerson наследует Person

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

класс SuperPerson наследует Person

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

важно определение и включает в себя различные типы.


посмотреть представляем сериализацию XML:

Элементы, Которые Могут Быть Сериализованы

следующие пункты могут быть сериализованы с помощью XmlSerializer класс:

  • публичные свойства чтения / записи и поля публичных классов
  • классы, реализующие ICollection или IEnumerable
  • XmlElement объекты
  • XmlNode объекты
  • DataSet объекты

в частности, ISerializable или [Serializable] атрибут не имеет значения.


теперь, когда вы сказали нам, в чем ваша проблема ("это не работает" - это не проблема), вы можете получить ответы на свою фактическую проблему, а не догадки.

когда вы сериализуете коллекцию типа, но на самом деле будет сериализовать коллекция экземпляров производных типов, вы должны позволить сериализатор знать, какие типы вы на самом деле будет сериализовать. Это также верно для коллекций object.

вам нужно использовать XmlSerializer (Тип,Тип[]) конструктор, чтобы дать список возможных типов.


нельзя сериализовать коллекцию объектов без указания ожидаемых типов. Вы должны передать список ожидаемых типов конструктору XmlSerializer (the


Я думаю, что лучше всего использовать методы с общими аргументами, например:

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

Я думаю, что подход Dreas в порядке. Альтернативой этому, однако, является наличие некоторых статических вспомогательных методов и реализация IXmlSerializable на каждом из ваших методов e.g метод расширения 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 напрямую, создайте сборки сериализации перед рукой, если это возможно, так как вы можете получить большой удар по производительности при создании новых XmlSerializers регулярно.

для коллекции нужно что-то вроде этого:

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

Ниже приведен класс 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);
            }
        }
    }
}

самый простой способ сделать это, что я нашел.. Применить


параметр knowTypeList позвольте сериализовать с 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);
            }
        }
    }

Если требование вывода 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);
}