78 votes

XML Sérialiser une liste générique d'objets sérialisables

Puis-je sérialiser une liste générique des objets sérialisables sans avoir à préciser leur type.

Quelque chose comme de l'intention à l'origine de la brisure de code ci-dessous:

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:

Pour ceux qui voulaient connaître le détail: lorsque j'essaie d'exécuter ce code, les erreurs sur le XMLSerializer[...] ligne avec:

Impossible de sérialiser le Système d'interface.Moment de l'exécution.La sérialisation.ISerializable.

Si je change d' List<object> - je obtenir de l' "There was an error generating the XML document.". Le InnerException détail est - "{"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."}"

La personne objet est défini comme suit:

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

Le PersonList est juste un List<Person> .

C'est juste pour le test, donc ne vous sentez pas à l'détails étaient trop importantes. La clé est que j'ai un ou plusieurs objets différents, qui sont tous des serializable. Je veux sérialiser tous à un seul fichier. J'ai pensé que la meilleure façon de faire c'est de les mettre dans une liste générique et sérialiser la liste en une seule fois. Mais cela ne fonctionne pas.

J'ai essayé avec List<IXmlSerializable> , mais qui échoue avec

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

Désolé pour le manque de détail, mais je suis un débutant et ne sais pas ce détail est nécessaire. Il serait utile si les gens demandent pour plus de détails essayé de répondre d'une manière qui ne me laisserait comprendre quels sont les renseignements requis, ou une réponse indiquant les directions possibles.

Aussi merci pour les deux réponses que j'ai obtenu jusqu'à présent - je pourrais avoir passé beaucoup plus de temps à lire sans arriver à ces idées. Il est incroyable de voir comment les gens utiles sont sur ce site.

89voto

Damasch Points 341

J'ai une solution pour une Liste générique<> avec dynamique de lier les éléments.

classe PersonalList c'est l'élément racine

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

Personne de la classe, c'est un simple élément de la liste

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

classe SpecialPerson hérite de Personne

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

classe SuperPerson hérite de Personne

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

et la principale Source de test

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

L'Important est la définition et comprend des formules types.

23voto

John Saunders Points 118808

Voir l'Introduction de la Sérialisation XML:

Les Éléments Qui Peuvent Être Sérialisées

Les éléments suivants peuvent être sérialisés à l'aide de la classe XmLSerializer:

  • Public propriétés en lecture/écriture et les champs de classes publiques
  • Les Classes qui implémentent ICollection ou IEnumerable
  • XmlElement objets
  • XmlNode objets
  • DataSet objets


En particulier, ISerializable ou [Serializable] attribut n'a pas d'importance.


Maintenant que vous avez dit nous quel est ton problème ("ça ne marche pas" n'est pas un énoncé du problème), vous pouvez obtenir des réponses à votre problème réel, plutôt que de suppositions.

Lorsque vous sérialiser une collection d'un type, mais sera effectivement la sérialisation d'une collection d'instances de types dérivés, vous devez laisser le sérialiseur savoir quels types vous aurez en fait être la sérialisation. Cela est également vrai pour les collections de object.

Vous devez utiliser le XmlSerializer(Type,Type[]) constructeur de donner la liste des types possibles.

9voto

Thomas Levesque Points 141081

Vous ne pouvez pas sérialiser une collection d'objets sans spécifier les types attendus. Vous devez transmettre la liste des types attendus au constructeur de XmlSerializer (paramètre 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))
{
    xmlSerializer.Serialize(streamWriter, list);
}
 

Si tous les objets de votre liste héritent de la même classe, vous pouvez également utiliser l'attribut XmlInclude pour spécifier les types attendus:

 [XmlInclude(typeof(Foo)), XmlInclude(typeof(Bar))]
public class MyBaseClass
{
}
 

4voto

Andreas Grech Points 39188

Je pense que c'est mieux si vous utilisez des méthodes avec des arguments génériques, comme suit:

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

3voto

Ian Points 13892

Je pense que Dreas' approche est ok. Une alternative à ceci, cependant, est d'avoir une statique méthodes d'assistance et de mettre en œuvre IXmlSerializable sur chacun de vos méthodes d'e.g un XmlWriter méthode d'extension et de l'XmlReader un pour le lire.

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

Si vous allez en bas de la route de l'utilisation de la classe XmlSerializer directement, créer de sérialisation assemblées avant de la main si possible, comme vous pouvez le prendre de grandes performances dans la construction de nouvelles XmlSerializers régulièrement.

Pour une collection, vous besoin de quelque chose comme ceci:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X