2009-03-03 11 views
129

Dado el siguiente código XML:¿Es posible deserializar XML en la lista <T>?

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

Y la clase siguiente:

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

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

¿Es posible utilizar XmlSerializer deserializar el XML en un List<User>? De ser así, ¿qué tipo de atributos adicionales necesitaré usar o qué parámetros adicionales necesito usar para construir la instancia XmlSerializer?

Una matriz (User[]) sería aceptable, aunque un poco menos preferible.

Respuesta

114

Puede encapsular la lista trivialmente:

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

Buena solución con [XmlElement ("usuario")] para evitar un nivel extra de elementos. Al ver esto, pensé con certeza que habría emitido un nodo o (si no tenía el atributo XmlElement), y luego agregó nodos debajo de eso. Pero lo probé y no fue así, emitiendo exactamente lo que quería la pregunta. –

+0

¿Qué sucede si tengo dos listas en UserList arriba? Probé tu método y dice que ya define un miembro llamado XYZ con los mismos tipos de parámetros –

+0

No sé por qué esto está marcado como respuesta correcta. Incluye agregar una clase para envolver la lista. Eso fue ciertamente lo que la pregunta está tratando de evitar. – DDRider62

4

No estoy seguro acerca de la lista <T> pero las matrices son ciertamente factibles. Y un poco de magia hace que sea muy fácil llegar a una Lista de nuevo.

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

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

¿Es posible prescindir de la clase "titular"? –

+0

@Daniel, AFAIK, no. Necesita serializar y deserializar en algún tipo de objeto concreto. No creo que la serialización XML admita nativamente las clases de recopilación como el comienzo de una serialización. Aunque no lo sé al 100%. – JaredPar

+0

[XmlElement ("list")] debe ser [XmlArray ("list")] en su lugar. Esa es la única forma en que la deserialización funcionó para mí en .NET 4.5 – eduardobr

13

Sí, va a serializar y deserializar una lista <>. Solo asegúrate de usar el atributo [XmlArray] si tienes dudas.

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

Esto funciona con Serialize() y Deserialize().

+0

¿Funciona esto para una recopilación? –

5

Sí, lo hace Deserialize a la lista <>. No es necesario mantenerlo en una matriz y envolver/encapsularlo en una lista.

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

    public UserHolder() 
    { 
    } 

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

código deserializar,

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

creo que he encontrado una mejor manera. No tienes que poner atributos en tus clases. He hecho dos métodos para serialización y deserialización que toman la lista genérica como parámetro.

Tome una mirada (que funciona para mí):

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

Así se puede serializar lista de lo que quieras! No necesita especificar el tipo de lista cada vez.

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

Gracias por responder la pregunta. Añadiría que para 'List ' el elemento del documento debería llamarse 'ArrayOfMyClass'. –

2

¿Qué tal

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

No es particularmente elegante, pero debería funcionar.

+2

¡Bienvenido a stackoverflow! Siempre es mejor proporcionar una breve descripción de un código de muestra para mejorar la precisión de la publicación :) –

26

Si decorar la clase User con el XmlType para que coincida con la capitalización requerida:

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

Entonces el XmlRootAttribute en el XmlSerializer ctor puede proporcionar la raíz deseada y permitir la lectura directa en 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); 
    } 

Crédito: basado en answer de YK1.

+3

En mi opinión, esta es claramente LA RESPUESTA a la pregunta. La pregunta era sobre la deserialización en la lista . Todas las otras soluciones, excepto tal vez una, incluyen una clase de ajuste para contener la lista, que ciertamente no fue la pregunta publicada, y lo que el autor de la pregunta parece estar tratando de evitar. – DDRider62

Cuestiones relacionadas