1
import scala.xml._
import scala.xml.transform.{RewriteRule, RuleTransformer}

object TransformIssue {
  def addNewElement(): RewriteRule = new RewriteRule {
    override def transform(n: Node): Seq[Node] = n match {
      case <element></element> => <element><new></new></element>
    }
  }

  def addThingElement(): RewriteRule = new RewriteRule {
    override def transform(n: Node): Seq[Node] = n match {
      case <element>{ children@_*}</element> => <element>{ children  }</element>
      case <new></new> => <new><thing></thing></new>
    }
  }

  def change(node: Node): Node =
    new RuleTransformer(
      addNewElement(),
      addThingElement()
    ).transform(node).head

  def changeWorkaround(node: Node): Node = {
    val out1 = new RuleTransformer(
      addNewElement()
    ).transform(node).head

    new RuleTransformer(
      addThingElement()
    ).transform(out1).head
  }

}

--

import org.scalatest.{FlatSpec, FunSpec}
import org.scalatest._

class TransformIssueSpec extends FlatSpec with Matchers {

  it should "apply transform to created elements" in {
    val output = TransformIssue.change(<element></element>)
    output should be(<element><new><thing></thing></new></element>)
  } // fails

  it should "work the same as the workaround imo" in {
    TransformIssue.change(<element></element>) should equal(TransformIssue.changeWorkaround(<element></element>))
  } // fails

}

当我们使用两个重写规则应用变换时:第一个添加新元素,第二个将子元素添加到新元素;那么第二个重写规则与第一个规则中添加的元素不匹配。

当我们在两个单独的 RuleTransformer 中应用相同的 RewriteRules 时,它确实会将子代添加到第一步中添加的元素中。我们希望changechangeWorkaround函数产生相同的输出。

Scala xml 中提出的问题

4

1 回答 1

1

你没有把它应用到孩子身上。

  def addThingElement(): RewriteRule = new RewriteRule {
    override def transform(n: Node): Seq[Node] = n match {
      case <element>{ children@_*}</element> => <element>{ transform(children)  }</element>
      case <new></new> => <new><thing></thing></new>
    }
  }

这样可行。

所以这是交易: on BasicTransformer,def transform(n: Node): Seq[Node]适用def transform(ns: Seq[Node]): Seq[Node]于 的所有子n节点,后一种方法将前者应用于每个节点。

RuleTransformer覆盖前一个方法,然后调用它,然后RewriteRule在结果上应用,所以它递归地工作。

这太令人困惑了,我花了一段时间才将代码重新跟踪到我从中回忆的内容。这里是:

  1. RuleTransformer Node => Seq[Node] 调用 super (BasicRewrite)
  2. BasicRewrite Node => Seq[Node] 在子节点上调用 Seq[Node] => Seq[Node]
  3. BasicRewrite Seq[Node] => Seq[Node] 在每个节点上调用 Node => Seq[Node]
  4. 由于 Node => Seq[Node] 被覆盖,如果 Seq 不为空,它将递归回 1
  5. RuleTransformer 现在按顺序应用每个 RewriteRule。

那么,在损坏的情况下,它将如下所示:

RuleTransformer.transform(<element/>)
BasicRewrite.transform(<element/>)
BasicRewrite.transform(Seq.empty)
addNewElement(<element/>)
addThingElement(<element><new/></element>)

在工作情况下,它将像这样:

RuleTransformer.transform(<element/>)
BasicRewrite.transform(<element/>)
BasicRewrite.transform(Seq.empty)
addNewElement(<element/>)
RuleTransformer.transform(<element><new/></element>)
BasicRewrite.transform(<element><new/></element>)
BasicRewrite.transform(Seq(<new/>))
RuleTransformer.transform(<new/>)
BasicRewrite.transform(<new/>)
BasicRewrite.transform(Seq.empty)
addThingElement(<new/>)
addThingElement(<element><new><thing/></new></element>)
于 2020-03-20T21:28:04.780 回答