我正在学习 Scala,并且希望更新某些 XML 中的嵌套节点。我有一些工作,但我想知道它是否是最优雅的方式。
我有一些 xml:
val InputXml : Node =
<root>
<subnode>
<version>1</version>
</subnode>
<contents>
<version>1</version>
</contents>
</root>
我想更新子节点中的版本节点,而不是内容中的节点。
这是我的功能:
def updateVersion( node : Node ) : Node =
{
def updateElements( seq : Seq[Node]) : Seq[Node] =
{
var subElements = for( subNode <- seq ) yield
{
updateVersion( subNode )
}
subElements
}
node match
{
case <root>{ ch @ _* }</root> =>
{
<root>{ updateElements( ch ) }</root>
}
case <subnode>{ ch @ _* }</subnode> =>
{
<subnode>{ updateElements( ch ) }</subnode>
}
case <version>{ contents }</version> =>
{
<version>2</version>
}
case other @ _ =>
{
other
}
}
}
这个函数有没有更简洁的写法?
最佳答案
一直以来,没有人真正给出最合适的答案!不过,现在我已经了解了它,这是我对它的新看法:
import scala.xml._
import scala.xml.transform._
object t1 extends RewriteRule {
override def transform(n: Node): Seq[Node] = n match {
case Elem(prefix, "version", attribs, scope, _*) =>
Elem(prefix, "version", attribs, scope, Text("2"))
case other => other
}
}
object rt1 extends RuleTransformer(t1)
object t2 extends RewriteRule {
override def transform(n: Node): Seq[Node] = n match {
case sn @ Elem(_, "subnode", _, _, _*) => rt1(sn)
case other => other
}
}
object rt2 extends RuleTransformer(t2)
rt2(InputXml)
现在,进行一些解释。 RewriteRule
类是抽象类。它定义了两个方法,都称为 transform
。其中一个采用单个 Node
,另一个采用 Node
的 Sequence
。它是一个抽象类,所以我们不能直接实例化它。通过添加一个定义,在本例中覆盖了其中一个 transform
方法,我们正在创建它的一个匿名子类。每个 RewriteRule 需要关注一个任务,尽管它可以做很多。
接下来,RuleTransformer
类将可变数量的 RewriteRule
作为参数。它的转换方法采用 Node
并返回 Node
的 Sequence
,通过应用每个用于实例化的 RewriteRule
这两个类都派生自 BasicTransformer
,它定义了一些不需要在更高层次上关注的方法。不过,它是 apply
方法调用 transform
,因此 RuleTransformer
和 RewriteRule
都可以使用与其关联的语法糖。例子中前者有,后者没有。
这里我们使用两级 RuleTransformer
,第一级将过滤器应用于更高级别的节点,第二级将更改应用于通过过滤器的任何内容。
同样使用提取器Elem
,这样就不用关心命名空间,有没有属性等细节了。并不是说元素 version
的内容被完全丢弃并替换为 2
。如果需要,它也可以匹配。
另请注意,提取器的最后一个参数是_*
,而不是_
。这意味着这些元素可以有多个子元素。如果您忘记了 *
,匹配可能会失败。在示例中,如果没有空格,匹配不会失败。因为空格被翻译成 Text
元素,所以 subnode
下的单个空格会导致匹配失败。
此代码比其他建议大,但它的优点是对 XML 结构的了解比其他建议少得多。它会更改任何名为 version
的元素,无论有多少层 - 一个名为 subnode
的元素,无论 namespace 、属性等。
此外...好吧,如果你有很多转换要做,递归模式匹配很快就会变得不屈服。使用 RewriteRule
和 RuleTransformer
,您可以有效地将 xslt
文件替换为 Scala 代码。
关于xml - Scala - 修改 xml 中的嵌套元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/970675/