4

我正在尝试替换 XML 片段,并且需要一个累加器。假设我有一个以 XML 格式存储的填空问题,如下所示:

val q = <text>The capitals of Bolivia are <blank/> and <blank/>.</text>

在某些时候,我想要将这些空白转换为 HTML 输入元素,并且我需要能够区分第一个和第二个,以便我可以检查它们。(忽略这样一个事实,在这种情况下,两个大写字母可以按任意顺序出现——这是我稍后会处理的一个令人头疼的问题。)

感谢 StackOverflow 上的一些可爱的答案,我产生了以下解决方案:

import scala.xml._
import scala.xml.transform._

class BlankReplacer extends BasicTransformer {
  var i = 0

  override def transform(n: Node): NodeSeq = n match {
    case <blank/> => {
      i += 1
      <input name={ "blank.%d".format(i) }/>
    }
    case elem: Elem => elem.copy(child=elem.child.flatMap(transform _))
    case _ => n
  }
}

这工作得相当好。new BlankReplacer()每次我想开始重新编号时,我都必须创建一个,但它非常有效:

scala> new BlankReplacer()(q)
res6: scala.xml.Node = <text>The capitals of Bolivia are <input name="blank.1"></input> and <input name="blank.2"></input>.</text>

这是问题。有没有一种简单的方法可以避免每次替换 a 时我必须做的突变<blank/>?我所拥有的并没有让我觉得可怕,但我认为如果我BlankReplacer每次必须将问题转换为 HTML 时都没有创建该类的新实例,这可能会更清晰。我确信有某种方法可以将它变成一个累加器,但我不知道该怎么做。

谢谢!托德

4

2 回答 2

4

这正是Anti-XML的拉链旨在解决的问题

换句话说,我们从一棵 XML 树开始,我们使用选择器深入到该树中,我们通过一些修改派生了该结果集的新版本(在我们的例子中,是一个新属性),现在我们想要返回到我们最初拥有的树上,除了我们在肠子里做的修改。这就是拉链的用途...

在您的情况下,您可以执行以下操作:

import com.codecommit.antixml._

def replaceBlanks(el: Elem) = {
  var i = 0
  (el \\ "blank").map { _ =>
    i += 1
    <input name={"blank.%d".format(i)}/>.convert
  }.unselect
}

或者您可以避免在此答案var中使用技巧:

def replaceBlanks(el: Elem) = {
  val blanks = el \\ "blank"

  (0 until blanks.size).foldLeft(blanks) {
    case (z, i) => z.updated(i, z(i).copy(
      name = "input",
      attrs = Attributes("name" -> "blank.%d".format(i + 1)))
    )
  }.unselect
}

现在我们可以将该方法应用于您的元素(将其转换为 之后com.codecommit.antixml.Elem):

scala> println(replaceBlanks(q.convert))
<text>The capitals of Bolivia are <input name="blank.1"/> and <input name="blank.2"/>.</text>

诀窍在于,我们可以\\像使用.scala.xmlscala.xmlunselect

于 2012-06-21T16:54:23.557 回答
1

Scales Xml提供折叠路径,允许您“修改”一棵树并积累......

import scales.utils._
import ScalesUtils._
import scales.xml._
import ScalesXml._

// the xml example
val q = <("text") /( "The capitals of Bolivia are ", <("blank")," and ",<("blank"),".")

// which elems to fold on?
val p = top(q) \\* "blank"

val f = foldPositions(p, p.size){ // size is used as the starting point for the fold
  case (question, path) =>
(question - 1, Replace( <("input") /@ ("name" -> ("blank."+question) )) )
  }

// f is an either, so we assuming its working here, and ._1 is the accumalator, _.2 the Path
val newTree = f.left.get._2.tree

唯一的怪癖是它以相反的文档顺序累积,还有一个非累积版本。这允许在某些具有破坏性的转换组合中(例如更改子子项,然后在另一个转换中删除它,所有这些都可以正常工作)。

折叠本身的输入是任何 Iterable 的路径,只要它们在同一棵树中,允许您根据需要组合查询。

有关如何在 Scales 中折叠 Xml 的更多详细信息,请参见此处

于 2012-06-23T09:34:30.583 回答