修改XML Scala中无突变?(Modify XML in Scala without mutat

2019-09-18 08:22发布

我试图取代XML块,需要沿途的蓄电池。 说我有一个填充式的空白存储为XML,像这样的问题:

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

在某些时候,我会想将这些空白转换成HTML输入元素,我需要能够区分第一和第二,所以我可以检查它们。 (忽略的是,在这种情况下,两国首都可以以任何顺序出现的事实 - 这是一个头痛的,我会处理后)

由于在计算器上一些可爱的回答,我产生了以下解决方案:

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/> 我有什么不罢工我可怕,但我认为这可能是清洁的,如果我没有创建的新实例BlankReplacer每次我有一个问题转换为HTML时类。 我敢肯定有某种方式把它变成一个蓄电池,但我不知道该怎么做。

谢谢! 托德

Answer 1:

秤的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

唯一的怪癖是,它积聚在反向文档顺序,也有不可累积版本。 这允许变换的组合时,有些是破坏性的(例如改变subchild,然后删除它在另一个转型都只是工作)。

输入的褶皱本身就是路径的可迭代的,只要他们在同一棵树,让您查询,您认为合适的结合。

看到这里,了解如何能够折叠秤XML的更多细节



Answer 2:

这恰恰是那种这个问题反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.xml ,但不像scala.xml我们可以进行修改,由此产生的“节点集”(实际上是一个拉链),然后把它们放回其原来的环境中使用unselect



文章来源: Modify XML in Scala without mutation?
标签: xml scala