当父节点在XML文档中不是唯一时,如何将节点添加到XML?

时间:2017-01-27 23:54:38

标签: xml scala

考虑这个例子

import scala.xml.{Node, Elem}
import scala.xml.transform.{RewriteRule, RuleTransformer}

val doc: Elem =
  <root>
    <level1>
      <Weekly>
        <Properties>
          <Property key="Sun"/>
          <Property key="Mon"/>
          <Property key="Tue"/>
        </Properties>
      </Weekly>
    </level1>
    <level2>
      <Monthly>
        <Properties>
          <Property key="Jan"/>
          <Property key="Feb"/>
          <Property key="Mar"/>
        </Properties>
      </Monthly>
    </level2>
  </root>

val wednesday = <Property key="Wed"/>

final class AddNewDayRule extends RewriteRule {
  override def transform(n: Node): Seq[Node] = n match {
    case e: Elem if e.label == "Properties" => e.copy(child = e.child ++ wednesday)
    case e => e
  }
}

val addWed = new RuleTransformer(new AddNewDayRule).transform(doc).head

结果是

addWed: scala.xml.Node = <root>
    <level1>
      <Weekly>
        <Properties>
          <Property key="Sun"/>
          <Property key="Mon"/>
          <Property key="Tue"/>
        <Property key="Wed"/></Properties>
      </Weekly>
    </level1>
    <level2>
      <Monthly>
        <Properties>
          <Property key="Jan"/>
          <Property key="Feb"/>
          <Property key="Mar"/>
        <Property key="Wed"/></Properties>
      </Monthly>
    </level2>
  </root>

正如您所看到的,<Property key="Wed"/>中添加了Monthly属性,因为Properties不是唯一的。

我还尝试限制传递给transform

的XML文档部分
val addWed = new RuleTransformer(new AddNewDayRule).transform(doc \\ "Weekly").head

但是,我得到的是

addWed: scala.xml.Node = <Weekly>
        <Properties>
          <Property key="Sun"/>
          <Property key="Mon"/>
          <Property key="Tue"/>
        <Property key="Wed"/></Properties>
      </Weekly>

而不是整个文件。

解决这个问题的最佳方法是什么?

更新
我尝试用通用路径解决这个问题,我的尝试在

之下
final class AddNewNodeRule(expectedPath: Array[String], newNode: Node) extends RewriteRule {
  private var index = 0
  private val currentPath = Array[String]()
  // only one transformation. If multiple, take expectedCount as input
  var transformed = false

  override def transform(ns: Seq[Node]): Seq[Node] = ns match {
    case e: Node if index < expectedPath.length && e.label == expectedPath(index) =>
      currentPath(index) = e.label
      index += 1
      println(s"i.inc = $index")
      e
    case e: Node if index == expectedPath.length - 1
      && (expectedPath sameElements currentPath) && !transformed =>
      transformed = true
      println(s"i.done = $index")
      //e.copy(child = e.child ++ newNode)
      e
    case other: Node =>
      println(s"i.other = $index, label=${other.label}")
      other
  }
}

val wednesday = <Property key="Wed"/>
val expectedPath = Array("level1", "Weekly", "Properties")
new RuleTransformer(new AddNewNodeRule(expectedPath, wednesday)).transform(doc).head

但现在它失败了

wednesday: scala.xml.Elem = <Property key="Wed"/>
expectedPath: Array[String] = Array(level1, Weekly, Properties)
i.other = 0, label=#PCDATA
i.other = 0, label=#PCDATA
i.other = 0, label=#PCDATA
i.other = 0, label=#PCDATA
i.other = 0, label=Property
i.other = 0, label=#PCDATA
i.other = 0, label=Property
i.other = 0, label=#PCDATA
i.other = 0, label=Property
i.other = 0, label=#PCDATA
i.other = 0, label=Properties
i.other = 0, label=#PCDATA
i.other = 0, label=Weekly
i.other = 0, label=#PCDATA
java.lang.ArrayIndexOutOfBoundsException: 0
    at #worksheet#.AddNewNodeRule.transform(scratch_13.scala:36)
    at scala.xml.transform.RuleTransformer.$anonfun$transform$1(scratch_13.scala:11)
    at scala.collection.IndexedSeqOptimized.foldLeft(scratch_13.scala:52)
    at scala.collection.IndexedSeqOptimized.foldLeft$(scratch_13.scala:60)
    at scala.collection.mutable.WrappedArray.foldLeft(scratch_13.scala:30)
    at scala.xml.transform.RuleTransformer.transform(scratch_13.scala:11)
    at scala.xml.transform.BasicTransformer.$anonfun$transform$2(scratch_13.scala:31)
    at scala.collection.TraversableLike.$anonfun$flatMap$1(scratch_13.scala:237)
    at scala.collection.mutable.ResizableArray.foreach(scratch_13.scala:55)
    at scala.collection.mutable.ResizableArray.foreach$(scratch_13.scala:48)
    at scala.collection.mutable.ArrayBuffer.foreach(scratch_13.scala:44)
    at scala.collection.TraversableLike.flatMap(scratch_13.scala:237)
    at scala.collection.TraversableLike.flatMap$(scratch_13.scala:234)
    at scala.collection.AbstractTraversable.flatMap(scratch_13.scala:100)
    at scala.xml.transform.BasicTransformer.transform(scratch_13.scala:31)
    at scala.xml.transform.BasicTransformer.transform(scratch_13.scala:41)
    at scala.xml.transform.RuleTransformer.transform(scratch_13.scala:11)
    at #worksheet#.#worksheet#(scratch_13.scala:54)

我不确定下一步会怎么做

1 个答案:

答案 0 :(得分:0)

像这样更新RewriteRule

final class AddNewDayRule extends RewriteRule {
  override def transform(n: Node): Seq[Node] = n match {
    case e: Elem if e.label == "Weekly" => e.copy(
      child = e.child map {
        case child: Elem =>
          if (child.label == "Properties") {
            child.copy(child = child.child ++ wednesday)
          } else {
            child
          }
        case other => other
      }
    )
    case e => e
  }
}

首先查找父节点Weekly,然后查找Properties的{​​{1}}子节点,然后更新其子节点。