我们下载函数的结果是字符串,因此,需要将字符串解析为 XML 文档。由于这个操作我们将经常要用,所以,要写一个简单的打包函数,用 worldBankDownload 下载数据,以 XDocument 对象的形式返回结果。下载异步运行,因此,我们使用异步工作流实现这个函数:
let worldBankRequest(props) = async {
let! text = worldBankDownload(props)
return XDocument.Parse(text) }
代码首先使用 let! 执用异步下载;完成之后,解析 XML 数据,并返回 XDocument 对象。使用 Async.RunSynchronously 进行下载,可以使用上一节的辅助函数,查询返回的 XML 文档。清单 13.9 显示了一个这样的示例,下载了有关国家的汇总信息,然后访问一些值,我们稍后需要。
清单 13.9 浏览地区信息 (F# Interactive)
> let doc =
worldBankRequest(["countries"], ["region", "NA"])
|> Async.RunSynchronously;;
val doc : XDocument = (...)
> let c = doc |> xnested ["countries"; "country" ];; [1]
val c : XElement
> c |> xattr "id";; [2]
val it : string = "EAP"
> c |> xelem "name" |> xvalue;; [3]
val it : string = "East Asia & Pacific"
我们首先访问返回文档中的第一个 country 元素,它是根元素 countries 的子元素。遍历 XML 树,我们需要使用 xnested 函数[1],指定要选择元素的路径。
现在,我们可以看一下元素的内容,准备提取哪些信息。首先,我们演示如何获取地区的 ID,它保存在 id 属性中,因此,我们使用 xattr 函数读取[2];我们还需要地区的名字,使显示数据的格式更友好,这个值在 name 元素中。
浏览了结构以后,可以保证我们知道对于一个地区来说,如何访问所有需要的地区信息,然后,再遍历所有地区。清单 13.10 使用相同的函数,但用在序列计算中。
清单 13.10 创建地区信息的序列 (F# Interactive)
> let regions =
seq { let countries = doc |> xnested [ "rsp"; "countries" ]
for country in countries |> xelems "country" do [1]
yield country |> xelem "name" |> xvalue };; [2]
val regions : seq<string * string> = seq
[ ("East Asia & Pacific";
("Europe & Central Asia";
("European Monetary Union";
("Heavily indebted poor countries (HIPC)"; ...]
与前面的列表相比,只有一个重要变化,就是现在能够处理数据中的所有 country 节点了。我们使用 xelems 函数[1],访问序列中的元素,用 for 循环进行迭代。因为使用了序列表达式,所以,能够使用 yield 关键字生成结果元素。我们使用了在清单 13.9 中尝试过的部分代码,得到国家的名字,再以序列的形式返回元素[2]。
在本节,我们已经知道如何得到地区信息的列表,作为进一步的研究使用。重要的不是使用的代码,而在于通常和处理过程。创建辅助函数便于数据访问,通过交互方式提取信息,便于了解文档的结构,然后,再把代码打包成函数。下一步,我们将下载需要的指标,比如,地区的森林覆盖情况。