2011-09-20 71 views
7

我有一个C#.NET 3.5应用程序,我想将包含List<>的类序列化为XML。我的阶级是这样的:序列化导出为ICollection <>到XML的List <>

[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> 

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

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

什么我需要做的就是将List<>出现在XML?

+0

我不相信你可以'XmlSerialize'的接口。你为什么要序列化为'ICollection'呢?序列化为'List '并返回给消费者一个'ICollection '...... ??? – IAbstract

+0

@IAbstract - 我不确定我是否理解。你的意思是用'[XmlElement(“Bar”)]'标记标记'私人列表 bar_'吗?这不会改变输出。另外,'XmlSerializer'文档建议它可以同时处理IEnumerable和ICollection接口。 http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=VS.90%29.aspx – PaulH

+0

我认为IAbstract有它 - 你不能序列化的接口。所以相反,你应该改变Foo,以便Bars是一个列表,而不是ICollection –

回答

2

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,写自己的自定义序列化。

编辑:阿图尔Mustafin是正确的,成员实现IEnumerableICollection不需要二传手,如在this msdn page解释说:

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

+2

这是错误和丑陋的答案,这是错误的,因为它的工作事件没有setter,看到我的文章 –

+0

XmlSerializer不要求可序列化的属性有一个二传手 –

3

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

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

你只需要二传手添加到正在序列化的公共属性,并更改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

相关问题