【问题标题】:XPath expression (in XSLT) to match siblings of node, up to a sibling fulfilling condition based on its childrenXPath 表达式(在 XSLT 中)匹配节点的兄弟节点,直到兄弟节点根据其子节点满足条件
【发布时间】:2012-04-16 22:43:27
【问题描述】:

我有以下标记,一个混合了 TR 和一个或两个子 TD 的表格

  <table>
    <tr>
      <td>
         <p>XXX</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>XXX</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>XXX</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
  </table>

我正在尝试使用 XSLT 1.0 将其转换为

<AAA>
  XXX
  <BBB>YYYYY</BBB>
  <BBB>YYYYY</BBB>
  ...
</AAA>
<AAA>
  XXX
  <BBB>YYYYY</BBB>
  <BBB>YYYYY</BBB>
  ...
</AAA>
...

从这可能是在 XSLT 中处理嵌套循环的最佳方式这一事实中抽象出来,XPath 表达式放入了什么???下面将选择当前 TR (XXX) 的所有 TR 兄弟姐妹,它们有两个 TD 孩子,但只到下一个 TR (XXX)。我根据它们有多少个 TD 子节点来区分包含 XXX 和 YYYYY 的节点。 1 = XXX,2 = YYYYY。

<xsl:for-each select="//table/tr">
   <xsl:if test="count(td) = '1'">
      XXX
   </xsl:if>
   <xsl:for-each select="???">
      all YYYYY up until the next XXX
   </xsl:for-each>
</xsl:for-each>

我有 "following-sibling::tr[child::td[following-sibling::td]" 但它与 YYYYY 匹配到最后 - 我如何让它只选择那些直到下一个TR 与 XXX?

谢谢!

【问题讨论】:

    标签: xml xslt xpath


    【解决方案1】:

    我。 XSLT 1.0 解决方案

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:key name="kFollowing" match="tr[td[2]]"
      use="generate-id(preceding-sibling::tr
                                      [not(td[2])]
                                         [1]
                       )"/>
    
     <xsl:template match="/*">
       <xsl:apply-templates select="tr[not(td[2])]"/>
     </xsl:template>
    
     <xsl:template match="tr[not(td[2])]">
      <AAA>
        <xsl:value-of select="concat('&#xA;', td/p, '&#xA;')"/>
    
        <xsl:apply-templates select="key('kFollowing', generate-id())"/>
      </AAA>
     </xsl:template>
    
     <xsl:template match="p">
      <BBB><xsl:value-of select="."/></BBB>
     </xsl:template>
    </xsl:stylesheet>
    

    当此 XSLT 1.0 转换应用于提供的 XML 文档时

    <table>
        <tr>
            <td>
                <p>XXX</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>YYYYYY</p>
            </td>
            <td>
                <p>YYYYYY</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>YYYYYY</p>
            </td>
            <td>
                <p>YYYYYY</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>YYYYYY</p>
            </td>
            <td>
                <p>YYYYYY</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>XXX</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>YYYYYY</p>
            </td>
            <td>
                <p>YYYYYY</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>YYYYYY</p>
            </td>
            <td>
                <p>YYYYYY</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>XXX</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>YYYYYY</p>
            </td>
            <td>
                <p>YYYYYY</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>YYYYYY</p>
            </td>
            <td>
                <p>YYYYYY</p>
            </td>
        </tr>
    </table>
    

    产生想要的正确结果

    <AAA>
    XXX
    <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
    </AAA>
    <AAA>
    XXX
    <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
    </AAA>
    <AAA>
    XXX
    <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
    </AAA>
    

    解释

    适当使用 key 来定义具有第二个 td 子级的所有 tr 作为立即的 generate-id() 的函数-前面的单子 tr 元素。


    二。 XSLT 2.0 解决方案:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="/*">
      <xsl:for-each-group group-starting-with="tr[not(td[2])]"
           select="tr">
        <xsl:apply-templates select="current-group()[1]"/>
      </xsl:for-each-group>
     </xsl:template>
    
     <xsl:template match="tr[not(td[2])]">
      <AAA>
        <xsl:value-of select="concat('&#xA;', td/p, '&#xA;')"/>
    
        <xsl:apply-templates select="current-group()[position() gt 1]"/>
      </AAA>
     </xsl:template>
    
     <xsl:template match="p">
      <BBB><xsl:value-of select="."/></BBB>
     </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于同一个 XML 文档(上图)时,会产生相同的正确结果

    <AAA>
    XXX
    <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
    </AAA>
    <AAA>
    XXX
    <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
    </AAA>
    <AAA>
    XXX
    <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
       <BBB>YYYYYY</BBB>
    </AAA>
    

    解释

    适当使用 xsl:for-each-groupgroup-starting-with 属性和 current-group() 函数。

    【讨论】:

      【解决方案2】:

      我一直认为有一个 XPath 构造完全符合您的要求会很有用:序列中的所有项目,直到(包括/排除)第一个匹配某个条件的项目。我已经考虑过这种语法,例如expr until condition。 Saxon 和 EXSLT 尝试提供更高阶的扩展函数来满足需求,例如 saxon:leading()。可悲的是,规范中没有任何内容。虽然在 XPath 3.0 中,使用高阶函数可以很容易地自己完成。

      您有时可以使用分组 (xsl:for-each-group) 来解决问题,但通常最简单和最优雅的方法是使用递归:即编写一个函数来处理序列中的第一项,然后如果某些条件为真,则调用自身来处理下一项。

      另一种方法是在序列中搜索与条件匹配的第一个节点,然后使用 subsequence() 选择直到该位置的节点:

      <xsl:variable name="positions" select="
        for $i in 1 to count($seq)
        return if (my:condition($seq[$i])) then $i else ()"/>
      <xsl:apply-templates select="subsequence($seq, 1, $positions[1])"/>
      

      【讨论】:

      • 抱歉,我看到您提到了 XSLT 1.0。这会让你的生活变得更加艰难。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多