2010-04-22 94 views
23

我有以下類如何添加XmlInclude屬性動態

[XmlRoot] 
public class AList 
{ 
    public List<B> ListOfBs {get; set;} 
} 

public class B 
{ 
    public string BaseProperty {get; set;} 
} 

public class C : B 
{ 
    public string SomeProperty {get; set;} 
} 

public class Main 
{ 
    public static void Main(string[] args) 
    { 
     var aList = new AList(); 
     aList.ListOfBs = new List<B>(); 
     var c = new C { BaseProperty = "Base", SomeProperty = "Some" }; 
     aList.ListOfBs.Add(c); 

     var type = typeof (AList); 
     var serializer = new XmlSerializer(type); 
     TextWriter w = new StringWriter(); 
     serializer.Serialize(w, aList); 
    }  
} 

現在,當我嘗試運行我在最後一行一個InvalidOperationException代碼說

是沒有預料到的類型XmlTest.C 。使用XmlInclude或SoapInclude屬性指定靜態未知的類型。

我知道用[XmlRoot]添加[XmlInclude(typeof(C))]屬性可以解決問題。但我想動態實現它。因爲在我的項目中,C類在加載之前是未知的。 C類作爲插件被加載,所以我不可能在那裏添加XmlInclude屬性。

TypeDescriptor.AddAttributes(typeof(AList), new[] { new XmlIncludeAttribute(c.GetType()) }); 

也試過

var type = typeof (AList); 

但沒有用了。它仍然給出同樣的例外。

有沒有人對如何實現它有任何想法?

回答

28

兩個選項;最簡單的(但給奇數XML)是:

XmlSerializer ser = new XmlSerializer(typeof(AList), 
    new Type[] {typeof(B), typeof(C)}); 

隨着輸出示例:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <ListOfBs> 
    <B /> 
    <B xsi:type="C" /> 
    </ListOfBs> 
</AList> 

更優雅是:

XmlAttributeOverrides aor = new XmlAttributeOverrides(); 
XmlAttributes listAttribs = new XmlAttributes(); 
listAttribs.XmlElements.Add(new XmlElementAttribute("b", typeof(B))); 
listAttribs.XmlElements.Add(new XmlElementAttribute("c", typeof(C))); 
aor.Add(typeof(AList), "ListOfBs", listAttribs); 

XmlSerializer ser = new XmlSerializer(typeof(AList), aor); 

隨着輸出示例:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <b /> 
    <c /> 
</AList> 

無論哪種情況你都可以必須緩存並重新使用ser實例;否則你會從動態編譯中溢出內存。

+1

我試過這個,但它給了我下面這個異常 「可能不會爲XmlTest.AList類型指定XmlRoot和XmlType屬性。 任何想法爲什麼它即將到來以及如何解決它? – 2010-04-22 10:02:49

+0

@Aindind - 那很奇怪; 「原樣」的例子對我來說工作得很好。你使用的是什麼框架版本? – 2010-04-23 05:21:13

+0

@Anindya - 我試過4.0和2.0(涵蓋3.0和3.5)並且無法重現。你能給出一個更完整的描述嗎? – 2010-04-23 05:25:51

2

查看XmlSerializer的文檔。有一個構造函數期望已知類型作爲第二個參數。這應該適合你的使用情況。

0

我不認爲屬性可以在運行時應用,因爲它們用於在CIL代碼中創建元數據。

6

建立在Marc的第一個答案(我只需要閱讀,所以我不需要防止奇怪的輸出),我使用更動態/通用類型數組來解釋未知類型,受此codeproject的啓發。

public static XmlSerializer GetSerializer() 
    { 
     var lListOfBs = (from lAssembly in AppDomain.CurrentDomain.GetAssemblies() 
          from lType in lAssembly.GetTypes() 
          where typeof(B).IsAssignableFrom(lType) 
          select lType).ToArray(); 
     return new XmlSerializer(typeof(AList), lListOfBs); 
    } 

(人們可以使用靜態或只讀型數組中的局部變量代替可能使之更有效率,例如,這將避免反覆使用反射,但我不知道有足夠的瞭解時,程序集會被加載,並且類和屬性會被初始化,以便知道它是否會給你帶來麻煩。我的用法並不多,花時間調查這一切,所以我多次使用同一個Reflection。)

+0

非常有魅力,非常感謝(和marc) – Tiax 2015-02-24 08:22:50