XPath - 获取具有条件的文本节点的父节点

时间:2015-05-21 10:31:16

标签: xpath

<doc ok="yes">
    <a>
        <b>
            <c>
                aa
                <d ok="yes">
                    bb
                </d>
                cc
            </c>
        </b>
    </a>
    <e>
        ee
    </e>
    <f ok="no">
        no
    </f>
</doc>

我需要使用XPath检索节点列表,其中每个节点必须满足以下条件:

  1. 节点至少有一个子文本节点

  2. 如果节点(或祖先轴中最近的节点)具有属性"ok",则值必须为"yes"

  3. 当任何祖先是结果的一部分时,排除节点

  4. 因此,在我的示例中,我想获得<c><e>。节点<d>被排除,因为它是<c>的子节点,它是结果的一部分。

    我已使用此表达式//*[count(./text()[normalize-space()])>0]启动条件(1)。它会返回<c><d><e><f>。我不知道如何排除<d>

1 个答案:

答案 0 :(得分:8)

我会把这分为两步。 首先,只考虑条件编号1和2。

//*[text()[normalize-space()]]
   [
      ancestor-or-self::*[not(@ok)] 
        or 
      ancestor-or-self::*[@ok][1][@ok='yes']
    ]

将XML视为输入,xpath上方返回3个元素:<c><d><e>

下一步将实现条件号3.可以通过重复第一步中使用的相同谓词来完成,但现在用于ancestor::*而不是当前节点。然后使用not()否定重复的谓词,因为我们希望祖先在1和1的条件下失败。 2(我们希望当前节点的祖先不是结果的一部分):

[not(
        ancestor::*[text()[normalize-space()]]
        [
            ancestor-or-self::*[not(@ok)] 
                or 
            ancestor-or-self::*[@ok][1][@ok='yes']
        ]
    )
]

将两个步骤组合在一起,您将获得以下xpath:

//*[text()[normalize-space()]]
   [
      ancestor-or-self::*[not(@ok)] 
        or 
      ancestor-or-self::*[@ok][1][@ok='yes']
    ]
    [not(
            ancestor::*[text()[normalize-space()]]
            [
                ancestor-or-self::*[not(@ok)] 
                    or 
                ancestor-or-self::*[@ok][1][@ok='yes']
            ]
        )
    ]

最终xpath中的每个外部谓词([])按顺序表示条件no 1,2和3.