XPath选择器,排除具有特定属性值的元素

7

我在这里发布了我的第一篇帖子 - 这是一个很棒的网站,我一定会尽我所能回馈社区。

我看到了许多关于以下问题的不同表现形式; 然而,我的尝试解决似乎没有起作用。

考虑这棵简单的树:

<root>
    <div>
        <p>hello</p>
        <p>hello2</p>
        <p><span class="bad">hello3</span></p>
    </div>
</root>

我希望能够编写一个XPath表达式,可以选择“div”的所有子节点,除了那些具有“class”属性等于“bad”的元素。

以下是我尝试过的内容:

/root/div/node()[not (@class='bad')]

然而这似乎并不起作用。

我在这里缺少什么?

谢谢,
艾萨克


@Isaac:这个测试 not(@class='bad') 对于你所有的 div 子元素都是真的。 - user357812
xpath选择节点,而不是“移除”它们。 - Steven D. Majewski
/root/div/node() 将返回所有 <p> 节点以及它们之间的空白文本节点。您是否想选择最后一个 <p>?如果您想选择最后一个 <p>,但不包括其内容(<span class="bad">...</span>),那么这不是 XPath 选择问题:您需要使用 XSLT 进行过滤。 - Steven D. Majewski
在XSLT中,最好的方法是使用身份模板并为*[class='bad']添加一个特殊的空产生式。 - Steven D. Majewski
@Steven D. Majewski:除了问题不清楚之外,您可以使用XPath表达式选择任何节点,例如/root/div//*[not(@class='bad')],它选择所有没有'class'属性值为'bad'的后代元素。 - user357812
显示剩余2条评论
4个回答

3
当您在提供的XML文档中测试XPath时,使用这里提供的工具,XPath似乎确实选择了所有没有属性class="bad"的子节点 - 这些是文档中的所有<p>元素。
请注意,唯一具有此类属性的子节点是<span>,它确实没有被选中。
您是否希望不选择包围spanp节点?

沙洛姆奥德,有趣的链接-谢谢。现在,我上面给出的示例是我正在使用的一个巨大XML树的最小表示。尝试使用Java执行相同的XPath表达式似乎不起作用。应该选择“p”元素;实际上,我想要的只是将那个“span”元素扔掉。我应该进一步调查为什么我的XPath表达式对我的特定问题不起作用;如果我找到任何有用的东西,我会在这里发布。谢谢! - Isaac
@Isaac:我们试图告诉你,span元素永远不会被选中,因为它不是div的子元素,而是p的子元素。 - user357812
我认为你的第一句话中有一个错别字,因为这个“Child nodes means all child nodes, whatever their depth, not just direct descendants”是错误的。 - user357812
@Alejandro:嗯,当然。好发现。不敢相信因为一个斜杠的缺失浪费了这么多时间...谢谢! - Isaac
FYI:链接似乎已经失效。 - Jean-Francois T.

1
我一直在编写一个Java程序中使用XPath。如果您想选择没有class="bad"的节点(即节点,但不是周围的

节点),您可以使用以下代码:

/root/div/descendant::*[not (@class='bad')]

Otherwise, if you want to select the

nodes that don't have a child with class='bad', you can use something like the following:

/root/div/p/*[not (@class='bad')]/..

the .. part selects the immediate parent node.


0

身份转换只是匹配和复制所有内容:

<xsl:template match="@*|node()" >
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

但是你可以添加一个空的转换,更具体地匹配你想要排除的模式:

 <xsl:template match="span[@class='bad']" />

(如果您想更明确地指定优先级,也可以添加一个 priority 属性。)


0
欢迎来到 SO,Isaac! 我会尝试这个:
/root/div/*[./*[@class != "bad"]]

这应该选择 div 元素的所有子元素 (*),这些子元素没有后代元素具有等于 badclass 属性。

编辑:

根据 @Alejandros 的评论:

/root/div/*[not(*/@class "bad")]

@FK82:我认为你尝试的更合适的路径是/root/div/*[not(*/@class = "bad")]。请注意,对于那些没有@class属性的元素,@class != "bad"将会是false... - user357812
我尝试过使用/root/div/node()[not (descendant-or-self::*[@class="bad"])],当时我认为这就是他想要的,但是根据他在上面的评论,他似乎是想包含包含<span class="bad">的外部<p>,但是过滤掉内部的<span class="bad">。 - Steven D. Majewski
@Steven D. Majewski:这根本没有任何意义。我想就没什么可补充的了。 - FK82
@FK82:再次强调,对于那些没有@class属性的元素,“@class and @class != 'bad'”将会是false,但你只想排除那些@class等于“bad”的元素。所以,对于那些你想要排除的元素,“@class='bad'”是true,然后对于那些你想要保留的元素,“not(@class='bad')”是true:对于节点集比较,“!=”和“=”运算符是不可逆的。最后,用“.”开始一个相对路径表达式几乎从来没有用过。 - user357812

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