DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> F#中的XML序列化
F#中的XML序列化
編輯:XML詳解     

這兩天在用F#寫一小段代碼,須要把一些對象存到外部文件中去。這個功能很簡單,因為.Net本身就內置了序列化功能。方便起見,我打算將這個對象序列化成XML而不是二進制數據流。這意味著我須要運用 XMLSerializer而不是BinaryFormatter。這本應沒有疑問,但是在運用時刻仍舊發生了一些小插曲。

解釋類型

在F#中有多種解釋形式。除了F#特有的Record類型外,在F#中也能夠解釋普通的“類”,如:

#light
moduleXMLSerializationtypePost() = 
    [<DefaultValue>]val mutableTitle : string
    [<DefaultValue>]val mutableContent : string
    [<DefaultValue>]val mutableTags : string array

上面的代碼在XMLSerialization模塊中解釋了一個Post類,其中包含三個公開字段。基本地說,它和C#中的如下解釋等價:

public classPost{public stringTitle;public stringContent;public string[] Tags;
}

可見,在解釋這種基本類型時,F#並沒有什麼優勢,反而須要更多的代碼。

運用 XMLSerializer執行 序列化

原本我以為運用 XMLSerializer來序列化一個對象特別簡單,寫一個基本的(泛型)函數就能夠了:

letbyXmlSerializer (graph: 'a) =letserializer =newXMLSerializer(typeof<'a>)letwriter =newStringWriter()
    serializer.Serialize(writer, graph)
    writer.ToString()

運用起來更加不在話下:

letpost =newXmlSerialization.Post()
post.Title <-"Hello"post.Content <-"World"post.Tags <- ["Hello";"World"]letxml = XmlSerialization.byXMLSerializer(post)
但是,在運行的時刻,XMLSerializer的構造函數卻拋出了InvalidOperationException: 

XMLSerialization cannot be serialized. Static types cannot be used as parameters or return types.

這句話的提示似乎是在說XMLSerialization是一個靜態類型——但這本來是F#的模塊啊。不過運用 .Net Reflector查看編譯後的程序集便會發覺,本來 Post類是這樣解釋的:

public static classXMLSerialization{public classPost{ ... }
}

雖然.Net中也有“模塊”的觀點,但是它和F#中的模塊從各方面來講幾乎沒有相似之處。F#的模塊會被編譯為靜態類,自然模塊中的要領或各種函數便成為靜態類中的內嵌類型及要領。這本沒有疑問,從理論上來說XMLSerializer也不該有疑問,不是嗎?

可惜XMLSerializer的確有這樣的疑問,我認為這是個Bug——但就算這是個Bug也不能處理當前的狀況。事實上,互聯網上也有人提出這個疑問,可惜半年來都沒有人回應。

手動序列化

那麼我又該如何做呢?我想,算了,既然如此,咱們執行 手動序列化吧。反正就是基本的對象,寫起來應該也不麻煩。比方在C#中咱們便能夠:

public classPost{
    ...public stringToXml()
    {varXML =newXElement("Post",newXElement("Title",this.Title),newXElement("Content",this.Content),newXElement("Tags",this.Tags.Select(t =>newXElement("Tag", t))));returnXML.ToString();
    }
}

很基本,不是嗎?但是用F#寫同樣的邏輯便有一些疑問了,結尾 得到的結果是:

typePost() = 
    ...memberp.ToXml() =letxml =newXElement(XName.Get("Post"))
        xml.Add(newXElement(XName.Get("Title"), p.Title))
        xml.Add(newXElement(XName.Get("Content"), p.Content))lettagElements = p.Tags > Array.map (funt-> newXElement(XName.Get("Tag"), t))
        xml.Add(newXElement(XName.Get("Tags"), tagElements))
        XML.ToString()
C#之所以能夠寫的基本,其中有諸多因素: 

  • XElement的構造函數結尾運用了params object[],這意味著咱們能夠把參數“羅列”出來,而不須要顯式地構造一個數組。
  • XElement的構造函數接受的本來是XName類型參數,但字符串能夠被隱式地轉化為XName類型。
  • XElement的構造函數能夠將IEnumerable<XElement>對象轉化為獨立的元素。

但是,除了結尾一條外,其他兩個特征在F#裡都不能享用到。因此,咱們只好用命令式編程的形式編寫此類代碼。您能夠發覺,這樣的F#代碼幾乎能夠被自動轉化為Java代碼。F#在寫這樣的代碼時實在沒有優勢。

運用 DataContractSerializer

手動執行 XML序列化雖然並不難處,但是實在麻煩。這不是一種通用的做法,咱們必須為每個類型各寫一套序列化(和反序列化)邏輯,在類型字段有所改動的時刻,序列化和反序列化的邏輯還必須有所改變。就在我打算寫一個基本的,通用的XML序列化要領時,我忽然想到以前看到過的一篇文章,說是在.Net 3.0中揭曉了新的類庫:DataContractSerializer。

DataContractSerializer看似和WCF有關 ,如DataContractAttribute,DataMemberAttribute等標記最典型的作用也一直用在WCF裡。但事實上,這些類型都是解釋在System.Runtime.Serialization.dll中的,這意味著這些功能從設計之初與WCF分離開來,能夠獨立運用。那麼咱們不如嘗試一下吧:

letserialize (graph : 'a) =letserializer =newDataContractSerializer(typeof<'a>)lettextWriter =newStringWriter();letxmlWriter =newXmlTextWriter(textWriter);
    serializer.WriteObject(XMLWriter, graph)
    textWriter.ToString()

果然好用,DataContractSerializer並沒有出現XMLSerializer那樣傻乎乎地不正確。自然,與之相對的反序列化函數也很簡單寫:

letdeserialize<'a> xml =letserializer =newDataContractSerializer(typeof<'a>)lettextReader =newStringReader(xml)letxmlReader =newXmlTextReader(textReader)
    serializer.ReadObject(XMLReader) :?> 'a
試驗一下,看看成效? 

letpost =newXmlSerialization.Post()
post.Title <-"Hello"post.Content <-"World"post.Tags <- ["Hello";"World"]letxml = XmlSerialization.serialize postletpost' = XmlSerialization.deserialize<XmlSerialization.Post> XML

經歷更多試驗,我發覺 DataContractSerializer對於龐雜類型的字段也能夠正常應對,而得到這些功能也只須要在目標類型上標記一個SerializableAttribute就行了,更細節的控制也能夠議決 DataContractAttribute等執行 控制。這樣看來,XMLSerializer似乎已經能夠退出歷史舞台了?

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved