【问题标题】:XPath expression with condition on multiple ancestors具有多个祖先条件的 XPath 表达式
【发布时间】:2011-02-12 22:07:03
【问题描述】:

我正在开发的应用程序接收到类似于以下的 XML 结构:

<Root>
    <Valid>
        <Child name="Child1" />
        <Container>
            <Child name="Child2" />
        </Container>
        <Container>
            <Container>
                <Child name="Child3"/>
                <Child name="Child4"/>
            </Container>
        </Container>
        <Wrapper>
            <Child name="Child5" />
        </Wrapper>
        <Wrapper>
            <Container>
                <Child name="Child19" />
            </Container>
        </Wrapper>
        <Container>
            <Wrapper>
                <Child name="Child6" />
            </Wrapper>
        </Container>
        <Container>
            <Wrapper>
                <Container>
                    <Child name="Child20" />
                </Container>
            </Wrapper>
        </Container>
    </Valid>
    <Invalid>
        <Child name="Child7" />
        <Container>
            <Child name="Child8" />
        </Container>
        <Container>
            <Container>
                <Child name="Child9"/>
                <Child name="Child10"/>
            </Container>
        </Container>
        <Wrapper>
            <Child name="Child11" />
        </Wrapper>
        <Container>
            <Wrapper>
                <Child name="Child12" />
            </Wrapper>
        </Container>
    </Invalid>
</Root>

我需要在以下条件下获取子元素的列表:

  1. 孩子有效祖先的n代后代。
  2. Child 可能是 Container 祖先的 m 代后代,它是 Valido 代后代> 祖先。
  3. Child 元素的有效祖先是作为 m 代祖先的 Container 元素和作为第一代祖先的 Valid 元素.

其中 m、n、o 是自然数。

我需要编写以下 XPath 表达式

Valid/Child
Valid/Container/Child
Valid/Container/Container/Child
Valid/Container/Container/Container/Child
...

作为单个 XPath 表达式。

例如,XPath 表达式将只返回 name 属性等于 Child1Child2Child 元素>、Child3Child4

我最接近解决方案的是以下表达式。

Valid/Child | Valid//*[self::Container]/Child

但是,这会选择 name 属性等于 Child19Child20Child 元素。

XPath 语法是否支持在 ChildValid 元素之间的所有祖先元素的可选出现或设置类似于前面示例中的 self 的条件?

【问题讨论】:

  • 有趣的一个。 +1。我只记得像Valid/Child | Valid/Container/Child | Valid/Container/Container/Child 这样的简单联合。可能是@Alejandro 或@Dimite 将提供一种仅跳过Container 步骤的简单方法。
  • @Flack +1。这确实涵盖了提供的示例。但是,ValidChild 元素之间的 Container 元素的数量是任意的(0,1,2,3,...)跨度>
  • 我希望@Dimitre 有解决方案。
  • @Rest Wing,这也足以作为您的示例:Valid//Child[not(ancestor::Wrapper)]
  • @Flack:+1。是的,这个例子就足够了。但是,可能存在任意数量的此类“无效”祖先。因此,我需要一个更通用的表达式,比如Valid//Child[each ancestor after Valid::Container)]

标签: c# xml xpath


【解决方案1】:

使用

//Child[ancestor::*
          [not(self::Container)][1]
                            [self::Valid]
       ]

在提供的 XML 文档上评估此 XPath 表达式时:

<Root>
    <Valid>
        <Child name="Child1" />
        <Container>
            <Child name="Child2" />
        </Container>
        <Container>
            <Container>
                <Child name="Child3"/>
                <Child name="Child4"/>
            </Container>
        </Container>
        <Wrapper>
            <Child name="Child5" />
        </Wrapper>
        <Wrapper>
            <Container>
                <Child name="Child19" />
            </Container>
        </Wrapper>
        <Container>
            <Wrapper>
                <Child name="Child6" />
            </Wrapper>
        </Container>
        <Container>
            <Wrapper>
                <Container>
                    <Child name="Child20" />
                </Container>
            </Wrapper>
        </Container>
    </Valid>
    <Invalid>
        <Child name="Child7" />
        <Container>
            <Child name="Child8" />
        </Container>
        <Container>
            <Container>
                <Child name="Child9"/>
                <Child name="Child10"/>
            </Container>
        </Container>
        <Wrapper>
            <Child name="Child11" />
        </Wrapper>
        <Container>
            <Wrapper>
                <Child name="Child12" />
            </Wrapper>
        </Container>
    </Invalid>
</Root>

正是想要的节点被选中:

<Child name="Child1"/>
<Child name="Child2"/>
<Child name="Child3"/>
<Child name="Child4"/>

解释

表达式:

//Child[ancestor::*
          [not(self::Container)][1]
                            [self::Valid]
       ]

表示

从文档中的所有Child 元素中,仅选择那些不是Container 的第一个祖先是Valid 的元素。

【讨论】:

  • @Dimitre:+1。宏伟,简单实现。太糟糕了,我不能给超过一票:)
  • 使用这个答案,我还可以通过and 轻松处理嵌套的Valid 元素-使用count(ancestor::Valid) = 1 处理当前条件。
  • @RestWing:是的,XPath 是一种了不起的语言。更不用说 XPath 2.0 和 XPath 3.0。最近我完全在 XPath 3.0 中实现了二叉搜索树数据结构——在我的博客中看看这个。
  • @Dimitre:会的。我想 .NET 框架只支持 XPath 1.0,对吗?
  • @RestWing:正确。但是,至少有两个 .NET XSLT 2.0 处理器:Saxon 和 XQSharp,当然,这两个处理器都支持 XPath 2.0。 Saxon EE9.3.04(付费版)早期实现了 XPath 3.0 和 XSLT 3.0。
【解决方案2】:
//Valid
 //Child[count(ancestor::Container[ancestor::Valid])
          = count(ancestor::*[ancestor::Valid])]

解释:

//Valid//Child

返回作为Valid 节点后代的所有Child 节点。

count(ancestor::Container[ancestor::Valid]])

返回作为当前节点 (Child) 的祖先的 Container 标签的数量,并且它们自己有一个名为 Valid 的祖先

count(ancestor::*[ancestor::Valid])

返回当前节点 (Child) 的祖先且自身拥有名为 Valid 的祖先的所有标签的数量

因此,只有当ValidChild 之间的所有标签都被称为Container 时,两个值才相等。

然而,这个表达式假定不会有任何嵌套的Valid标签,即/Valid/Valid/Child不会被它接受。

更新:再次查看您的 xml,这不是更容易吗?

//Valid//Child[not(ancestor::Wrapper)]

【讨论】:

  • +1。灿烂。更新的表达可能会更容易。但是,它没有第一个严格。请参阅有关 Flack 提出的答案的问题评论。我可以假设不会有 Valid 元素嵌套:)
  • 虽然您的解决方案有效,但我已将@Dimitre 给出的解决方案标记为最终答案。但是,我不会脱掉你的条纹,你应得的:)
猜你喜欢
  • 1970-01-01
  • 2014-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-02
相关资源
最近更新 更多