是否可以将XML反序列化为List <T>?


155

给定以下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>

和以下类:

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

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

是否可以使用XmlSerializer将XML反序列化为List<User>?如果是这样,我需要使用哪种类型的附加属性,或者我需要使用哪些附加参数来构造XmlSerializer实例?

User[]如果不太理想,则可以使用数组()。

Answers:


137

您可以简单地封装列表:

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
使用[XmlElement(“ user”)]的不错的解决方案,可以避免额外级别的元素。看着这个,我确定可以发出<user>或<Items>节点(如果您没有XmlElement属性),然后在该节点下添加<user>节点。但是我尝试了一下,但没有尝试,因此准确地提出了问题想要的内容。
乔恩·克拉格

如果我在上面的UserList下有两个列表怎么办?我尝试了您的方法,并说它已经定义了具有相同参数类型的名为XYZ的成员
Kala J

我不知道为什么这被标记为正确答案。它包括添加一个类来包装列表。这肯定是这个问题试图避免的问题。
DDRider62 '16

1
@ DDRider62问题不说“不包装”。大多数人都很务实,只想获取数据。该答案允许您通过.Items成员来执行此操作。
马克·格雷夫

50

如果您使用来装饰UserXmlType以匹配所需的大小写:

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

然后XmlRootAttributeXmlSerializerctor上可以提供所需的根目录并允许直接读入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);
    }

信用:基于答案来自YK1


11
在我看来,这显然是问题的答案。问题是关于反序列化为List <T>。除解决方案外,所有其他解决方案都包括一个包装类来包含列表,该类当然不是发布的问题,以及问题的作者似乎想避免的问题。
DDRider62 '16

1
使用这种方法,XmlSerializer必须静态缓存并重新使用,以避免严重的内存泄漏,有关详细信息,请参阅使用StreamReader和XmlSerializer的内存泄漏
dbc

16

是的,它将序列化和反序列化List <>。如有疑问,只需确保使用[XmlArray]属性。

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

这适用于Serialize()和Deserialize()。


16

我想我找到了一个更好的方法。您不必将属性放入类中。我已经提出了两种用于序列化和反序列化的方法,它们以通用列表作为参数。

看看(它对我有用):

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

因此,您可以序列化所需的任何列表!您无需每次都指定列表类型。

        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
感谢您实际回答问题。我要补充一点,List<MyClass>因为document元素应命名为ArrayOfMyClass
马克斯·托罗

8

是的,它确实反序列化到List <>。无需将其保留在数组中并包装/封装在列表中。

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

    public UserHolder()
    {
    }

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

反序列化代码,

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

5

不确定List <T>,但是数组肯定是可行的。一点点魔术使再次进入列表变得非常容易。

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

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

2
没有“ holder”类,是否可以做?
丹尼尔·谢弗

@ Daniel,AFAIK,不。您需要序列化和反序列化为某些具体的对象类型。我不认为XML序列化本身就支持将集合类作为序列化的开始。我不是100%知道的。
JaredPar'3

[XmlElement(“ list”)]应该改为[XmlArray(“ list”)]。这是反序列化在.NET 4.5中为我工作的唯一方式
eduardobr'Mar 3''16十一月

2

怎么样

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

并不是特别花哨,但应该可以。


2
欢迎来到stackoverflow!最好为示例代码提供简短的描述,以提高发布的准确性:)
Picrofo Software 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.