Scala XML解析库

来源:互联网 发布:公安网络安全保密协议 编辑:程序博客网 时间:2024/05/22 11:52

XML 解析

Scala标准库中内置了XML支持,XML相关类在包scala.xml中。

XML节点类型

Node是最基础的XML节点类型(抽象类)。
Node类型是NodeSeq的子类,而NodeSeq继承自Seq[Node],用于记录节点的序列。
Node类型定义了一系列用于获取节点信息的方法:

  • prefix成员方法,用于获取当前节点的标签前缀
  • child成员方法(抽象方法),用于获取子节点序列
  • attributes成员方法,用于获取当前节点属性
  • label成员方法(抽象方法),用于获取当前节点标签名称
  • text成员方法,用于获取当前节点文本内容

如下所示:

def prefix: String = nulldef child: Seq[Node]def attributes: MetaData = Nulldef label: Stringoverride def text: String = super.text

Node类型的伴生对象中定义了提取器,可以用于提取节点中的标签名称属性子节点等内容:

def unapplySeq(n: Node) = Some((n.label, n.attributes, n.child))

Elem类型继承于Node类型,实现了Node类型中的抽象内容。
有如下测试XML文件:

<!-- FileName: Example.xml --><root>    <node arg="arg_node">        <node1 arg="arg_node1">node1</node1>        <node1 argOne="node1_arg_one" argTwo="node1_arg_two">test_node1</node1>    </node>    <node><node2>node2</node2></node>    <node>        <node3 arg="arg_node3">node3</node3>        <node4 arg="arg_node4">node4</node4>    </node></root>

加载与保存XML文件

加载和保存XML文件可以使用XMLLoader特质以及继承于XMLLoader[Elem]的单例对象XML

  • XMLLoader的实例方法loadFile()可以从指定路径加载XML文件进行解析,方法返回由输入XML文件生成的Elem节点对象。
  • XML对象的方法save()write()可用于XML节点(Node类型)保存到文件中。
  • save()方法接收文件路径(String类型)作为参数,大部分参数带有默认值。
  • write()接收java.io.Writer类型作为参数,参数没有默认值。

查找节点和节点属性

NodeSeq类提供了\()\\()等方法用于节点的查找,继承于NodeSeq类的NodeElem等类型都可以使用这些方法进行节点查找。

查找节点

\()以及\\()方法签名类似,接收节点名称作为参数(String类型),返回节点序列(NodeSeq类型)。

  • \()方法返回当前节点下一级子节点中指定名称节点的序列。
  • \\()方法返回当前节点所有子节点中指定名称节点的序列。
  • 使用loadFile()方法加载XML文件后,返回的Elem类型的当前节点为根节点
  • 节点查找支持使用模式匹配的方式。
  • 使用模式匹配方式查找节点时,匹配表达式中的节点标签不能带有属性(不支持)。

节点属性

节点属性内容可以直接从节点中获取,也可以通过查找获取属性内容。

  • 使用\()\\()方法同样可以进行属性查找,需要在属性名字符串前加上@字符表示搜索的内容为属性,如\("@num")表示查找名称为num的属性内容。
  • 在使用\()方法查找属性时,查找的的范围不是子节点的属性,而是当前节点的属性。
  • 可以直接使用\@()方法在当前子节点中进行属性查找,直接使用属性名作为参数,无需再添加@字符。
  • 还可以使用attribute()以及attributes()方法从节点中获取属性。

遍历节点

Elem类型的成员字段child保存了子节点的序列(Seq[Node]类型),可以通过for循环语句进行遍历:

import scala.xml._object Main extends App {    val xmlFile = XML.loadFile("Example.xml")    val getChild: Node => Unit = rootNode => for (node <- rootNode.child)        node match {            //如果只需要节点文本,可以将表达式嵌在匹配语句中            case <node1>{ text }</node1> => println("Node1 text: " + text)            //支持多级标签匹配            case <node><node2>{ text }</node2></node> => println("Case node_node2: " + text)            //如果需要整个节点的内容,需要使用@符号            case n @ <node2>{ _ }</node2> => println("Node2 text: " + n.text)            //使用attribute()或者attributes()方法获取节点的属性            case n @ <node3>{ _ }</node3> => println("Node3 attribute: " + n.attribute("arg").get)            case n @ <node4>{ _ }</node4> => println("Node4 attribute: " + n.attributes("arg"))            //匹配其它类型节点,也可以写成 case _ if node.child.length > 0 => ...            case _ if node.child != null => getChild(node)        }    getChild(xmlFile)}

遍历节点同样可以使用高阶函数,以上代码等价于:

import scala.xml._object Main extends App {    val xmlFile = XML.loadFile("Example.xml")    val getChild: Node => Unit = rootNode => rootNode.child foreach {        node => node match {            case <node1>{ text }</node1> => println("Node1 text: " + text)            case <node><node2>{ text }</node2></node> => println("Case node_node2: " + text)            case n @ <node2>{ _ }</node2> => println("Node2 text: " + n.text)            //若仅需要属性的内容,可以直接在模式匹配表达式中获取属性(n为Node类型)            case <node3>{ n @ _ }</node3> => println("Node3 attribute: " + n.text)            //若需要从模式匹配表达式中获取多个属性,则可以写成(n为Seq[Node]类型)            case <node4>{ n @ _* }</node4> => println("Node4 attribute: " + n(0).text)            //匹配其它类型节点,也可以写成 case _ if node.child.length > 0 => ...            case _ if node.child != null => getChild(node)        }    }    getChild(xmlFile)}

输出结果:
Node1 text: node1
Node1 text: test_node1
Case node_node2: node2
Node3 attribute: arg_node3
Node4 attribute: arg_node4

创建XML

可以直接将代码嵌入XML语句中:

scala> val str = "Test"str: String = Testscala> val node0 = <li>{ str }</li>             //xml节点内容可以插入变量,使用花括号区分表达式与xml本身内容node0: scala.xml.Elem = <li>Test</li>scala> val node1 = <li name={ str }>test</li>   //xml属性中插入变量node1: scala.xml.Elem = <li name="Test">test</li>

可以将复杂的表达式在XML语句中进行多重嵌套

scala> val node3 = <ul>{ for (i <- 1 to 3) yield <li>{ i }</li> }</ul>node3: scala.xml.Elem = <ul><li>1</li><li>2</li><li>3</li></ul>

在Scala中,节点是不可变的,拼接节点的正确方式是使用Elem类型的cospy()方法,并在复制时重新设定child参数。
copy()方法的定义如下所示:

def copy(    prefix: String = this.prefix,    label: String = this.label,    attributes: MetaData = this.attributes,    scope: NamespaceBinding = this.scope,    minimizeEmpty: Boolean = this.minimizeEmpty,    child: Seq[Node] = this.child.toSeq): Elem = Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*)

使用copy()方法拼接节点如下所示:

//使用具名参数指定子节点内容scala> node3.copy(child = node0 ++ node1)res0: scala.xml.Elem = <ul><li>Test</li><li name="Test">test</li></ul>//保留原节点的内容进行拼接scala> node3.copy(child = node3.child ++ node0 ++ node1)res1: scala.xml.Elem = <ul><li>1</li><li>2</li><li>3</li><li>Test</li><li name="Test">test</li></ul>//创建新节点时也可以设定其它属性,如标签名、标签前缀等scala> node3.copy(child = node0 ++ node1, prefix = "Test", label = "test")res2: scala.xml.Elem = <Test:test><li>Test</li><li name="Test">test</li></Test:test>
0 0
原创粉丝点击