web-dev-qa-db-fra.com

Est-il possible de désérialiser XML en liste <T>?

Étant donné le code XML suivant:

<?xml version="1.0"?>
<user_list>
   <user>
      <id>1</id>
      <name>Joe</name>
   </user>
   <user>
      <id>2</id>
      <name>John</name>
   </user>
</user_list>

Et la classe suivante:

public class User {
   [XmlElement("id")]
   public Int32 Id { get; set; }

   [XmlElement("name")]
   public String Name { get; set; }
}

Est-il possible d’utiliser XmlSerializer pour désérialiser le code XML dans un fichier List<User>? Si tel est le cas, quel type d'attributs supplémentaires dois-je utiliser ou quels paramètres supplémentaires dois-je utiliser pour construire l'instance XmlSerializer?

Un tableau ( User[]) serait acceptable, si un peu moins préférable.

147
Daniel Schaffer

Vous pouvez encapsuler la liste trivialement:

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

[XmlRoot("user_list")]
public class UserList
{
    public UserList() {Items = new List<User>();}
    [XmlElement("user")]
    public List<User> Items {get;set;}
}
public class User
{
    [XmlElement("id")]
    public Int32 Id { get; set; }

    [XmlElement("name")]
    public String Name { get; set; }
}

static class Program
{
    static void Main()
    {
        XmlSerializer ser= new XmlSerializer(typeof(UserList));
        UserList list = new UserList();
        list.Items.Add(new User { Id = 1, Name = "abc"});
        list.Items.Add(new User { Id = 2, Name = "def"});
        list.Items.Add(new User { Id = 3, Name = "ghi"});
        ser.Serialize(Console.Out, list);
    }
}
130
Marc Gravell

Si vous décorez la classe User avec le XmlType pour qu'elle corresponde à la capitalisation requise:

[XmlType("user")]
public class User
{
   ...
}

Ensuite, le XmlRootAttribute du XmlSerializer ctor peut fournir la racine souhaitée et permettre une lecture directe dans List <>:

    // e.g. my test to create a file
    using (var writer = new FileStream("users.xml", FileMode.Create))
    {
        XmlSerializer ser = new XmlSerializer(typeof(List<User>),  
            new XmlRootAttribute("user_list"));
        List<User> list = new List<User>();
        list.Add(new User { Id = 1, Name = "Joe" });
        list.Add(new User { Id = 2, Name = "John" });
        list.Add(new User { Id = 3, Name = "June" });
        ser.Serialize(writer, list);
    }

...

    // read file
    List<User> users;
    using (var reader = new StreamReader("users.xml"))
    {
        XmlSerializer deserializer = new XmlSerializer(typeof(List<User>),  
            new XmlRootAttribute("user_list"));
        users = (List<User>)deserializer.Deserialize(reader);
    }

Crédit: basé sur réponse de YK1 .

45
richaux

Je pense avoir trouvé un meilleur moyen. Vous n'êtes pas obligé de mettre des attributs dans vos classes. J'ai mis au point deux méthodes de sérialisation et de désérialisation prenant la liste générique en paramètre.

Jetez un coup d'oeil (ça marche pour moi):

private void SerializeParams<T>(XDocument doc, List<T> paramList)
    {
        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(paramList.GetType());

        System.Xml.XmlWriter writer = doc.CreateWriter();

        serializer.Serialize(writer, paramList);

        writer.Close();           
    }

private List<T> DeserializeParams<T>(XDocument doc)
    {
        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(List<T>));

        System.Xml.XmlReader reader = doc.CreateReader();

        List<T> result = (List<T>)serializer.Deserialize(reader);
        reader.Close();

        return result;
    }

Ainsi, vous pouvez sérialiser la liste que vous voulez! Vous n'avez pas besoin de spécifier le type de liste à chaque fois.

        List<AssemblyBO> list = new List<AssemblyBO>();
        list.Add(new AssemblyBO());
        list.Add(new AssemblyBO() { DisplayName = "Try", Identifier = "243242" });
        XDocument doc = new XDocument();
        SerializeParams<T>(doc, list);
        List<AssemblyBO> newList = DeserializeParams<AssemblyBO>(doc);
16
tudor.iliescu

Oui, il va sérialiser et désérialiser une liste <>. Assurez-vous simplement que vous utilisez l'attribut [XmlArray] en cas de doute.

[Serializable]
public class A
{
    [XmlArray]
    public List<string> strings;
}

Cela fonctionne à la fois avec Serialize () et Deserialize ().

16
Coincoin

Oui, il se désérialise en Liste <>. Pas besoin de le garder dans un tableau et de l'envelopper/de l'encapsuler dans une liste.

public class UserHolder
{
    private List<User> users = null;

    public UserHolder()
    {
    }

    [XmlElement("user")]
    public List<User> Users
    {
        get { return users; }
        set { users = value; }
    }
}

Code de désérialisation,

XmlSerializer xs = new XmlSerializer(typeof(UserHolder));
UserHolder uh = (UserHolder)xs.Deserialize(new StringReader(str));
7
Nemo

Pas sûr de List <T> mais les tableaux sont certainement faisables. Et un peu de magie permet de retrouver facilement une liste.

public class UserHolder {
   [XmlElement("list")]
   public User[] Users { get; set; }

   [XmlIgnore]
   public List<User> UserList { get { return new List<User>(Users); } }
}
5
JaredPar

Que diriez-vous

XmlSerializer xs = new XmlSerializer(typeof(user[]));
using (Stream ins = File.Open(@"c:\some.xml", FileMode.Open))
foreach (user o in (user[])xs.Deserialize(ins))
   userList.Add(o);    

Pas particulièrement chic mais ça devrait marcher.

2
PRJ