xml文档,7.3.2 用 XML 表示文档

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)
font="Cambria" size="18" style="bold">


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 = { 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("Unknown node: " + name)
该函数取一个 XML 元素作为参数值,当我们以后使用 XML 文档时,我们给它根元素。函数主体是一个 match 结构,依据已知的选项检查元素的名称,如果它遇到一个未知的标记,将引发异常。
加载图像和文本的部分很容易,因为,我们只需要使用工具函数,读取其属性,并创建相应的 DocumentPart 值。其余两个文档部件类型涉及递归,使它们变得更加有趣。
要从一个 titled 元素创建 TitledPart,我们首先解析属性中的标题文本,然后,以递归方式处理部件中的第一个 XML 元素。要读取第一个子元素,我们调用 Elements() 方法,它返回所有子元素,作为一个 .NET 的 IEnumerable 集合。在 F# 中,IEnumerable 被简称为 seq<'a,我们可以使用来自 Seq 模块的函数来处理,它类似于处理列表的函数。在我们的示例中,我们使用 Seq.head,它返回集合中的第一个元素(头)。如果我们在 C# 中写这个代码,可以调用 Elements().First() 来达到相同的效果。
要从 split 元素创建 SplitPart,我们需要解析所有的子结点,因此,我们再次调用 Elements() 方法,但这一次,我们将结果转换为XElement 值的函数式列表。我们以递归方式使用映射把每个转换成 DocumentPart 值,把 loadPart 函数作为参数值。
这个函数是非常简单的,因为,它提供几行代码,为每个受支持的标记解析 XML 节点。异常的简单是由于这样的事实,XML 文档是分层次的,与目标表示的方式相同。当部件有嵌套的子部件时,我们就使用递归。
最后,我们可以看到,应用程序如何显示更大的文档:在 XML 编辑器中设计的文档,比在 F# 中创新值更容易。清单 7.14 显示了用于组合到目前为止,我们已经开发的所有代码的管道,成为一个通常的 Windows 窗体应用程序。
Listing 7.14 Putting the parts of the application together (F#)
open System.Windows.Forms
[]
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 显示运行的结果。
image7.3.2 用 XML 表示文档xml文档
图 7.4 完成的应用程序,显示更复杂的文档,有四种文档部件
我们提到过,分层次表示对于操作文档,以及执行初始化结构,都是有用的。现在就让我们来看一看。
Tags:  什么是xml文档 xml文档的结构 xml文档

延伸阅读

最新评论

发表评论