Is it possible to deserialize XML into List<T>?

161,201

Solution 1

You can encapsulate the list trivially:

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

Solution 2

If you decorate the User class with the XmlType to match the required capitalization:

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

Then the XmlRootAttribute on the XmlSerializer ctor can provide the desired root and allow direct reading into 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);
    }

Credit: based on answer from YK1.

Solution 3

I think I have found a better way. You don't have to put attributes into your classes. I've made two methods for serialization and deserialization which take generic list as parameter.

Take a look (it works for 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;
    }

So you can serialize whatever list you want! You don't need to specify the list type every time.

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

Solution 4

Yes, it will serialize and deserialize a List<>. Just make sure you use the [XmlArray] attribute if in doubt.

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

This works with both Serialize() and Deserialize().

Solution 5

Yes, it does deserialize to List<>. No need to keep it in an array and wrap/encapsulate it in a list.

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

    public UserHolder()
    {
    }

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

Deserializing code,

XmlSerializer xs = new XmlSerializer(typeof(UserHolder));
UserHolder uh = (UserHolder)xs.Deserialize(new StringReader(str));
Share:
161,201

Related videos on Youtube

Daniel Schaffer
Author by

Daniel Schaffer

Updated on August 02, 2020

Comments

  • Daniel Schaffer
    Daniel Schaffer almost 4 years

    Given the following 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>
    

    And the following class:

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

    Is it possible to use XmlSerializer to deserialize the xml into a List<User> ? If so, what type of additional attributes will I need to use, or what additional parameters do I need to use to construct the XmlSerializer instance?

    An array ( User[] ) would be acceptable, if a bit less preferable.

  • JaredPar
    JaredPar over 15 years
    @Daniel, AFAIK, no. You need to serialize and deserialize into some concrete object type. I do not believe that XML serialization natively supports collection classes as the start of a serialization. I do not 100% know that though.
  • Jon Kragh
    Jon Kragh almost 14 years
    Nice solution with the [XmlElement("user")] to avoid an extra level of elements. Looking at this, I thought for sure that it would have emitted a <user> or <Items> node (if you did not have the XmlElement attribute), and then add <user> nodes under that. But I tried it and it did not, thus emitting exactly what the question wanted.
  • Max Toro
    Max Toro about 12 years
    Thanks for actually answering the question. I would add that for List<MyClass> the document element should be named ArrayOfMyClass.
  • Picrofo Software
    Picrofo Software over 11 years
    Welcome to stackoverflow! It's always better to provide a short description for a sample code to improve the post accuracy :)
  • Kala J
    Kala J about 10 years
    What if I had two lists under UserList above? I tried your method and it says it already defines a member called XYZ with the same parameter types
  • eduardobr
    eduardobr over 8 years
    [XmlElement("list")] should be [XmlArray("list")] instead. That is the only way Deserialization worked for me in .NET 4.5
  • DDRider62
    DDRider62 over 7 years
    In my point of view, this is clearly THE answer to the question. The question was about deserializing into List<T>. All the other solutions, except maybe one, include a wrapping class to contain the list, which was certainly not the question posted, and what the author of the question seems to be trying to avoid.
  • DDRider62
    DDRider62 over 7 years
    I don't know why this is marked as right answer. It includes adding a class to wrap the list. That was certainly what the question is trying to avoid.
  • Marc Gravell
    Marc Gravell over 7 years
    @DDRider62 the question doesn't say "without wrapping". Most people are pretty pragmatic and just want to get the data out. This answer allows you to do that, via the .Items member.
  • dbc
    dbc almost 6 years
    With this approach, the XmlSerializer must be statically cached and reused to avoid a severe memory leak, see Memory Leak using StreamReader and XmlSerializer for details.