Scala中处理XML文件的功能是比较强大的,因此不管怎样至少也得接触一下。XML也是Scala中的“第一类公民”,直接将一个XML格式的字符串赋值给一个val值变量就可以开始处理了。最基本的有scala.xml.Elem类中的 \ 方法和 \\ 方法,它们可用于直接对XML文件进行解析和提取。
假如有如下的XML文件:
- <symbols>
- <symbol ticker="AAPL">
- <units>200</units>
- </symbol>
- <units>300</units>
- <symbol ticker="IBM">
- <units>400</units>
- </symbol>
- </symbols>
我们可以写一个Scala脚本来提取出其中的<units>元素,顺便比较一下 \ 与 \\ 方面的区别。脚本代码如下:
- val xmlFile =
- <symbols>
- <symbol ticker="AAPL">
- <units>200</units>
- </symbol>
- <units>300</units>
- <symbol ticker="IBM">
- <units>400</units>
- </symbol>
- </symbols>
- println(xmlFile) // 直接打印xmlFragment的内容
- println(xmlFile.getClass()) // 打印xmlFragment的类型
- val unitsNodes = xmlFile \ "units" // 提取出<units>元素
- println(unitsNodes mkString "\n") // 打印提取出的结果
- println(unitsNodes getClass) // 打印symbolNodes的类型
- <symbols>
- <symbol ticker="AAPL">
- <units>200</units>
- </symbol>
- <units>300</units>
- <symbol ticker="IBM">
- <units>400</units>
- </symbol>
- </symbols>
- class scala.xml.Elem
- <units>300</units>
- class scala.xml.NodeSeq$$anon$1
我们调用单反斜杠 \ 方法很简单地就提取出了xmlFile中的<units>元素,并且打印出来的结果也跟源文件格式一致。但是,我们注意到提取出来的<units>元素仅仅是当前节点<symbols>下的<units>元素,如果我们想要同时提取出xmlFile中嵌套在<symbol>中的<units>元素,此时应该使用双反斜杠 \\ 方法来代替上面脚本中的单反斜杠,修改如下:
- val unitsNodes = xmlFragment \\ "units"
执行结果如下:
- <units>200</units>
- <units>300</units>
- <units>400</units>
- class scala.xml.NodeSeq$$anon$1
如果想要获取<units>300</units>中的数值300,则应该使用到Scala中的模式匹配,利用case语句来实现这一功能。假如我们针对上面使用了双反斜杠提取出来的unitsNodes,因为结果是一个数组,所以应该用unitsNodes(1)来获得对于<units>300</units>的引用,具体代码如下:
- unitsNodes(1) match {
- case <units>{numOfUnits}</units> =>
- println("num of units : " + numOfUnits)
- }
- num of units : 300
- println(unitNodes(1).text)
既然不能简单处理,那只能用回Scala中强大的模式匹配了。另外,如果我们想要同时提取出类似于<symbol ticker="AAPL">中的ticker属性的值,也同样使用到了模式匹配。具体脚本如下:
- xmlFile match {
- case <symbols>{allSymbol @ _*}</symbols> =>
- for(symbolNode @ <symbol>{_*}</symbol> <- allSymbol){
- println("%-7s %s".format(
- symbolNode \ "@ticker", (symbolNode \ "units").text))
- }
- }
在这里,<symbols>{allSymbol @ _*}</symbols> 中的@符号用于声明定义一个临时变量用来引用<symbols>…</symbols>中的所有内容(即下划线加星号 _*,在这个例子中就是所有<symbol>…</symbol>元素对了),包括其他节点元素。
在Scala的模式匹配case语句中,使用特殊符号@可以定义临时变量,例如for表达式,如下。真是强大啊!那么接下来的这一句:
- for(symbolNode @ <symbol>{_*}</symbol> <- allSymbol)
中的symbolNode 也容易理解了,而 <- allSymbol 不就是 for 表达式中的迭代赋值吗?最后,println()中使用了正则表达式以及格式化输出format方法。
那么,for表达式中的symbolNode 与 allSymbols 是同样类型的(allSymbols迭代地赋值给 symbolNode 嘛),所以每一个 symbolNode 其实也就是每一个<symbol>…</symbol>元素对了,看看文章最前面给出的XML文件,<symbol>内是包含了 ticker 属性的,如下:
- <symbol ticker="AAPL">
- <units>200</units>
- </symbol>
那么我们使用symbolNode \ "@ticker" 这种格式将ticker属性的值提取出来。而接下来的 (symbolNode \ "units").text 我们都知道是对当前的<symbol>元素对往下找出<units>元素对,再用text方法取出其值。
注:小心看清楚脚本代码中的括号对等等哦!^_^
执行结果如下: