2009年12月1日 星期二

如何移除XML序列化時的Namespace宣告

今天在寫Web Service時,對方要求Web Method回傳的結果是用XML字串,但我又不想用組字串的方式來組出回應的XML,雖然可以用XML DOM的方法在記憶體產生XML DOM後再吐出XML字串,但這個方法又太麻煩了些,所以就想到用XML序列化這個好物,於是寫了一個小Function來做XML序列化的動作.
比如我的XML長這樣:
   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Order>
   3:   <OrderId>1</OrderId>
   4:   <OrderDate>2009/12/2</OrderDate>
   5: </Order>

所以我就建了一個用來序列化這個XML的Class,如下:


   1: Public Class Order
   2:     Private _OrderId As String
   3:     Private _OrderDate As String
   4:  
   5:     Public Property OrderId() As String
   6:         Get
   7:             Return _OrderId
   8:         End Get
   9:         Set(ByVal value As String)
  10:             _OrderId = value
  11:         End Set
  12:     End Property
  13:  
  14:     Public Property OrderDate() As String
  15:         Get
  16:             Return _OrderDate
  17:         End Get
  18:         Set(ByVal value As String)
  19:             _OrderDate = value
  20:         End Set
  21:     End Property
  22: End Class

當然XML文件要怎麼對應成這個Class?最簡單的方法是透過.Net Framework所提供的xsd.exe工具來作,xsd工具可以從XML文件產生XML Schema(就是此份XML的定義檔),然後再從XML Schema產生出Class,詳細用法大家可以參考MSDN文件的說明.

為了方便起見,我寫了個小Function用來做XML序列化的動作,如下面程式:


   1: Public Function SerializeToXml(ByVal targetObject As Object) As String
   2:     Dim ser As New XmlSerializer(targetObject.GetType)
   3:     Dim mem As New IO.MemoryStream
   4:     Dim writer As New Xml.XmlTextWriter(mem, System.Text.Encoding.UTF8)
   5:     ser.Serialize(writer, targetObject)
   6:     mem = CType(writer.BaseStream, IO.MemoryStream)
   7:     Return Encoding.UTF8.GetString(mem.ToArray)
   8: End Function

要用時只要傳入塞好值的物件,就可以吐出我要的XML了,如下面程式:


   1: Dim obj As New Order
   2: obj.OrderId = "1"
   3: obj.OrderDate = Now.ToString
   4:  
   5: Console.WriteLine(SerializeToXml(obj))


吐出來的XML如下:


   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   3: <OrderId>1</OrderId>
   4: <OrderDate>2009/12/2 下午 03:33:50</OrderDate>
   5: </Order>

很簡單吧,不過仔細看看,在Order中多了2個Namespace的宣告,xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"跟xmlns:xsd="http://www.w3.org/2001/XMLSchema",不理他嗎?當初跟廠商談XML格式沒有說要加這2個namespace,萬一傳回去對方驗證錯誤怎辦?所以為了保險起見,還是要想辦法去掉這2個Namespace宣告,要控制序列化的結果當然就是從XML序列化的相關Attribute著手,看有沒有那個Attribute能去掉這2個宣告的,試了XmlRoot,XmlType這幾個跟Root有關的屬性都不行,正想放棄用Replace大法直接把這2串字去掉時,想到萬一未來版本Microsoft把序列化的Default Namespace給改了怎辦?那我不就要改程式,這麼苟且好像不太好,所以就上網goole一下看有沒有解,還好讓我找到原來可以透過XmlSerializerNamespaces來去掉或是變更序列化的Namespace的方法,所以把剛才的函式修改如下(重點是第4,5及第7行):


   1: Public Function SerializeToXml(ByVal targetObject As Object) As String
   2:     Dim ser As New XmlSerializer(targetObject.GetType)
   3:     Dim mem As New IO.MemoryStream
   4:     Dim ns As New XmlSerializerNamespaces()
   5:     ns.Add("", "")
   6:     Dim writer As New Xml.XmlTextWriter(mem, System.Text.Encoding.UTF8)
   7:     ser.Serialize(writer, targetObject, ns)
   8:     mem = CType(writer.BaseStream, IO.MemoryStream)
   9:     Return Encoding.UTF8.GetString(mem.ToArray)
  10: End Function

改好後,吐出來的XML果然就跟我想要的XML 100%相同了.YA!

沒有留言:

張貼留言