为了测试一个可以转换XML文档中Text元素的方法,我编写了两个非常简单的选择器,并在生成的Zipper上应用了map / toUpperCase。结果应该是除了通过第一个选择器排除的文本元素之外的所有文本元素都将转换为大写。但它只适用于最下面的Text元素。这是代码:
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>
因此,在倒数第二个命令中,大写适用于所有目标Text元素,但在拉出拉链后,只转换了四个元素中的两个。我已使用<hi/>
代替<hi>these</hi>
进行了尝试,然后those
获得了大写。知道这里有什么问题吗?
我正在使用scala 2.10.3的arktekk.no fork。
答案 0 :(得分:1)
您遇到的问题来自取消选择过程中的合并冲突。
为了简化您的问题,我将使用以下数据:
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>
当您使用*
选择器选择树中的所有元素时,您会在结果集中获得a
和b
Elem
。第二个浅选择器仅查看a
或b
的直接子项,并获取Text
值。我们从foo
获得a
,从bar
获得b
。
修改后,第一个unselect
包含个人Elem
及其更新内容:
<a>FOO<b>bar</b></a>
<b>BAR</b>
现在,下一个unselect
需要将b
合并回a
以形成a
的新版本。当前版本的b
意味着新的a
,以便:
<a>foo<b>BAR</b></a>
在您的冲突中,您可以a
与孩子List(FOO, <b>bar</b>)
或孩子List(foo, <b>BAR</b>)
。
由于没有通用的方法来确定哪个列表更好(它们都同时更新),因此选择取决于实现。在这种情况下,它需要来自树中更深层次的修改。
您可以通过不选择Elem
并直接修改Text
节点来解决此问题,从而避免任何可能的冲突(因为它们只能在Elem
上发生)。所以你写道:
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
定义用于此特定情况的自定义合并策略;将以某种方式消除儿童名单不同部分的歧义。即使可能,我怀疑它是否值得付出努力。