如何使用XPath从XML中检索递归元素

时间:2015-09-30 08:56:04

标签: xml xslt recursion xpath xquery

在处理Xpath表达式时,我遇到了一个案例,我必须找出该节点,该节点依赖于另一个节点的一个元素。

以下是使用的XML示例:

<?xml version="1.0" encoding="UTF-8" ?>

<parent1>
    <child1 id="1">
        <in>Starting1</in>
        <out>connect1</out>
    </child1>
    <child1 id="2">
        <in>connect1</in>
        <out>connect1.1</out>
    </child1>
    <child1 id="3">
        <in>Starting2</in>
        <out>connect2</out>
    </child1>
    <child1 id="4">
        <in>connect1.1</in>
        <out>connect1.2</out>
    </child1>
    <child1 id="5">
        <in>connect1.2</in>
        <out>end1</out>
    </child1>
    <child1 id="6">
        <in>connect2</in>
        <out>connect2.1</out>
    </child1>
    <child1 id="7">
        <in>connect2.1</in>
        <out>connect2.2</out>
    </child1>
    <child1 id="8">
        <in>connect2.2</in>
        <out>open2</out>
    </child1>
</parent1>

期望的输出是找出节点,其起点为&#34;开始&#34;然后前往另一个节点(意味着,节点外是In用于其他节点)&amp;结束于&#34;结束&#34;。

在开始和结束之间可能有x个连接。

我使用了以下xpath表达式。但这仅限于2级递归。

//parent1/child1[in=(//parent1/child1[in=(//parent1/child1[in=(//parent1/child1[contains(in,"Starting")]/out)]/out)]/out) and not(contains(out,"end"))]

输出:

<child1 id="8">
  <in>connect2.2</in>
  <out>open2</out>
</child1>

因为,我不确定节点之间可以连接多少个连接器。那么,在XML1.0中是否有任何方法可以找出递归?

有一个duplicate question already in stackoverflow.但是,我没有从那里得到解决方案。

enter image description here

2 个答案:

答案 0 :(得分:1)

您的要求不是很清楚。从我认为我明白的一点点来看,我相信你必须在两次通过中做到这一点。这是一个局部的例子:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="edge-by-in" match="child1" use="in" />
<xsl:key name="edge-by-out" match="child1" use="out" />

<xsl:template match="/parent1">
    <xsl:variable name="first-pass">
        <xsl:for-each select="child1">
            <edge id="{@id}">
                <xsl:apply-templates select="." mode="find-start"/>
                <xsl:apply-templates select="." mode="find-end"/>
            </edge>
        </xsl:for-each>
    </xsl:variable>
    <output>
        <!-- process the nodes contained in $first-pass -->
    </output>
</xsl:template>

<xsl:template match="child1" mode="find-start">
    <xsl:variable name="prev" select="key('edge-by-out', in)" />
    <xsl:choose>
        <xsl:when test="$prev">
            <xsl:apply-templates select="$prev" mode="find-start"/>
        </xsl:when>
        <xsl:otherwise>
            <start>
                <xsl:value-of select="in"/>
            </start>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="child1" mode="find-end">
    <xsl:variable name="next" select="key('edge-by-in', out)" />
    <xsl:choose>
        <xsl:when test="$next">
            <xsl:apply-templates select="$next" mode="find-end"/>
        </xsl:when>
        <xsl:otherwise>
            <end>
                <xsl:value-of select="out"/>
            </end>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

将此值应用于输入时,$first-pass变量将包含:

   <edge id="1">
      <start>Starting1</start>
      <end>end1</end>
   </edge>
   <edge id="2">
      <start>Starting1</start>
      <end>end1</end>
   </edge>
   <edge id="3">
      <start>Starting2</start>
      <end>open2</end>
   </edge>
   <edge id="4">
      <start>Starting1</start>
      <end>end1</end>
   </edge>
   <edge id="5">
      <start>Starting1</start>
      <end>end1</end>
   </edge>
   <edge id="6">
      <start>Starting2</start>
      <end>open2</end>
   </edge>
   <edge id="7">
      <start>Starting2</start>
      <end>open2</end>
   </edge>
   <edge id="8">
      <start>Starting2</start>
      <end>open2</end>
   </edge>

现在,您可以使用它来选择具有(或不具有)特定startend的节点。

答案 1 :(得分:0)

// parent1 / child1 [contains(in / text(),'Starting')而不是(contains(out / text(),'end'))]

<强>更新

查看图片。它完全返回您要求的内容:

enter image description here

  

期望的输出是找出具有起始点的节点   作为“开始”&amp;不以“结束”结尾。

您可以根据自己的期望更新问题吗?也许您希望使用您添加的XML输出。