7 3 2 用 XML 表示文件

2021-05-26 04:32:06 字數 3729 閱讀 8080

7.3.2 用 xml 表示文件

xml 格式非常流行,完美地適合於儲存分層資料,比如,我們上一節中的文件。處理 xml,對於許多現實世界的應用程式,是重要的。因此,在這一節中,我們將擴充套件應用程式,以支援從 xml 檔案中載入文件。我們將使用 .net 3.5 的 linq to xml api 來做大部分的困難工作——編寫另外的 xml 解析器也沒有任何意義。linq to xml 是函式概念如何用於主流框架的很好例子:雖然,它不是純粹的函式式 api (型別一般是可變的),它允許物件以遞迴和宣告的形式構造。這可以使**的結構一目了然,所以,它比使用 dom api 的典型**更容易理解。

在某種意義上,這是資料從一種表示到另一種表示的另一種轉換。在這種情況下,源表示是 linq to xml 物件的結構, 目標是我們 7.3.1 節的文件資料型別。這次轉換變得更容易,因為,兩個資料結構是分層的。清單 7.11 演示了基於 xml 格式,我們將用它來表示我們的文件。

listing 7.11 xml representation of a sample document (xml)

in this book, we'll introduce you (...)

在看轉換的核心部分之前,我們需要實現一些工具函式,解析在 xml 中顯示的屬性值。特別是,我們需要乙個函式,解析字型名和 splitpart 的方向。清單 7.12 顯示了這些函式,引入了幾個來自 linq to xml 庫的物件。

listing 7.12 parsing font and orientation using linq to xml (f#)

open system.xml.linq

let attr(node:xelement, name, defaultvalue) =

let attr = node.attribute(xname.get(name))

if (attr <> null) then attr.value else defaultvalue

let parseorientation(node) =

match attr(node, "orientation", "") with

| "horizontal" –> horizontal

| "vertical" –> vertical

| _ -> failwith "unknown orientation!"

let parsefont(node) =

let style = attr(node, "style", "")

let style =

match style.contains("bold"), style.contains("italic") with

| true, false -> fontstyle.bold

| false, true -> fontstyle.italic

| true, true -> fontstyle.bold ||| fontstyle.italic

| false, false -> fontstyle.regular 

let name = attr(node, "font", "calibri")

new font(name, float32(attr(node, "size", "12")), style)

這段**將只使用 system.xml.dll 和 system.xml.linq.dll 程式集的引用。在 visual studio 中,通常可以從解決方案資源管理器中,用新增引用命令實現。在 f#  中,可以使用 #r"..." 指令,指定程式集的路徑作為引數值,如果程式集在全域性程式集快取(gac )中,只要指定名稱。

這個清單以 attr 函式開始,用於讀取屬性,它取乙個 xelement (linq to xml 型別表示 xml 元素)作為第乙個引數值,後面是這個屬性的名字,最後乙個引數是預設值,當該屬性缺失時使用。下乙個函式使用 attr 來讀取傳入的 xml 節點 orientation 屬性的值。如果該屬性包含意外的值,函式將使用標準的 f# failwith 函式,引發異常。

parsefont 用於把 xml 標記的屬性,像清單 7.11 中的標題,轉換成 .net 中的 font 物件。最有趣的部分是解析 style 屬性的方式,它檢查屬性值是否包含兩個字串(「bold」 和 「italic」),作為子字串,然後,使用模式匹配為四個可能的每乙個指定樣式。這個函式還將表示大小的字串轉換成數字,使用 float32 轉換函式,然後建立 font. 的例項。

現在,我們已經有了我們需要的所有的工具函式,載入 xml 文件很容易。清單 7.13 顯示了遞迴函式 loadpart,它執行完整的轉換。

listing 7.13 loading document parts from xml (f#)

let rec loadpart(node:xelement) =

match node.name.localname with

| "titled" –>

let tx =

let body = loadpart(seq.head(node.elements()))

titledpart(tx, body)

| "split" ->

let orient = parseorientation node

let nodes = node.elements() |> list.ofseq |> list.map loadpart

splitpart(orient, nodes)

| "text" ->

textpart()

| "image" ->

imagepart(attr(node, "filename", ""))

| name -> failwith("unknown node: " + name)

該函式取乙個 xml 元素作為引數值,當我們以後使用 xml 文件時,我們給它根元素。函式主體是乙個 match 結構,依據已知的選項檢查元素的名稱,如果它遇到乙個未知的標記,將引發異常。

載入影象和文字的部分很容易,因為,我們只需要使用工具函式,讀取其屬性,並建立相應的 documentpart 值。其餘兩個文件部件型別涉及遞迴,使它們變得更加有趣。

要從乙個 titled 元素建立 titledpart,我們首先解析屬性中的標題文字,然後,以遞迴方式處理部件中的第乙個 xml 元素。要讀取第乙個子元素,我們呼叫 elements() 方法,它返回所有子元素,作為乙個 .net 的 ienumerable 集合。在 f# 中,ienumerable 被簡稱為 seq

要從 split 元素建立 splitpart,我們需要解析所有的子結點,因此,我們再次呼叫 elements() 方法,但這一次,我們將結果轉換為xelement 值的函式式列表。我們以遞迴方式使用對映把每個轉換成 documentpart 值,把 loadpart 函式作為引數值。

這個函式是非常簡單的,因為,它提供幾行**,為每個受支援的標記解析 xml 節點。異常的簡單是由於這樣的事實,xml 文件是分層次的,與目標表示的方式相同。當部件有巢狀的子部件時,我們就使用遞迴。

最後,我們可以看到,應用程式如何顯示更大的文件:在 xml 編輯器中設計的文件,比在 f# 中創新值更容易。清單 7.14 顯示了用於組合到目前為止,我們已經開發的所有**的管道,成為乙個通常的 windows 窗體應用程式。

open system.windows.forms

圖 7.4 完成的應用程式,顯示更複雜的文件,有四種文件部件

我們提到過,分層次表示對於操作文件,以及執行初始化結構,都是有用的。現在就讓我們來看一看。

XML入門 XML文件規則

命名空間 xml 的能力來自它的靈活性,即您和我以及數百萬其他人可以定義我們自己的標記來描述我們的資料。記得表示個人姓名和位址的樣本 xml 文件嗎?那個文件包括表示個人尊稱的元素,這是對元素名稱非常合理的選擇。如果您經營一家網上書店,您或許會建立乙個表示書名的元素。如果您經營一家網上抵押放款公司,...

XML文件標記

xml文件中有六種標記 1 elements 最常見的標記形式,它確定它們所包圍的內容。以start tag開始,以end tag結束非空元素包含了子元素或字元資料。空元素沒有內容,能寫成以下二種形式 or 2 attributes 是出現在元素的first tag中位於元素名稱後的名稱 值對。所有...

XML文件分類

按照對xml文件規範的遵循程度,將xml文件分類三類 1.格式不良好 malformed 的xml文件。完全沒有遵守xml文件基本規則的xml文件。2.格式良好 well formed 但無效的xml文件。遵守了xml文件基本規則,但沒有使用dtd或schema定義語義約束的xml文件 使用了dtd...