2011-09-20 70 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

相關問題