如何使用XSL并行遍历多个节点集?

时间:2012-03-22 22:22:15

标签: xml xslt

我想知道是否有一种方法可以同时迭代XML文档和两组节点,我知道这不是一个非常具有描述性的问题,所以这里有一个例子:

xml示例:

<document>
  <animal species="dog">
    <fact>4 legs</fact>
    <fact>2 eyes</fact>
    <fact>loyal</fact>
  </animal>
  <animal species="horse">
    <fact>rideable</fact>
    <fact>4 legs</fact>
    <fact>2 eyes</fact>
    <fact>huge</fact>
  </animal>
</document>

xsl需要编辑:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
  <body>
  <h2>Results</h2>
  <table border="1">
    <tr bgcolor="#9acd32">
      <th>Dogs</th>
      <th>Horses</th>
    </tr>
    <xsl:for-each select="document">
      <???for all greater quantity of facts between the 2 in this document>
        <tr>
          <td><xsl:value-of select="./animal/@dog/fact[current#]"/></td>
          <td><xsl:value-of select="./animal/@horse/fact[current#]"/></td>
        </tr>
      </???>
    </xsl:for-each>
  </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

究竟应该更换???用于检索以下表格的方法?

Dogs   |Horses   | 
4 legs |rideable |
2 eyes |4 legs   |
loyal  |2 eyes   |

感谢任何可能的帮助。

3 个答案:

答案 0 :(得分:0)

像往常一样,如果你被困在XSLT1并且不能使用xsl:for-each-group,你想使用muenchian分组

<html>
   <body>
      <h2>Results</h2>
      <table border="1">
         <tr bgcolor="#9acd32">
            <th>Dogs</th>
            <th>Horses</th>
         </tr>
         <tr>
            <td>4 legs</td>
            <td>rideable</td>
         </tr>
         <tr>
            <td>2 eyes</td>
            <td>4 legs</td>
         </tr>
         <tr>
            <td>loyal</td>
            <td>2 eyes</td>
         </tr>
         <tr>
            <td></td>
            <td>huge</td>
         </tr>
      </table>
   </body>
</html>

来自这个xslt

 -  <xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
       <xsl:template match="/">
        <html>
         <body>
          <h2>Results</h2>
          <table border="1">
           <tr bgcolor="#9acd32">
            <th>Dogs</th>
            <th>Horses</th>
           </tr>
           <xsl:for-each select="document">
            <xsl:for-each select="animal/fact[generate-id()=generate-id(key('f',1+count(preceding-sibling::fact))[1])]">
             <tr>
              <xsl:variable name="k"  select="key('f',1+count(preceding-sibling::fact))"/>
              <td><xsl:value-of select="$k[../@species='dog']"/></td>
              <td><xsl:value-of select="$k[../@species='horse']"/></td>
             </tr>
            </xsl:for-each>
           </xsl:for-each>
          </table>
         </body>
        </html>
       </xsl:template>
       <xsl:key name="f" match="fact" use="1+count(preceding-sibling::fact)"/>
       </xsl:stylesheet>

答案 1 :(得分:0)

我的解决方案适用于XSLT 1.0,并在Altova XMLSpy下工作。以下模板:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
  <body>
  <h2>Results</h2>
  <table border="1">
    <tr bgcolor="#9acd32">
      <th>Dogs</th>
      <th>Horses</th>
    </tr>
    <xsl:apply-templates select="document">
        <xsl:with-param name="num" select="1" />
    </xsl:apply-templates>
  </table>
  </body>
  </html>
</xsl:template>
<xsl:template match="document">
<xsl:param name="num" />
    <xsl:if test="count(animal/fact[$num]) = count(animal)">
        <tr>
            <xsl:for-each select="animal">
              <td><xsl:value-of select="fact[$num]"/></td>
            </xsl:for-each>
        </tr>
        <xsl:apply-templates select=".">
            <xsl:with-param name="num" select="$num + 1"/>
        </xsl:apply-templates>
    </xsl:if>
</xsl:template>
</xsl:stylesheet>

应用于提供的输入XML,给出以下结果:

<html>
    <body>
        <h2>Results</h2>
        <table border="1">
            <tr bgcolor="#9acd32">
                <th>Dogs</th>
                <th>Horses</th>
            </tr>
            <tr>
                <td>4 legs</td>
                <td>rideable</td>
            </tr>
            <tr>
                <td>2 eyes</td>
                <td>4 legs</td>
            </tr>
            <tr>
                <td>loyal</td>
                <td>2 eyes</td>
            </tr>
        </table>
    </body>
</html>

我认为你不想生成只填充一个单元格的行(不应该考虑huge列中只有Horse值的最后一行。这与David的解决方案不同。

编辑:但是,改变我的方法以获得与David相同的结果非常简单。它只需要通过调用count() XPATH函数来改变条件:

<xsl:if test="count(animal/fact[$num]) > 0">

而不是:

<xsl:if test="count(animal/fact[$num]) = count(animal)">

完成后,您将获得以下输出:

<html>
    <body>
        <h2>Results</h2>
        <table border="1">
            <tr bgcolor="#9acd32">
                <th>Dogs</th>
                <th>Horses</th>
            </tr>
            <tr>
                <td>4 legs</td>
                <td>rideable</td>
            </tr>
            <tr>
                <td>2 eyes</td>
                <td>4 legs</td>
            </tr>
            <tr>
                <td>loyal</td>
                <td>2 eyes</td>
            </tr>
            <tr>
                <td></td>
                <td>huge</td>
            </tr>
        </table>
    </body>
</html>

答案 2 :(得分:0)

这是一个通用的XSLT 1.0解决方案,可以在XML文档中存在任何数量的动物时正常工作。没有物种名称是硬编码的

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vHavingMaxFactsPosition">
  <xsl:for-each select="/*/animal">
   <xsl:sort select="count(*)" data-type="number"
        order="descending"/>
   <xsl:if test="position() = 1">
    <xsl:value-of select="count(preceding-sibling::animal)+1"/>
   </xsl:if>
  </xsl:for-each>
 </xsl:variable>

 <xsl:variable name="vHavingMaxFacts" select=
   "/*/*[position() = $vHavingMaxFactsPosition]"/>

 <xsl:template match="/*">
  <html>
   <body>
     <table border="1">
       <tr>
         <xsl:apply-templates select="*/@species"/>
       </tr>
       <xsl:apply-templates mode="startRow" select="$vHavingMaxFacts/*"/>
     </table>
   </body>
  </html>
 </xsl:template>

 <xsl:template match="fact" mode="startRow">
  <xsl:variable name="vPos" select="position()"/>

  <tr>
   <xsl:for-each select="/*/*">
    <td><xsl:value-of select="*[position()=$vPos]"/></td>
   </xsl:for-each>
  </tr>
 </xsl:template>

 <xsl:template match="@species">
  <th><xsl:value-of select="."/></th>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<document>
    <animal species="dog">
        <fact>4 legs</fact>
        <fact>2 eyes</fact>
        <fact>loyal</fact>
    </animal>
    <animal species="horse">
        <fact>rideable</fact>
        <fact>4 legs</fact>
        <fact>2 eyes</fact>
        <fact>huge</fact>
    </animal>
</document>

产生了想要的正确结果

<html>
   <body>
      <table border="1">
         <tr>
            <th>dog</th>
            <th>horse</th>
         </tr>
         <tr>
            <td>4 legs</td>
            <td>rideable</td>
         </tr>
         <tr>
            <td>2 eyes</td>
            <td>4 legs</td>
         </tr>
         <tr>
            <td>loyal</td>
            <td>2 eyes</td>
         </tr>
         <tr>
            <td></td>
            <td>huge</td>
         </tr>
      </table>
   </body>
</html>

上面对此XML文档应用相同的转换时(3个动物 - 添加了5个事实的蜘蛛):

<document>
    <animal species="dog">
        <fact>4 legs</fact>
        <fact>2 eyes</fact>
        <fact>loyal</fact>
    </animal>
    <animal species="horse">
        <fact>rideable</fact>
        <fact>4 legs</fact>
        <fact>2 eyes</fact>
        <fact>huge</fact>
    </animal>
    <animal species="spider">
        <fact>insect</fact>
        <fact>6 legs</fact>
        <fact>x eyes</fact>
        <fact>small</fact>
        <fact>dangerous</fact>
    </animal>
</document>

再次生成正确的结果

<html>
   <body>
      <table border="1">
         <tr>
            <th>dog</th>
            <th>horse</th>
            <th>spider</th>
         </tr>
         <tr>
            <td>4 legs</td>
            <td>rideable</td>
            <td>insect</td>
         </tr>
         <tr>
            <td>2 eyes</td>
            <td>4 legs</td>
            <td>6 legs</td>
         </tr>
         <tr>
            <td>loyal</td>
            <td>2 eyes</td>
            <td>x eyes</td>
         </tr>
         <tr>
            <td></td>
            <td>huge</td>
            <td>small</td>
         </tr>
         <tr>
            <td></td>
            <td></td>
            <td>dangerous</td>
         </tr>
      </table>
   </body>
</html>