序列化列表<>导出为ICollection<>到XML

时间:2011-09-20 18:59:30

标签: c# xml c#-3.0 xml-serialization

我有一个C#.NET 3.5应用程序,我想将包含List<>的类序列化为XML。我的班级看起来像这样:

[XmlRoot("Foo")]
class Foo
{
    private List<Bar> bar_ = new List<Bar>();

    private string something_ = "My String";

    [XmlElement("Something")]
    public string Something { get { return something_; } }

    [XmlElement("Bar")]
    public ICollection<Bar> Bars
    {
        get { return bar_; }
    }
}

如果我这样填充它:

Bar b1 = new Bar();
// populate b1 with interesting data
Bar b2 = new Bar();
// populate b2 with interesting data

Foo f = new Foo();
f.Bars.Add(b1);
f.Bars.Add(b2);

然后将其序列化为:

using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(@"C:\foo.xml"))
{
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
    serializer.Serialize(textWriter, f);
}

我得到一个看起来像这样的文件:

<Foo>
    <Something>My String</Something>
</Foo>

但是,我想要的是XML,看起来像这样:

<Foo>
    <Something>My String</Something>
    <Bar>
        <!-- Data from first Bar -->
    </Bar>
    <Bar>
        <!-- Data from second Bar -->
    </Bar>
</Foo>

我需要做些什么才能让List<>出现在XML中?

2 个答案:

答案 0 :(得分:3)

XmlSerializer要求可序列化属性具有setter。除此之外,XmlSerializer无法序列化接口属性。以下代码将起作用:

[XmlElement("Bar")]
public List<Bar> Bars
{
    get { return bar_; }
    set { throw new NotSupportedException("This property 'Bars' cannot be set. This property is readonly."); }
}

如果您不喜欢这个解决方案(异常有点难看),那么您可以实现IXmlSerializable并编写自己的自定义序列化。

修改:Artur Mustafin是对的,实施IEnumerableICollection 的成员不需要setter ,如{ {3}}:

  

XmlSerializer为实现IEnumerableICollection的类提供特殊处理。实现IEnumerable的类必须实现采用单个参数的公共Add方法。 Add方法的参数必须与从Current返回的值GetEnumerator属性返回的类型相同,或者是该类型的基础之一。除了ICollection之外,实现IEnumerable的类(例如CollectionBase)必须具有公共Item索引属性(C#中的索引器),它采用整数,并且必须具有公共{ {1}}整数类型的属性。 Count方法的参数必须与从Add属性返回的类型相同,或者是该类型的基础之一。对于实现Item的类,要从序列化的ICollection属性中检索要序列化的值,而不是通过调用Item

答案 1 :(得分:3)

给出正确的答案,没有必要为List<T>公共属性创建丑陋的setter,抛出异常。

这是因为List<>已经实现ICollection<T>并提供了序列化机制使用的签名void Add(T object)的方法;

您只需将setter添加到要序列化的公共属性,并将ICollection<T>更改为List<T>

[XmlRoot("Foo")]
public class Foo
{
    private List<Bar> bar_ = new List<Bar>();

    [XmlElement("Something")]
    public string Something { get; set; }

    [XmlElement("Bar")]
    public List<Bar> Bars { get { return bar_; } }
}

您将获得输出:

<?xml version="1.0" encoding="utf-8"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Something>My String</Something>
  <Bar />
  <Bar />
</Foo>

此外,最好在内存中序列化xml,查看结果或测试它,如下所示:

static void Main(string[] args)
{
     Bar b1 = new Bar();
     // populate b1 with interesting data
     Bar b2 = new Bar();
     // populate b2 with interesting data

     Foo f = new Foo();
     f.Bars.Add(b1);
     f.Bars.Add(b2);
     f.Something = "My String";

     using (MemoryStream ms = new MemoryStream())
     using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(ms))
     {
         System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
         serializer.Serialize(textWriter, f);
         string text = Encoding.UTF8.GetString(ms.ToArray());
         Console.WriteLine(text);
     }

    Console.ReadKey(false);
}

要使用接口序列化,请使用我的项目XmlSerialization