web-dev-qa-db-fra.com

Sérialisation des listes de classes en XML

J'ai une collection de classes que je veux sérialiser vers un fichier XML. Cela ressemble à ceci:

public class Foo
{
  public List<Bar> BarList { get; set; }
}

Où une barre n'est qu'un wrapper pour une collection de propriétés, comme ceci:

public class Bar
{
  public string Property1 { get; set; }
  public string Property2 { get; set; }
}

Je veux marquer cela pour qu'il sorte dans un fichier XML - cela sera utilisé à la fois pour la persistance, et aussi pour rendre les paramètres via un XSLT dans une forme lisible par l'homme.

Je veux obtenir une belle représentation XML comme celle-ci:

<?xml version="1.0" encoding="utf-8"?>
<Foo>
  <BarList>
    <Bar>
      <Property1>Value</Property1>
      <Property2>Value</Property2>   
    </Bar>
    <Bar>
      <Property1>Value</Property1>
      <Property2>Value</Property2>   
    </Bar>
  </Barlist>
</Foo>

où se trouvent tous les Bars de la Barlist avec toutes leurs propriétés. Je suis assez sûr que j'aurai besoin d'un balisage sur la définition de classe pour le faire fonctionner, mais je n'arrive pas à trouver la bonne combinaison.

J'ai marqué Foo avec l'attribut

[XmlRoot("Foo")]  

et le list<Bar> avec l'attribut

[XmlArray("BarList"), XmlArrayItem(typeof(Bar), ElementName="Bar")]

pour essayer de dire au sérialiseur ce que je veux faire. Cela ne semble pas fonctionner cependant et je reçois juste une balise vide, ressemblant à ceci:

<?xml version="1.0" encoding="utf-8"?>
<Foo> 
  <Barlist />
</Foo>

Je ne sais pas si le fait que j'utilise les propriétés automatiques devrait avoir un effet, ou si l'utilisation de génériques nécessite un traitement spécial. J'ai réussi à utiliser cela avec des types plus simples comme une liste de chaînes, mais une liste de classes m'a jusqu'ici échappé.

34
Jon Artus

Juste pour vérifier, avez-vous marqué Bar comme [Sérialisable]?

De plus, vous avez besoin d'un ctor sans paramètre sur Bar, pour désérialiser

Hmm, j'ai utilisé:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {

        Foo f = new Foo();

        f.BarList = new List<Bar>();

        f.BarList.Add(new Bar { Property1 = "abc", Property2 = "def" });

        XmlSerializer ser = new XmlSerializer(typeof(Foo));

        using (FileStream fs = new FileStream(@"c:\sertest.xml", FileMode.Create))
        {
            ser.Serialize(fs, f);
        }
    }
}

public class Foo
{
    [XmlArray("BarList"), XmlArrayItem(typeof(Bar), ElementName = "Bar")]
    public List<Bar> BarList { get; set; }
}

[XmlRoot("Foo")]
public class Bar
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
}

Et cela a produit:

<?xml version="1.0"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <BarList>
    <Bar>
      <Property1>abc</Property1>
      <Property2>def</Property2>
    </Bar>
  </BarList>
</Foo>
31
Carl

Tout a l'air génial. Comme @Carl l'a dit, vous devez ajouter l'attribut [Serializable] à vos classes, mais à part cela, votre création XML devrait fonctionner.

Foo

[Serializable]
[XmlRoot("Foo")]
public class Foo
{
    [XmlArray("BarList"), XmlArrayItem(typeof(Bar), ElementName = "Bar")]
    public List<Bar> BarList { get; set; }
}

Bar

[Serializable]
public class Bar
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
}

Code à tester

Foo f = new Foo();
f.BarList = new List<Bar>();
f.BarList.Add(new Bar() { Property1 = "s", Property2 = "2" });
f.BarList.Add(new Bar() { Property1 = "s", Property2 = "2" });

FileStream fs = new FileStream("c:\\test.xml", FileMode.OpenOrCreate);
System.Xml.Serialization.XmlSerializer s = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
s.Serialize(fs, f);

Sortie

<?xml version="1.0" ?> 
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <BarList>
        <Bar>
            <Property1>s</Property1> 
            <Property2>2</Property2> 
        </Bar>
        <Bar>
            <Property1>s</Property1> 
            <Property2>2</Property2> 
        </Bar>
    </BarList>
</Foo>
4
FryHard
var xmlfromLINQ = new XElement("BarList",
            from c in BarList 
            select new XElement("Bar",
                new XElement("Property1", c.Property1),
                new XElement("Property2", c.Property2)
             ));
1
Lê Quý Đôn

Cela fait plus de 5 ans que cet article a été publié. Je donne mon expérience depuis juillet 2013 (.NET Framework 4.5). Pour ce que cela vaut et pour qui cela peut concerner:

Quand je définis une classe comme ça: (code VB.Net)

<Serializable> Public Class MyClass
    Public Property Children as List(of ChildCLass)
    <XmlAttribute> Public Property MyFirstProperty as string
    <XmlAttribute> Public Property MySecondProperty as string
End Class

<Serializable> Public Class ChildClass
    <XmlAttribute> Public Property MyFirstProperty as string
    <XmlAttribute> Public Property MySecondProperty as string
End Class

Avec cette définition, la classe est (dé) sérialisée sans aucun problème. Voici le XML qui sort d'ici:

<MyClass> MyFirstProperty="" MySecondProperty=""
    <Children>
        <ChildClass> MyFirstProperty="" MySecondProperty=""
        </ChildClass>
   </Children>
</MyClass>

Il ne m'a fallu que deux jours pour comprendre que la solution était de laisser de côté le <XmlElement> préfixe des éléments List (of T).

1
Peter Klein