在 Scala 中修改 XML 而不发生突变? [英] Modify XML in Scala without mutation?

查看:28
本文介绍了在 Scala 中修改 XML 而不发生突变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to replace pieces of XML and need an accumulator along the way. Say I have a fill-in-the-blank question stored as XML like so:

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

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

At some point I'm going to want to turn those blanks into HTML input elements and I need to be able to distinguish the first and second so I can check them. (Ignore the fact that, in this case, the two capitals can appear in either order--that's a headache I'll deal with later.)

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

Thanks to some lovely answers on StackOverflow, I produced the following solution:

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(),但它几乎有效:

and this works reasonably well. I have to create a new BlankReplacer() each time I want to start re-numbering, but it pretty much works:

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 类的新实例,这可能会更清晰.我确信有某种方法可以将它变成累加器,但我不确定如何去做.

Here's the question. Is there an easy way to avoid the mutation I have to do each time I replace a <blank/>? What I have doesn't strike me as horrible, but I think this could be cleaner if I weren't creating a new instance of the BlankReplacer class every time I had to convert a question to HTML. I'm sure there's some way to turn this into an accumulator, but I'm not sure how to do it.

谢谢!托德

推荐答案

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

Scales Xml provides folding over paths allowing you "modify" a tree and to accumulate...

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

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

The only quirk is that it accumulates in reverse document order, there is also a non-accumulative version. This allows for combinations of transformations when some are destructive (for example changing a subchild and then deleting it in another transformation all just works).

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

The input to the fold itself is any Iterable of Paths, as long as they are in the same tree, allowing you to combine queries as you see fit.

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

这篇关于在 Scala 中修改 XML 而不发生突变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆