2012-06-21 57 views
4

我試圖替換一些XML,並且需要一個累加器。說我有一個填充式的空白存儲爲XML,像這樣的問題:在Scala中修改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

假設你的問題不包含'''%''',看到https://gist.github.com/2965695(這太可怕了,我知道:-) – opyate

+0

其實,這是相當聰明,但恐怕很可能我們在某個時候需要百分號。 – TOB

+0

是的,你可以計算'%d'的實例,但是我的午餐時間用完了:-) – opyate

回答

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,只要它們在同一棵樹中,允許您按照您認爲合適的方式組合查詢。

See here for more details on how to Fold Xml in Scales

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 
} 

,也可以使用的伎倆避免varthis 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

+0

這很酷。我特別喜歡沿着樹的方向,然後使用地圖的能力。但它仍然有變異,對吧? – TOB

+0

不,沒有突變拉鍊是專門設計用於在純功能環境中更新更方便。 –

+0

我正在談論'var i'和'i + = 1'部分的變化。 – TOB