1

为了测试在 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>

因此,在倒数第二个命令中,将大写应用于所有目标文本元素,但在退出拉链后,四个元素中只有两个被转换。我已经尝试过,<hi/>而不是<hi>these</hi>然后those被大写。知道这里有什么问题吗?

我正在为 Scala 2.10.3 使用 arktekk.no fork。

4

1 回答 1

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>

当您使用选择*器选择树中的所有元素时,您会在结果集中获得aand b Elema第二个浅选择器只查看or的直接子级b并获取Text值。所以我们fooabarb

修改后,第一个unselect包含个人Elems 及其更新:

<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>)。由于没有通用的方法来确定哪个列表更好(它们都同时更新),因此选择取决于实现。在这种情况下,它采用来自树中更深层次的修改。

您可以通过不选择Elems 并Text直接修改节点来解决此问题,从而避免任何可能的冲突(因为它们只能发生在Elems 上)。所以你写:

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用于此特定情况的自定义合并策略;它将设法以某种方式消除子列表的不同部分的歧义。即使可能,我怀疑这是否值得。

于 2014-02-27T18:20:44.333 回答