使用XmlSerializer序列化可空屬性


使用XmlSerializer可以方便的將對象序列化為xml,實現應用之間的數據交互。但是XmlSerializer卻不能很好地序列化類型中的可空字段。 
例如,有如下定義的類Person: 

C#代碼   收藏代碼
  1. [Serializable]  
  2. [XmlRoot(ElementName = "Person")]  
  3. public class Person  
  4. {  
  5.     public string FirstName { getset; }  
  6.   
  7.     public string LastName { getset; }  
  8.   
  9.     public int? Age { getset; }  
  10. }  


其中的Age屬性為Nullable int類型。 
我們的實例化代碼如下所示: 

C#代碼   收藏代碼
  1. var person = new Person  
  2.                  {  
  3.                      FirstName = "First",  
  4.                  };  
  5. person.OutputXml(Console.Out);  


其中方法OutputXml為擴展方法,使用XmlSerializer來序列化對象,具體定義為: 

C#代碼   收藏代碼
  1. public static void OutputXml<T>(this T dto, TextWriter textWriter)  
  2. {  
  3.     var xmlTypeMapping = typeof(T);  
  4.     var serializer = new XmlSerializer(xmlTypeMapping);  
  5.     var xmlns = new XmlSerializerNamespaces();  
  6.     xmlns.Add(string.Empty, string.Empty);  
  7.     using (var writer = new XmlTextWriter(textWriter) { Formatting = Formatting.Indented })  
  8.     {  
  9.         serializer.Serialize(writer, dto, xmlns);  
  10.     }  
  11. }  


使用上述方法序列化對象person,得到的結果為: 
 
注意到雖然Age屬性為空,卻仍然被序列化成 了古怪的xml,這往往不是所期望的結果。事實上同為空的LastName屬性的序列化正是大多數情況下我們所期望的行為——忽略為空的屬性。 
另一方面,如果試圖序列化類屬性為xml 屬性(而非xml元素),則甚至不能工作。例如如果我們將Person類的定義修改成如下形式以便序列化為xml屬性: 

C#代碼   收藏代碼
  1. [Serializable]  
  2. [XmlRoot(ElementName = "Person")]  
  3. public class Person  
  4. {  
  5.     [XmlAttribute]  
  6.     public string FirstName { getset; }  
  7.   
  8.     [XmlAttribute]  
  9.     public string LastName { getset; }  
  10.   
  11.     [XmlAttribute]  
  12.     public int? Age { getset; }  
  13. }  


Xmlserializer甚至無法正常序列化上面同樣的person對象並拋出如下“XmlAttribute/XmlText cannot be used to encode complex types”的錯誤: 
 
如何解決上述的2個問題呢? 
1. 序列化可空屬性為XmlElement 
為了在序列化可空屬性的時候忽略空值的古怪輸出,我們可以在Person類中定義一個返回bool的特殊方法ShouldSerializeAge,並在其中實現定義我們的序列化規則: 

C#代碼   收藏代碼
  1. [Serializable]  
  2. [XmlRoot(ElementName = "Person")]  
  3. public class Person  
  4. {  
  5.     public string FirstName { getset; }  
  6.   
  7.     public string LastName { getset; }  
  8.   
  9.     public int? Age { getset; }  
  10.   
  11.     public bool ShouldSerializeAge()  
  12.     {  
  13.         return Age.HasValue;  
  14.     }  
  15. }  


在這里我們定義序列化規則為:序列化非空Age。注意該方法的名字一定是以ShouldSerialize開頭並連接上需要自定義規則的屬性名稱。這樣序列化出來的person對象為: 
 
空的Age和空的LastName一樣在序列化時被忽略了輸出。正是我們期望的結果! 
2. 序列化可空屬性為XmlAttribute 
由於可空屬性無法被直接序列化為XmlAttribute,我們需要采用間接的辦法——定義間接屬性。此時我們可以如下定義Person類: 

C#代碼   收藏代碼
  1. [Serializable]  
  2. [XmlRoot(ElementName = "Person")]  
  3. public class Person  
  4. {  
  5.     [XmlAttribute]  
  6.     public string FirstName { getset; }  
  7.   
  8.     [XmlAttribute]  
  9.     public string LastName { getset; }  
  10.   
  11.     [XmlIgnore]   // (4)   
  12.     public int? Age { getset; }  
  13.   
  14.     [XmlAttribute(AttributeName = "Age")]  // (1)  
  15.     public string AgeValue  
  16.     {  
  17.         get  
  18.         {  
  19.             // (2)  
  20.             return Age.HasValue ? Age.Value.ToString() : null;  
  21.         }  
  22.         set  
  23.         {  
  24.             int result;  
  25.             // (3)  
  26.             Age = int.TryParse(value, out result) ? result : (int?) null;  
  27.         }  
  28.     }  
  29. }  


注意類中注釋的部分: 

  1. 為原可空屬性定義一個“虛擬”的屬性,該屬性可為任意名稱任意類型(示例里定義的為string類型的AgeValue),但注意要將該屬性的序列化名稱置為原可空屬性Age。
  2. 在get方法里根據Age是否為空將其轉化為AgeValue。
  3. 在set方法里將輸入的value轉化為合適的值賦給可空屬性Age。
  4. 將原Age屬性標記為XmlIgnore,使XmlSerializer在序列化時忽略序列化該屬性。


使用上述方法,我們可以將Age序列化為XmlAttribute了,雖然實際上我們是“騙”了XmlSerializer並使用另一個屬性作為Age屬性的值。 
因此,當person對象中的Age為空時,我們得到如下的xml結果: 
 
而當person對象中的Age不為空時,我們也可以正常的用XmlAttribute來表示Age的值了。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM