我正在尝试替换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>
这是问题所在。有没有一种简单的方法可以避免每次更换<blank/>
时我必须做的突变?我所拥有的并不会让我觉得太可怕,但我认为如果我每次将问题转换为HTML时都没有创建BlankReplacer
类的新实例,那么这可能会更清晰。我确定有办法把它变成累加器,但我不知道该怎么做。
谢谢! 托德
答案 0 :(得分:4)
这正是Anti-XML的拉链designed to solve所带来的问题:
换句话说,我们从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
使用this answer中的技巧:
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.xml
一样,但与scala.xml
不同,我们可以对生成的“节点集”进行修改(实际上是一个拉链),然后使用unselect
将它们放回原来的上下文中。
答案 1 :(得分: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
唯一的怪癖是它以反向文档顺序累积,还有一个非累积版本。当某些变形具有破坏性时,这允许变换的组合(例如,更改子变量然后在另一个变换中删除它只是起作用)。
折叠本身的输入是任何可路径的路径,只要它们在同一个树中,允许您根据需要组合查询。