Ist es möglich, XML in Liste <T> zu deserialisieren?

155

Gegeben das folgende 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>

Und die folgende Klasse:

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

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

Ist es möglich, XmlSerializerdie XML in eine zu deserialisieren List<User>? Wenn ja, welche Art von zusätzlichen Attributen muss ich verwenden oder welche zusätzlichen Parameter muss ich zum Erstellen der XmlSerializerInstanz verwenden?

Ein Array ( User[]) wäre akzeptabel, wenn es etwas weniger vorzuziehen wäre.

Daniel Schaffer
quelle

Antworten:

137

Sie können die Liste trivial kapseln :

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);
    }
}
Marc Gravell
quelle
5
Gute Lösung mit dem [XmlElement ("Benutzer")], um eine zusätzliche Ebene von Elementen zu vermeiden. Als ich das betrachtete, dachte ich sicher, dass es einen <user> - oder <Items> -Knoten ausgegeben hätte (wenn Sie nicht das XmlElement-Attribut hätten), und fügte dann <user> -Knoten darunter hinzu. Aber ich habe es versucht und es nicht, und so genau das ausgesendet, was die Frage wollte.
Jon Kragh
Was wäre, wenn ich oben zwei Listen unter UserList hätte? Ich habe Ihre Methode ausprobiert und es heißt, dass sie bereits ein Mitglied namens XYZ mit denselben Parametertypen definiert
Kala J
Ich weiß nicht, warum dies als richtige Antwort markiert ist. Es umfasst das Hinzufügen einer Klasse zum Umschließen der Liste. Das war es sicherlich, was die Frage zu vermeiden versucht.
DDRider62
1
@ DDRider62 die Frage sagt nicht "ohne Wrapping". Die meisten Leute sind ziemlich pragmatisch und wollen nur die Daten herausholen. Mit dieser Antwort können Sie dies über das .ItemsMitglied tun .
Marc Gravell
50

Wenn Sie die UserKlasse mit dem dekorieren, das XmlTypeder erforderlichen Großschreibung entspricht:

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

Dann kann der XmlRootAttributeon the XmlSerializerctor die gewünschte Wurzel bereitstellen und das direkte Einlesen in List <> ermöglichen:

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

Gutschrift: basierend auf der Antwort von YK1 .

richaux
quelle
11
Aus meiner Sicht ist dies eindeutig DIE Antwort auf die Frage. Die Frage betraf die Deserialisierung in Liste <T>. Alle anderen Lösungen, außer vielleicht einer, enthalten eine Wrapping-Klasse, die die Liste enthält, die sicherlich nicht die gestellte Frage war, und was der Autor der Frage zu vermeiden versucht.
DDRider62
1
Bei diesem Ansatz XmlSerializermuss der statisch zwischengespeichert und wiederverwendet werden, um einen schwerwiegenden Speicherverlust zu vermeiden. Weitere Informationen finden Sie unter Speicherverlust mit StreamReader und XmlSerializer .
dbc
16

Ja, eine Liste wird serialisiert und deserialisiert <>. Stellen Sie einfach sicher, dass Sie im Zweifelsfall das Attribut [XmlArray] verwenden.

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

Dies funktioniert sowohl mit Serialize () als auch mit Deserialize ().

Münze
quelle
16

Ich glaube, ich habe einen besseren Weg gefunden. Sie müssen keine Attribute in Ihre Klassen einfügen. Ich habe zwei Methoden für die Serialisierung und Deserialisierung erstellt, bei denen die generische Liste als Parameter verwendet wird.

Schau mal (es funktioniert bei mir):

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

So können Sie jede gewünschte Liste serialisieren! Sie müssen den Listentyp nicht jedes Mal angeben.

        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);
tudor.iliescu
quelle
3
Vielen Dank, dass Sie die Frage tatsächlich beantwortet haben. Ich würde hinzufügen, dass für List<MyClass>das Dokument Element benannt werden sollte ArrayOfMyClass.
Max Toro
8

Ja, es wird in List <> deserialisiert. Sie müssen es nicht in einem Array aufbewahren und in eine Liste einschließen / einkapseln.

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

    public UserHolder()
    {
    }

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

Deserialisierungscode,

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

Ich bin mir nicht sicher über List <T>, aber Arrays sind auf jeden Fall machbar. Und ein bisschen Magie macht es wirklich einfach, wieder zu einer Liste zu gelangen.

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

   [XmlIgnore]
   public List<User> UserList { get { return new List<User>(Users); } }
}
JaredPar
quelle
2
Kann man auf die Klasse "Inhaber" verzichten?
Daniel Schaffer
@ Daniel, AFAIK, nein. Sie müssen in einen konkreten Objekttyp serialisieren und deserialisieren. Ich glaube nicht, dass die XML-Serialisierung Sammlungsklassen als Start einer Serialisierung nativ unterstützt. Das weiß ich allerdings nicht zu 100%.
JaredPar
[XmlElement ("Liste")] sollte stattdessen [XmlArray ("Liste")] sein. Nur so funktionierte die Deserialisierung für mich in .NET 4.5
eduardobr
2

Wie wäre es mit

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

Nicht besonders schick, aber es sollte funktionieren.

PRJ
quelle
2
Willkommen bei stackoverflow! Es ist immer besser, eine kurze Beschreibung für einen Beispielcode bereitzustellen, um die Post-Genauigkeit zu verbessern :)
Picrofo Software