È possibile deserializzare XML in List <T>?


155

Dato il seguente XML:

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

E la seguente classe:

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

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

È possibile utilizzare XmlSerializerper deserializzare l'xml in un List<User>? In tal caso, quale tipo di attributi aggiuntivi dovrò usare o quali parametri aggiuntivi dovrò usare per costruire l' XmlSerializeristanza?

Un array ( User[]) sarebbe accettabile, se un po 'meno preferibile.

Risposte:


137

È possibile incapsulare l'elenco in modo banale:

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

5
Bella soluzione con [XmlElement ("utente")] per evitare un ulteriore livello di elementi. Guardando questo, ho pensato con certezza che avrebbe emesso un nodo <utente> o <Articoli> (se non avessi l'attributo XmlElement), e quindi aggiungere nodi <utente> sotto quello. Ma l'ho provato e non l'ha fatto, emettendo così esattamente ciò che la domanda voleva.
Jon Kragh,

E se avessi due elenchi in Elenco utenti sopra? Ho provato il tuo metodo e dice che definisce già un membro chiamato XYZ con gli stessi tipi di parametri
Kala J

Non so perché questo sia contrassegnato come risposta giusta. Include l'aggiunta di una classe per avvolgere l'elenco. Questo era certamente ciò che la domanda sta cercando di evitare.
DDRider62,

1
@ DDRider62 la domanda non dice "senza avvolgimento". Molte persone sono piuttosto pragmatiche e vogliono solo ottenere i dati. Questa risposta ti consente di farlo, tramite il .Itemsmembro.
Marc Gravell

50

Se decori la Userclasse con il XmlTypecorrispondente alla maiuscola richiesta:

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

Quindi l' XmlRootAttributeon the XmlSerializerctor può fornire la radice desiderata e consentire la lettura diretta nella Lista <>:

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

Credito: basato sulla risposta di YK1 .


11
Dal mio punto di vista, questa è chiaramente la risposta alla domanda. La domanda riguardava la deserializzazione nella Lista <T>. Tutte le altre soluzioni, tranne forse una, includono una classe di wrapping per contenere l'elenco, che sicuramente non era la domanda pubblicata, e ciò che l'autore della domanda sembra cercare di evitare.
DDRider62,

1
Con questo approccio, è XmlSerializernecessario essere staticamente memorizzati nella cache e riutilizzati per evitare una grave perdita di memoria, vedere Perdita di memoria utilizzando StreamReader e XmlSerializer per i dettagli.
dbc,

16

Sì, serializzerà e deserializzerà un Elenco <>. Assicurati di utilizzare l'attributo [XmlArray] in caso di dubbio.

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

Funziona con Serialize () e Deserialize ().


16

Penso di aver trovato un modo migliore. Non devi inserire attributi nelle tue classi. Ho creato due metodi per la serializzazione e la deserializzazione che prendono la lista generica come parametro.

Dai un'occhiata (funziona per me):

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

Quindi puoi serializzare qualunque lista tu voglia! Non è necessario specificare il tipo di elenco ogni volta.

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

3
Grazie per aver effettivamente risposto alla domanda. Vorrei aggiungere che per List<MyClass>l'elemento del documento dovrebbe essere nominato ArrayOfMyClass.
Max Toro,

8

Sì, deserializza in Elenco <>. Non è necessario tenerlo in un array e avvolgerlo / incapsularlo in un elenco.

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

    public UserHolder()
    {
    }

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

Codice di deserializzazione,

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

5

Non sono sicuro dell'elenco <T> ma gli array sono sicuramente fattibili. E un po 'di magia rende davvero facile tornare a un Elenco.

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

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

2
È possibile fare a meno della classe "titolare"?
Daniel Schaffer,

@Daniel, AFAIK, no. È necessario serializzare e deserializzare in un tipo di oggetto concreto. Non credo che la serializzazione XML supporti nativamente le classi di raccolta come l'inizio di una serializzazione. Non lo so al 100% però.
JaredPar,

[XmlElement ("elenco")] dovrebbe essere invece [XmlArray ("elenco")]. Questo è l'unico modo in cui la deserializzazione ha funzionato per me in .NET 4.5
eduardobr, l'

2

Che ne dite di

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

Non particolarmente elegante ma dovrebbe funzionare.


2
Benvenuto in StackOverflow! È sempre meglio fornire una breve descrizione di un codice di esempio per migliorare l'accuratezza dei post :)
Software Picrofo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.