7.3.2 用 XML 表示文档

前端之家收集整理的这篇文章主要介绍了7.3.2 用 XML 表示文档前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

7.3.2 用 XML 表示文档

XML 格式非常流行,非常适合于保存分层次的数据,比如,上一节的文档。如何处理 XML,对于许多实际应用非常重要,因此,在这一节,我们要扩展我们的应用程序,以支持从 XML 文件加载文档。我们将使用.NET 3.5 的 LINQ to XMLAPI 完成大部分的困难工作,自己再写另外的 XML 解析器没有任何意义。LINQto XML 是函数概念应用于主流框架的很好示例:虽然它不是纯粹的函数式 API(类型一般是可变的),但是对象能够以递归和声明的形式构造,这就使代码的结构一目了然,因此,比使用 DOM API 的传统代码更容易理解。

在某种意义上,这是数据从一种表示到另一种表示的再次转换。这次,源表示是 LINQ to XML 对象的结构,目标是我们 7.3.1 节的文档数据类型。这次的转换更容易,因为两种数据结构都是分层次的。清单 7.11 是用 XML 格式表示的文档。

清单 7.11 XML 表示的样本文档 (XML)

<titled title="FunctionalProgramming for the Real World"

font="Cambria"size="18" style="bold">

<splitorientation="vertical">

<imagefilename="C:\Writing\Functional\Cover.jpg" /> | 把子部分保存为

<text>In thisbook,we'll introduce you (...)</text> | 嵌套的 XML 元素

</split>

</titled>

在看到转换的核心之前,我们需要实现一些工具函数,解析在 XML 中显示的特性值;特别是,我们需要解析字体名和 SplitPart方向的函数。清单 7.12 显示了这些函数,并引入了几个LINQ to XML 库的对象。

清单 7.12 使用 LINQ to XML 解析字体和方向 (F#)

open System.Xml.Linq

let attr(node:XElement,name,defaultValue)= [1]

let attr =node.Attribute(XName.Get(name))

if (attr <> null) thenattr.Value else defaultValue <-- 如果没有,就返回默认值

let parSEOrientation(node) = [2]

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

| "horizontal" –>Horizontal

| "vertical" –>Vertical

| _ -> failwith "Unknownorientation!" <-- 抛出异常

let parseFont(node) = [3]

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

let style =

matchstyle.Contains("bold"),style.Contains("italic") with

| true,false ->FontStyle.Bold

| false,true ->FontStyle.Italic

| true,true ->FontStyle.Bold ||| FontStyle.Italic<-- 组合两个 .NET 枚举值

| 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#Interactive 中,可以使用 #r"..." 指令,参数值指定程序集的路径,如果程序集在全局程序集缓存(GAC)中,只要指定名称就行了。

清单开头的 attr 函数[1],用于读特性,第一个参数是 XElement(LINQ to XML 类型,表示 XML 元素),后面特性的名字,最后一个参数是默认值,当没有这个特性时使用。第二个函数[2]使用 attr 读取传入的 XML 节点中 orientation 特性的值。如果特性包含的不是希望的值,函数将使用标准的 F#failwith 函数,抛出异常。

parseFont [3]用于把 XML 标记的特性,像清单 7.11中的标题,转换成 .NET 中的 Font 对象。最有意义的部分是解析 style 特性,检查特性值是否包含两个字符串(bold 和 italic),作为子字符串,然后,使用模式匹配为四种可能性中的每一个指定样式。这个函数还使用 float32 转换函数,把表示大小的字符串转换成数字,然后创建 Font 实例。

我们已经有了需要的所有工具函数,因此,加载 XML 文档就很容易了。清单7.13 显示了递归函数loadPart,实现全部转换。

清单 7.13从 XML 中加载文档 (F#)

let rec loadPart(node:XElement) =

match node.Name.LocalName with [1]

| "titled" –>

let tx = { Text =attr(node,"title",""); Font = parseFont node}

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({Text =node.Value; Font = parseFont node})

| "image" ->

ImagePart(attr(node,"filename",""))

| name -> failwith("Unknownnode: " + name) [2]

函数的参数为 XML 元素,我们在以后使用时,我们把 XML 文档的根元素给它。函数主体是一个 match构造[1],依据已知的选项检查元素的名称,如果遇到未知的标记[2],就抛出异常。

加载图像和文本部分很容易,因为我们只要使用工具函数,读取特性,并创建相应的 DocumentPart 值。其余两个文档部分类型涉及递归,因此更重要。

要从 titled 元素创建 TitledPart,首先要解析标题文本的特性,然后,递归处理这部分中的第一个 XML 元素。读取第一个子元素,我们调用 Elements() 方法,它以 .NET IEnumerable 集合的形式返回所有子元素。在 F# 中,IEnumerable<T> 简称为 seq<'a>,因此,可以使用 Seq 模块中的函数来处理,它类似于处理列表的函数。在我们的示例中,我们使用Seq.head,返回集合中的第一个元素(头)。如果我们用 C# 写代码,可以调用Elements().First() 来达到同样的效果

从 split 元素创建 SplitPart,需要解析所有子元素,因此,我们再次调用 Elements() 方法,但这一次,我们把结果转换为XElement 值的函数式列表。我们递归地把每个元素转换成 DocumentPart 值,使用映射,把 loadPart 函数作为参数值。

这个函数非常简单,因为它只用几行代码,就能为每个受支持标记解析 XML 节点。之所以这样简单,是因为 XML 文档的分层次,与目标表示形式的方式相同,这样,一个部分有嵌套的子部分时,能够使用递归。

最后,我们会看到应用程序如何显示大文档:在 XML 编辑器中设计文档,比在 F#中创建值更容易。清单7.14 显示了最后的组装,把目前为止我们已经开发的所有代码组合成通常的 Windows 窗体应用程序。

清单 7.14 把应用程序的所有部分组织到一起 (F#)

open System.Windows.Forms

[<System.STAThread>]

do

let doc =loadPart(XDocument.Load(@"..\..\document.xml").Root)

let bounds = { Left = 0.0f; Top =0.0f; Width = 520.0f; Height = 630.0f }

let parts = documentToScreen(doc,bounds)

let img = drawImage (570,680) 25.0f(drawElements parts)

let main = new Form(Text ="Document",BackgroundImage = img,

ClientSize = Size(570,680))

Application.Run(main)

代码首先使用XDocument 类,从 XML 文件加载文档。我们把文档的根元素传递给 loadPart 函数,进行文档分层次表示的转换;接下来,我们使用documentToScreen,将它转换成平面表示,然后,使用我们在清单 7.8 中看到的代码,绘制并显示文档。我们还添加了 STAThread 特性,这是独立的 Windows 窗体应用程序所必需的。最后一行,用 Application.Run 方法,启动应用的程序。图 7.4 显示运行的结果。

图 7.4 最终完成的应用程序,显示更复杂的文档,有四种文档部分

我们提到过,文档的分层次表示对于文档的初始构造和操作,都是有用的。现在,我们就来看一看。

原文链接:https://www.f2er.com/xml/297639.html

猜你在找的XML相关文章