如何用XPath匹配前后元素均满足条件的XML元素
XPath筛选相邻元素问题排查与修正
原始XML文档
<root> <tok lemma="per" xpos="SPS00">per</tok> <tok lemma="els" xpos="L3CP0">los</tok> <tok lemma="qual" xpos="PR0CP000">quals</tok> <tok lemma="ser" xpos="VSIP3P0">són</tok> <tok lemma="digne" xpos="AQ0CP00">dignes</tok> <tok lemma="de" xpos="SPC00">de</tok> <tok lemma="gloriós" xpos="AQ0FS00">gloriosa</tok> <tok lemma="memòria" xpos="NCFS000">memòria</tok> <tok xpos="CC" lemma="i">e</tok> <tok lemma="li" xpos="L3CSD" >li</tok> <tok lemma="plàcia" xpos="VMSP3S0">plàcia</tok> <tok lemma="molt" xpos="RG">molt</tok> <tok lemma="per" xpos="SPS00">per</tok> </root>
尝试的XPath表达式
//tok[starts-with(@xpos, "L")]/preceding-sibling::tok[1][not(starts-with(@xpos, "V"))]/following-sibling::tok[1][not(starts-with(@xpos, "V"))]
预期与实际结果
- 预期结果:仅匹配
<tok lemma="els" xpos="L3CP0">los</tok>(即前后相邻元素的xpos都不以V开头、自身xpos以L开头的tok) - 实际结果:匹配到两个元素:
<tok lemma="els" xpos="L3CP0">los</tok> <tok lemma="li" xpos="L3CSD">li</tok>
原XPath错误原因
原表达式的筛选逻辑链条完全偏离了需求:它先找xpos以L开头的节点,取其前一个符合条件的兄弟节点,再从这个前兄弟节点取后一个符合条件的节点。这里的后节点判断是基于前兄弟的视角,完全没检查原L开头节点的后兄弟属性。
比如对<tok lemma="li" xpos="L3CSD">li</tok>来说,它的前兄弟xpos不以V开头,从该前兄弟取后一个节点就是li本身,此时判断的是li自己的xpos而非它的后兄弟,所以错误被选中。
修正后的XPath
直接对目标节点同时校验前后相邻兄弟的属性,表达式如下:
//tok[ starts-with(@xpos, "L") and not(starts-with(preceding-sibling::tok[1]/@xpos, "V")) and not(starts-with(following-sibling::tok[1]/@xpos, "V")) ]
逻辑说明
这个表达式对每个tok做三重校验:
- 当前节点
xpos以L开头; - 前一个相邻兄弟节点的
xpos不以V开头; - 后一个相邻兄弟节点的
xpos不以V开头。
执行后只会匹配到<tok lemma="els" xpos="L3CP0">los</tok>,完全符合预期。
内容的提问来源于stack exchange,提问作者jfontana
相关产品推荐
相关产品推荐

