Scala XML解析库

这篇博客介绍了Scala中XML的处理,包括XML节点类型、加载与保存文件、查找节点和属性、遍历以及创建XML的方法。详细讲解了如何使用Scala内置的XML支持进行各种操作,如通过模式匹配查找节点、获取属性等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

XML 解析

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

XML节点类型

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

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

如下所示:

def prefix: String = null
def child: Seq[Node]
def attributes: MetaData = Null
def label: String
override 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 = Test
scala> 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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值