在Scala中查找特定标签的所有节点并替换它们

时间:2014-01-20 12:23:57

标签: xml scala

我有一个XML节点:

<a>
    <b>
        <c>
            foo
        </c>
    </b>
    <b2>
           bar
   </b2>
</a>

我需要以递归方式遍历每个孩子并找到一个标签,比如说b,并将其替换为另一个带有其他文字的标签<b3>baz</b3>,以便最终结果如下:

<a>
    <b3>
           baz
    </b3>
    <b2>
           bar
   </b2>
</a>

我怎样才能实现这一目标?

2 个答案:

答案 0 :(得分:2)

使用标准库没有任何好的方法可以做到这一点,但是类似下面的方法会起作用:

def replaceAllBs(elem: Elem): Elem = elem.copy(
  child = elem.child.map {
    case elem: Elem if elem.label == "b" => <b3>baz</b3>
    case elem: Elem => replaceAllBs(elem)
    case other => other
  }
)

即,我们通过树来检查每个元素是否为b,如果是,则替换它,如果不是则继续使用它。

这是zippers的一个非常好的用例,旨在使更新不可变数据结构更加优雅。例如,使用Anti-XML(遗憾的是不再维护),您可以编写以下内容:

(elem \\ "b").map(_ => <b3>baz</b3>.convert).unselect.head

您在树中进行选择,进行一些更改,然后使用unselect移回到顶部。如果您对这种方法感兴趣,Scales XML会得到积极维护并提供另一个Scala XML拉链实现(尽管它在语法上比Anti-XML更笨重)。

答案 1 :(得分:0)

天真的递归实现:

import scala.xml.{Elem, Node}

def replace(xml: Node)(p: Node => Boolean)(elem: Node): Node = xml match {
    case x: Node if p(x) => elem
    case Elem(prefix, label, attribs, scope, child @ _*) =>
        Elem(prefix, label, attribs, scope, child.map(replace(_)(p)(elem)): _*)
    case x: Node => x
}

val xml = <a><k/><b><c>text</c></b></a>
replace(xml)(_.label == "c")(<z>TEXT</z>)