本节书摘来自华章社区《Clojure数据分析秘笈》一书中的第1章,第1.7节将XML数据读入Incanter数据集,作者(美)Eric Rochester,更多章节内容可以访问云栖社区“华章社区”公众号查看
1.7 将XML数据读入Incanter数据集
一类非常常用的数据格式是XML,人们对其褒贬不一。但在某种情况下,几乎所有人都不得不处理它。Clojure可以使用Java的XML库,但它也有自己的包,这个包提供了一种在Clojure中使用XML的更自然的方式。
1.7.1 准备工作
首先,在Leiningen project.clj文件中引入以下依赖:
1.7.3 实现原理
本方法按以下顺序处理XML:
- 解析XML数据文件。
- 利用解析树抽取数据节点。
- 将节点转换成代表数据的映射序列。
- 最后,将其转换至Incanter数据集。
load-xml-data实现了这个过程。其中包括三个参数:输入文件名、传入解析完成的XML的根节点并返回第一个数据节点的函数,以及传入一个数据节点并返回下个数据节点或空值(如果其后没有节点)的函数。
首先,函数解析XML文件并将之装入zipper(将在后面的章节详细讨论zipper),然后利用传入的两个函数将数据节点抽取成一个序列。对于每个数据节点,获取其子节点并将其转换成一系列“标签名/内容”对。每个数据节点的对转换成一个映射,然后映射序列转换至Incanter数据集。
1.7.4 更多信息
本方法中使用了一对有趣的数据结构或概念。这两种数据结构在函数式编程语言或者Lisp中都很常见,但是都未出现在主流的编程语言中,接下来深入学习一下。
利用zipper浏览结构
解析完成后的XML文件需要作为参数传入clojure.zip/xml-zip。这将使用Clojure本身的XML数据结构并将其转换成可以用如clojure.zip/down和clojure.zip/right之类的命令进行快速浏览。作为一门函数式编程语言,Clojure使用不可变数据结构;而zipper提供了一种浏览、修改类树结构的高效、自然方法,例如XML文档。
zipper非常实用且有趣,理解它们有助于理解如何使用不可变数据结构。如需关于zipper更详细的信息,Clojure关于其的文档会有帮助(http://clojure-doc.org/articles/tutorials/parsing_xml_with_zippers.html)。但是如果很喜欢刨根问底,参考杰拉德·休伊特的论文“The Zipper”(http://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf)。
流水线处理
可以使用->>宏来以流水线的方式展示处理过程。对于深度嵌套的函数调用,这个宏需要从右往左阅读,这使得处理过程的数据流和转换序列更清晰。
在Clojure中可以执行流水线处理是因为它的宏系统。->>仅将函数调用重写成Clojure本身的嵌套的格式,与格式读入的方式一样。传入宏的第一个参数将作为下一个表达式的最后一个参数插入。数据结构则插入第三个表达式作为最后一个参数,等等,直到这种格式结束。也就是说,以(->> x first (map length) (apply +))表达式开始。接下来是Clojure构建最终表达式的一系列中间步骤(需要整合的元素在每个阶段都会高亮显示)。
- (->> x first (map length) (apply +))
- (->> (first x) (map length) (apply +))
- (->> (map length (first x)) (apply +))
- (apply + (map length (first x)))
比较XML和JSON
XML和JSON(在1.4节中提到)非常相似。可以证实的是,JSON的流行很大程度上是被对XML冗长特性的醒悟所驱动的。
当在Clojure中处理这些格式的数据时,最大的不同在于JSON数据是直接转换成对应数据内容的Clojure内部数据结构,例如映射和向量。然而对于XML而言,XML被读入至反映XML结构的记录类型,而不是反映数据结构的记录类型。
换句话说,JSON中的映射的键值来自域,例如,来自first_name或者age。然而,XML中映射的键值来自数据格式、标签、属性或者子节点,也就是说,标签和属性名来自域。这额外的一层抽象使得XML更加不灵活。