在 XPath 中,如何在锚元素前后 select 组跟随兄弟节点?

In XPath, how can I select groups of following-sibling nodes before and after an anchor element?

我有一个 xml 文档,如下所示,

<sec>
    <p type="Running">aa</p>
    <p type="heading 1">bb</p>
    <p type="body">cc</p>
    <p type="body">dd</p>
    <p type="Running">ee</p>
    <p type="Body">ff</p>
    <p type="heading">gg</p>
    <p type="body">hh</p>
    <p type="body">ii</p>
    <p type="Running">jj</p>
    <p type="list">kk</p>
    <p type="list">ll</p>
    <p type="list">mm</p>
    <p type="list">nn</p>
</sec>

使用 xpath 我需要 select 来自 <p><p> 节点的连续后续兄弟节点,其属性值为 Running

SO 在上面的例子中

    <p type="heading 1">bb</p>
    <p type="body">cc</p>
    <p type="body">dd</p>

    <p type="Body">ff</p>
    <p type="heading">gg</p>
    <p type="body">hh</p>
    <p type="body">ii</p>

    <p type="list">kk</p>
    <p type="list">ll</p>
    <p type="list">mm</p>
    <p type="list">nn</p>

节点组应该 selected。

如何向 select 这些节点编写 XPath 查询?

XPath version - 2.0

这应该对你有帮助 -

var j = new List<string>();
var t = new XmlDocument();
t.Load(new StreamReader("xmlPath"));
var type = t.GetElementsByTagName("p");

for (var i = 0; i < type.Count; i++)
{
    if (((XmlNode)type[i]).Attributes[0].Value != "Running")
    {
       j.Add(((XmlNode)type[i]).OuterXml);
    }
}

Xpath 下面 select 除了 type='Running'

之外的所有内容
/sec/p[not(@type='Running')]

我不知道 XPath 2,但是有了 XPath 1 和一些高级编程,您可以使用这种 XPath 表达式,计算前面的兄弟类型 ="Running":

//p[not(@type="Running")
    and count(preceding-sibling::p[@type="Running"])=1]
                                                     ^
                                                     |
                                                1 then 2 then 3

这个问题目前有三个答案,但我认为没有一个能真正回答这个问题。

在 XPath 2.0 中,一切都是序列。如果你 select 一组节点,在 XPath 1.0 中你称之为 "node set",在 XPath 2.0 中它是 "sequence of nodes"。一个属性的序列是不能嵌套的:(1, (2, 3), 4)等同于(1, 2, 3, 4).

你要求 select 声明 selects 节点集,这意味着你想对每个集做一些事情。合乎逻辑的做法如下所示:

for $r in sec/p[@type = 'Running']
return $r
    /following-sibling::p
    [not(@type = 'Running')]
    [. << following-sibling::p[@type = 'Running'][1]]

这是一个相当复杂的表达式。虽然它会在内部 select 您之后的子集,但由于序列归一化,净效应是一个 selection 等于 sec/p[not(@type = 'Running')].

的单个序列

在 XPath 2.0 中不可能以不同的方式执行此操作,因此自然要做的事情是使用宿主语言,如 XSLT 或 XQuery,到 select @type = 'Running' 节点,并且在每次命中时,select(并做某事)将跟随兄弟姐妹直到下一个 @type = 'Running':

<xsl:template match="sec">
    <xsl:apply-templates select="p[@type = 'Running']" />
</xsl:template>

<xsl:template match="p">
    <!-- do something before the group -->
    <xsl:apply-templates select="following-sibling::p[following-sibling::p[@type = 'Running'] >> .]" mode="group"/>
    <!-- do something after the group -->
</xsl:template>

<xsl:template match="p" mode="group">
   <!-- do something with items in the group -->
</xsl:template>

这里使用xsl:for-each-group可能更容易,这是为了这种事情。