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

4
<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. 当任何祖先是结果的一部分时,排除该节点

因此,在我的示例中,我想获取和。节点被排除在外,因为它是的子节点,而是结果的一部分。
我已经从条件(1)开始使用此表达式//*[count(./text()[normalize-space()])>0]。它返回、、和。我不知道如何排除。
1个回答

9
我会把这个分成两步来完成。 首先,只考虑条件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和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中,每个外部谓词([])依次表示条件1、2和3。


1
这里可能有一个小错别字:ancestor*[text()[normalize-space()]],但除此之外它完美地工作了,谢谢。 - Iale

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接