2014-02-27 38 views
1

要測試可以轉換XML文檔中的Text元素的方法,我編寫了兩個非常簡單的選擇器,並在生成的Zipper上應用map/toUpperCase。結果應該是除了通過第一個選擇器排除的那些文本元素之外的所有文本元素都被轉換爲大寫。但它只適用於最深的文本元素。下面的代碼:反XML:使用自定義選擇器的Zipper上的某些修改不會在取消選擇後保留

scala> import com.codecommit.antixml._ 
import com.codecommit.antixml._ 

scala> val elemSelector = Selector({case x:Elem if x.name != "note" => x}) 
elemSelector: com.codecommit.antixml.Selector[com.codecommit.antixml.Elem] = <function1> 

scala> val textSelector = Selector({case x:Text => x}) 
textSelector: com.codecommit.antixml.Selector[com.codecommit.antixml.Text] = <function1> 

scala> val xml = XML.fromString("<tei><div><p>this<note>not<foreign lang=\"greek\">that</foreign>not</note></p><p>those<hi>these</hi></p></div></tei>") 
xml: com.codecommit.antixml.Elem = <tei><div><p>this<note>not<foreign lang="greek">that</foreign>not</note></p><p>those<hi>these</hi></p></div></tei> 

scala> val zipper = xml \\ elemSelector \ textSelector 
zipper: com.codecommit.antixml.Zipper[com.codecommit.antixml.Text] = thisthatthosethese 

scala> val modified = zipper.map(t => new Text(t.text.toUpperCase)) 
modified: com.codecommit.antixml.Zipper[com.codecommit.antixml.Text] = THISTHATTHOSETHESE 

scala> val result = modified.unselect.unselect 
result: com.codecommit.antixml.Zipper[com.codecommit.antixml.Node] = <tei><div><p>this<note>not<foreign lang="greek">THAT</foreign>not</note></p><p>those<hi>THESE</hi></p></div></tei> 

因此,在倒數第二個命令,上殼適用於所有有針對性的文字內容,但走出的拉鍊後,只有兩個的四個要素的轉化。我試過<hi/>而不是<hi>these</hi>,然後those得到大寫。任何想法這裏有什麼問題?

我使用的是Scala 2.10.3的arktekk.no fork。

回答

1

您遇到的問題來自unselection過程中的合併衝突。當您選擇樹中的所有元素與*選擇

val textSelector = Selector { case x: Text => x } 

val xml = XML.fromString("<root><a>foo<b>bar</b></a></root>") 

val zipper = xml \\ * \ textSelector 

val modified = zipper.map(t => t.copy(text = t.text.toUpperCase)) 

val result = modified.unselect.unselect 
// => <root><a>foo<b>BAR</b></a></root> 

你得到abElem S:

只是爲了簡化您的問題一點點,我會用以下數據你的結果集。第二個淺選擇器僅查看ab的直接子項,並採用Text值。所以我們從abarb得到foo

的修改後第一次unselect所包含的各個Elem s的他們的更新:

<a>FOO<b>bar</b></a> 
<b>BAR</b> 

現在,下unselect需要合併ba,形成a一個新版本。的b當前版本意味着新a使得:

<a>foo<b>BAR</b></a> 

而且還有你的衝突,你可以有a與孩子List(FOO, <b>bar</b>)或與孩子List(foo, <b>BAR</b>)。 由於沒有通用的方法來確定哪個列表更好(它們都是同時更新的),所以選擇取決於實現。在這種情況下,它需要修改來自樹中較深層次的內容。

您可以通過不選擇Elem和直接修改Text節點來解決此問題,從而避免任何可能的衝突(因爲它們只能在Elem s上發生)。所以你寫:

val zipper = xml \\ textSelector 

val modified = zipper.map(t => t.copy(text = t.text.toUpperCase)) 

val result = modified.unselect 
// => <root><a>FOO<b>BAR</b></a></root> 

如果這不是你的使用情況的選擇,有可能定義一個定製的合併策略unselect用於此特定情況;一個將設法消除兒童名單的不同部分。即使可能,我懷疑這是否值得付出努力。

+0

感謝您的指點。我看了一下合併策略,然後按照你的建議不要浪費太多時間。我最終編寫了一個SAX解析器。 – fbaumgardt