Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Xml Scala:修改NodeSeq_Xml_Scala - Fatal编程技术网

Xml Scala:修改NodeSeq

Xml Scala:修改NodeSeq,xml,scala,Xml,Scala,我有这样一个问题: foobar 废话废话 巴福 我想向所有bars'属性添加一个新属性。我目前正在做: val rule = new RewriteRule() { override def transform(node: Node): Seq[Node] = { node match { case Elem(prefix, "bar", attribs, scope, content@_*) => Elem(prefix, "ba

我有这样一个问题:


foobar
废话废话
巴福

我想向所有
bar
s'属性添加一个新属性。我目前正在做:

   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
       }
     }
   }

但问题是它只在1个节点上工作。我希望它在所有节点上递归工作,如果我在
for
循环中调用转换,我不能用新值替换它们,因为它们变得不可变。我怎样才能解决这个问题呢?

这个坏男孩完成了任务:

def updateVersion( node : Node ) : Node = node match {
         case <foo>{ ch @ _* }</foo> => <foo>{ ch.map(updateVersion )}</foo>
         case <baz>{ ch @ _* }</baz> => <baz>{ ch.map(updateVersion ) }</baz>
         case Elem(prefix, "bar", attribs, scope, content@_*)  => Elem(prefix, "bar", attribs append Attribute(None, "key3", Text("value3"), scala.xml.Null) , scope, content:_*)
         case other @ _ => other
       }
def updateVersion(节点:节点):节点=节点匹配{
案例{ch@*}=>{ch.map(updateVersion)}
案例{ch@*}=>{ch.map(updateVersion)}
案例要素(前缀“bar”、属性、范围、,content@_*)=>Elem(前缀“bar”、attribs append属性(无、“key3”、文本(“value3”)、scala.xml.Null)、范围、内容:*)
案例其他@\=>其他
}
试试看


您的原始代码似乎是正确的。问题不在于它不能递归地工作(确实如此),而是当只有一个现有属性时会出现一个奇怪的问题

请看以下内容,除了我添加了一些用于调试的打印语句外,它们基本上与您的代码相同:

val rule = new RewriteRule() {
   override def transform(node: Node): Seq[Node] = {
        println("transforming '" + node + "'")
        val result = node match {
            case elem @ Elem(prefix, label @ "bar", attribs, scope, children @ _*)  => 
                Elem(prefix, label, attribs append Attribute(None, "newKey", Text("newValue"), Null), scope, children: _*)          
            case other => other
        } 
        println("result: '" + result + "'")
        result
   }
}

object xf extends RuleTransformer(rule) 
现在我们测试它:

scala> xf { <bar/> }
transforming '<bar></bar>'
result: '<bar newKey="newValue"></bar>'
transforming '<bar></bar>'
result: '<bar newKey="newValue"></bar>'
res0: scala.xml.Node = <bar newKey="newValue"></bar>
scala>xf{}
转化“
结果:“”
转化“
结果:“”
res0:scala.xml.Node=
我们看到,对于没有属性的元素,转换会导致添加新属性,并且返回的结果也是正确的。(我不知道为什么转换会发生两次。)

但是,当存在现有属性时:

scala> xf { <bar key1="value1"/> }
transforming '<bar key1="value1"></bar>'
result: '<bar key1="value1" newKey="newValue"></bar>'
res1: scala.xml.Node = <bar key1="value1"></bar>
scala>xf{}
转化“
结果:“”
res1:scala.xml.Node=
转换的结果是正确的,但不会传播到最终结果

但当存在两个(或更多)现有属性时,一切正常:

scala> xf { <bar key1="value1" key2="value2"/> }
transforming '<bar key1="value1" key2="value2"></bar>'
result: '<bar key2="value2" key1="value1" newKey="newValue"></bar>'
transforming '<bar key1="value1" key2="value2"></bar>'
result: '<bar key2="value2" key1="value1" newKey="newValue"></bar>'
res2: scala.xml.Node = <bar key2="value2" key1="value1" newKey="newValue"></bar>
scala>xf{}
转化“
结果:“”
转化“
结果:“”
res2:scala.xml.Node=

我很想相信这是库中的一个bug。

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

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

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>
scala>updateBar(blabla)
处理“blabla”
处理“blabla”
处理“blabla”
结果:“布拉布拉”
结果:“布拉布拉”
结果:“布拉布拉”
res1:scala.xml.Node=blabla
虽然您的原始代码是由内而外工作的(简化示例):

scala>xf{}
转化“
结果:“”
转化“
结果:“”
转化“
结果:“”
res4:scala.xml.Node=
在某些情况下,这两种技术可能会产生不同的结果


另一个区别是匹配的代码稍微详细一些:您需要一种情况用于相关元素的实际转换,另一种情况用于递归处理子节点。不过,所示的示例可能需要进行一些重构。

另请参见我的实际解决方案是该线程中答案的修改版本,非常感谢。在转换中包含不相关的元素可能不是一个好做法。@Knut:我想知道是否可以将前两个案例扩展到任何标记,但我失败了:
case Elem(prefix,tag,attribs,scope,content)=>Elem(prefix,tag,attribs,scope,content.map(updateVersion))
给出编译器错误,因为Elem的最后一个参数应该是节点而不是节点seq。也许我应该迭代节点seq。这不起作用。1)在调用copy或%之前,需要将Elem强制转换为Elem。编译器抱怨没有在节点上定义副本。2) 儿童是未定义的,您似乎正在尝试做的事情并不是让它递归工作所必需的。如果你删除副本和中间的案例,它的工作原理与OP的相同。@Knut我的错误。
@
,而不是
子项
。孩子们的问题是Scala的库错误,我的大脑拒绝接受。:-)你需要做“这个转换元素。孩子”。不过,这实际上是所提到的两种不同技术(由内而外/由外而内)的结合。它是有效的(尽管它触发了我在回答中描述的bug),但它会将整个树转换两次,这是不必要的。(据我所知,我可能错了,这以前发生过。)很好。我会说“由内而外”或“由外而内”取决于“案例”的顺序。不,这是因为RuleTransformer从每个最里面的元素开始转换。(或者看起来,我没有看源代码,只是看行为。)如果在这个特定示例中更改案例的顺序,例如,将第二个案例放在顶部,则“条”将永远不会匹配。
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
}
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>
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>