【问题标题】:XSLT for-each loop table outputXSLT for-each 循环表输出
【发布时间】:2014-06-20 14:19:06
【问题描述】:

我想用 XSLT 创建一个表,其中流程阶段是标题,而 processNo 是值。

  <PV_Batch masterBatchNo="KH8944">
    <tempTable processStage="Blending" processNo="KF0285" />
    <tempTable processStage="Coating" processNo="KF0282" />
    <tempTable processStage="Compression" processNo="KF0283" />
    <tempTable processStage="Granulation" processNo="12345" />
    <tempTable processStage="Granulation" processhNo="KF0284" />
    <tempTable processStage="Mixing" processNo="KF0286" />
    <tempTable processStage="mixing" processNo="K12035" />
  </PV_Batch>

Blending | Coating | Compression | Granulation | Mixing
KF0285   | KF0282  | KF0283      | 12345       | KF0286
         |         |             | Kf0284      | K12035

我在重复值方面遇到问题,每当我有重复时,它们就会超出范围,就像这样。

Blending | Coating | Compression | Granulation | Mixing
KF0285   | KF0282  | KF0283      | 12345       | Kf0284  | KF0268  | K12035

这是我的表格 xslt 样式表中的代码片段。

          <xsl:for-each select="//tempTable[generate-id() = generate-id(key('keyComponent', @processStage)[1])]">

              <th width="" align="Left" class="tableHeaderRow">
                  <xsl:value-of select="@processStage" />
                </th>

          </xsl:for-each>

          <tr>
            <xsl:for-each select="tempTable">
              <td align="Left" class="tableNormalRow">
                <xsl:value-of select="@processNo"/>
              </td>
            </xsl:for-each>
          </tr>

【问题讨论】:

    标签: xml xslt for-loop scope


    【解决方案1】:

    显示的 sn-p 中的第二个循环必须执行与标头循环相同的分组:

    <xsl:for-each select="//tempTable[generate-id() = generate-id(key('keyComponent', @processStage)[1])]">
          <!-- get current processStage -->
          <xsl:variable name="processStage" select="@processStage"/>
          <td align="Left" class="tableNormalRow">
          <!-- loop over all tempTable belonging to current processStage -->
          <xsl:for-each select="//tempTable[@processStage=$processStage]">
              <p>                  
                  <xsl:value-of select="@processNo"/>
              </p>
          </xsl:for-each>
          </td>
    </xsl:for-each>
    

    如果一个 processStage 有多个值,这会将它们分别放在同一个 td 中的一个 p 中,所以这个表:

    Blending | Coating | Compression | Granulation | Mixing
    KF0285   | KF0282  | KF0283      | 12345       | KF0286
             |         |             | Kf0284      | K12035
    

    将仅包含 2 行。

    但是,如果这应该生成一个包含 3 行的表,则解决方案会稍微复杂一些。


    编辑:

    XSLT 2.0 解决方案:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    
    <xsl:output indent="yes"/>
    
    <xsl:variable name="processStages" select="distinct-values(//tempTable/@processStage)"/>
    
    <xsl:variable name="maxRows"
        select="max(for $i in $processStages return count(//tempTable[@processStage=$i]))"/>
    
    <xsl:template match="PV_Batch">
        <table>
            <tr>
                <xsl:for-each-group select="tempTable" group-by="@processStage">
                    <th width="" align="Left" class="tableHeaderRow">
                        <xsl:value-of select="@processStage"/>
                    </th>
                </xsl:for-each-group>
            </tr>
    
            <xsl:variable name="tempTable" select="tempTable"/>
            <xsl:for-each select="1 to $maxRows">
                <xsl:variable name="pos" select="."/>
                <tr>
                    <xsl:for-each select="$processStage">
                        <td>
                            <xsl:value-of select="$tempTable[@processStage=current()][$pos]/@processNo"/>
                        </td>
                    </xsl:for-each>
                </tr>
            </xsl:for-each>
        </table>
    </xsl:template>
    </xsl:stylesheet>
    

    XSLT 1.0 解决方案:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
    <xsl:output indent="yes"/>
    
    <xsl:key name="keyComponent" match="tempTable" use="@processStage"/>
    
    <xsl:template match="PV_Batch">
        <table>
            <tr>
                <xsl:for-each
                    select="//tempTable[generate-id() = generate-id(key('keyComponent', @processStage)[1])]">
    
                    <th width="" align="Left" class="tableHeaderRow">
                        <xsl:value-of select="@processStage"/>
                    </th>
    
                </xsl:for-each>
            </tr>
    
            <xsl:call-template name="tr"/>
        </table>
    </xsl:template>
    
    <xsl:template name="tr">
        <xsl:param name="pos" select="1"/>
        <xsl:variable name="temp">
            <xsl:for-each
                select="//tempTable[generate-id() = generate-id(key('keyComponent', @processStage)[1])]">
                <xsl:value-of select="//tempTable[@processStage=current()/@processStage][$pos]/@processNo"/>
            </xsl:for-each>
        </xsl:variable>
        <xsl:if test="normalize-space($temp)!=''">
            <tr>
                <xsl:for-each
                    select="//tempTable[generate-id() = generate-id(key('keyComponent', @processStage)[1])]">
                    <td>
                        <xsl:value-of select="//tempTable[@processStage=current()/@processStage][$pos]/@processNo"/>
                    </td>
                </xsl:for-each>
            </tr>
            <xsl:call-template name="tr">
                <xsl:with-param name="pos" select="$pos + 1"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    
    </xsl:stylesheet>
    

    【讨论】:

    • 感谢 Tobias 的回答,确实,我正在尝试找出一种动态创建表的方法,因为来自 SQL 的数据不会相同,因此肯定会有一个更大的表超过两行。
    • @batman_in_disguise 你是限制在 xslt 1.0 还是你也可以使用 xslt 2.0?
    • 我更愿意继续使用 xslt 1.0。
    • 我加了一个2.0的解决方案...我看看能不能拿出一个1.0的解决方案
    • 谢谢,可惜1.0的解决方案不行
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-08
    • 2015-11-12
    • 1970-01-01
    • 2016-05-19
    • 1970-01-01
    相关资源
    最近更新 更多