Scala:修改NodeSeq

par*_*rsa 6 xml scala

我有这样的NodeSeq:

<foo>
<baz><bar key1="value1" key2="value2">foobar</bar></baz>
Blah blah blah
<bar key1="value3">barfoo</bar>
</foo>

我想为所有bars'属性添加一个新属性.我现在正在做:

   val rule = new RewriteRule() {
     override def transform(node: Node): Seq[Node] = {
       node match {
          case Elem(prefix, "bar", attribs, scope, content@_*)  => Elem(prefix, "bar", attribs append Attribute(None, "newKey", Text("newValue"), scala.xml.Null) , scope, content:_*)
          case other => other
       }
     }
   }
Run Code Online (Sandbox Code Playgroud)

但问题是它只适用于1个节点.我希望它以递归方式处理所有节点,如果我在for循环中调用转换,我不能用新值替换它们,因为它们变得不可变.我怎么解决这个问题?

Knu*_*daa 3

这是您自己的解决方案的简化版本(使用 Daniel 的匹配逻辑变体):

def updateBar(node: Node): Node = node match {
    case elem @ Elem(_, "bar", _, _, child @ _*) => elem.asInstanceOf[Elem] % Attribute(None, "newKey", Text("newValue"), Null) copy(child = child map updateBar)
    case elem @ Elem(_, _, _, _, child @ _*) => elem.asInstanceOf[Elem].copy(child = child map updateBar)
    case other => other
}
Run Code Online (Sandbox Code Playgroud)

请注意,此代码与原始代码之间的主要区别在于,此代码从外部处理节点如下所示,我在第一个答案中添加了一些打印语句:

scala> updateBar(<foo><bar>blabla</bar></foo>)
processing '<foo><bar>blabla</bar></foo>'
processing '<bar>blabla</bar>'
processing 'blabla'
result: 'blabla'
result: '<bar newKey="newValue">blabla</bar>'
result: '<foo><bar newKey="newValue">blabla</bar></foo>'
res1: scala.xml.Node = <foo><bar newKey="newValue">blabla</bar></foo>
Run Code Online (Sandbox Code Playgroud)

虽然您的原始代码是从内到外工作的(简化示例):

scala> xf { <a><b><c/></b></a> }
transforming '<c></c>'
result: '<c></c>'
transforming '<b><c></c></b>'
result: '<b><c></c></b>'
transforming '<a><b><c></c></b></a>'
result: '<a><b><c></c></b></a>'
res4: scala.xml.Node = <a><b><c></c></b></a>
Run Code Online (Sandbox Code Playgroud)

在某些情况下,这两种技术可能会产生不同的结果。

另一个区别是匹配代码稍微冗长:您需要一种情况来实际转换相关元素,并需要一种情况来递归处理子节点。不过,所示的示例可能需要稍微重构一下。

  • 不,这是因为 RuleTransformer 从每个最里面的元素开始转换。(或者看起来是这样,我没有查看源代码,只是查看了行为。)如果您更改此特定示例中的案例顺序,例如将第二个案例放在顶部,则“栏”将永远不会匹配。 (2认同)