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