如何使用XSL Transformation管道或两阶段转换

时间:2013-08-01 10:46:26

标签: xml xslt transform

我正在尝试将一个包罗万象的xsl样式表更改为两阶段样式表设置,其中xml在第一步中被翻译为“理想”xml,第二步仅仅是格式化。

最初,我们有这个xml(删除了无关的部分)

<?xml-stylesheet type="text/xsl" href="xsl/storeFinancialReport.xsl" ?>
<storeFinancialReport>
  <ProductGroupEntry><ProductGroupCode>xz1</ProductGroupCode><ProductGroup>thing1</ProductGroup><Quantity>0.0000</Quantity><DiscountExcl>0.000000</DiscountExcl><SalesExcl>30.700000</SalesExcl><Sales>35.000000</Sales></ProductGroupEntry>
  <ProductGroupEntry><ProductGroupCode>xz2</ProductGroupCode><ProductGroup>thing2</ProductGroup><Quantity>13.0000</Quantity><DiscountExcl>0.000000</DiscountExcl><SalesExcl>1480.970000</SalesExcl><Sales>1688.310000</Sales></ProductGroupEntry>
  <ProductGroupEntry><ProductGroupCode>xz3</ProductGroupCode><ProductGroup>thing3</ProductGroup><Quantity>2.0000</Quantity><DiscountExcl>0.000000</DiscountExcl><SalesExcl>50.730000</SalesExcl><Sales>57.830000</Sales></ProductGroupEntry>
  <ProductGroupEntry><ProductGroupCode>xz4</ProductGroupCode><ProductGroup>thing4</ProductGroup><Quantity>2.0000</Quantity><DiscountExcl>0.000000</DiscountExcl><SalesExcl>40.450000</SalesExcl><Sales>46.110000</Sales></ProductGroupEntry>
  <ProductGroupEntry><ProductGroupCode>xz5</ProductGroupCode><ProductGroup>thing5</ProductGroup><Quantity>2.0000</Quantity><DiscountExcl>1.000000</DiscountExcl><SalesExcl>18.000000</SalesExcl><Sales>18.000000</Sales></ProductGroupEntry>
  <ProductGroupEntry><ProductGroupCode>xz6</ProductGroupCode><ProductGroup>thing6</ProductGroup><Quantity>4.0000</Quantity><DiscountExcl>10.080000</DiscountExcl><SalesExcl>62.900000</SalesExcl><Sales>68.420000</Sales></ProductGroupEntry>
</storeFinancialReport>

我现在正在尝试创建一个单独的模板来执行可以重复使用的转换,求和,计算等,然后在主文件中的第二个模板中使用html格式,该文件使用“Enrichment”导入文件/ idealising“模板

到目前为止,我在下面理论上调用了一个模板,它将执行总和等,将其存储在一个变量中,然后将这个新变换的xml作为变量传递给执行HTML格式化的模板

主要/ HTML格式文件

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:set="http://exslt.org/sets"
extension-element-prefixes="set">
<xsl:output method="html" version="1.0" encoding="UTF-8"
    indent="yes" />

<xsl:include href="scrap_book_enrichment.xsl" />

<!-- call Product Group Block -->

<xsl:template name="ProductGroupBlock">
    <xsl:variable name="buildProductGroupResultXml"> <!-- store the results of this template call to this variable -->
        <xsl:call-template name="buildProductGroup">
            <xsl:with-param name="buildProductGroupResults" select="//storeFinancialReport"/>
        </xsl:call-template>
    </xsl:variable>

    <xsl:call-template name="displayProductGroupResults">
        <xsl:with-param name="buildProductGroupResults2" select="$buildProductGroupResultXml" />
    </xsl:call-template>
</xsl:template>

<xsl:template name="displayProductGroupResults">
    <xsl:param name="buildProductGroupResults2" />
    <xsl:for-each select="buildProductGroup">
        <tr>
            <td>
                <xsl:value-of select="/" />
            </td>
            <td colspan="3">
                <xsl:value-of
                    select="$buildProductGroupResults2/ProductGroupCodeText/text()" />
            </td>

            <td align="right">
                <xsl:value-of
                    select="$buildProductGroupResults2/ProductGroupQuantityText/text()" />
            </td>

            <td align="right">
                <xsl:value-of
                    select="$buildProductGroupResults2/ProductGroupDiscountExclText/text()" />
            </td>

            <td align="right">
                <xsl:value-of
                    select="$buildProductGroupResults2/ProductGroupSalesExclText/text()" />
            </td>

            <td align="right">
                <xsl:value-of
                    select="$buildProductGroupResults2/ProductGroupSalesText/text()" />
            </td>

            <td align="right">
                <xsl:value-of
                    select="$buildProductGroupResults2/ProductGroupSalesMinusSalesExclText/text()" />
            </td>

            <td align="right">
                <xsl:value-of
                    select="$buildProductGroupResults2/ProductGroupSalesExclPercentage/text()" />
            </td>

        </tr>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

第二个导入文件,创建“理想”xml

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:set="http://exslt.org/sets" extension-element-prefixes="set">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>


<xsl:template name="buildProductGroup">
<xsl:param name="buildProductGroupResults" />
<xsl:for-each select="set:distinct($buildProductGroupResults/ProductGroupEntry/ProductGroup)">

        <xsl:variable name="ProductGroup">
                        <xsl:value-of select="text()" />
                    </xsl:variable>

                    <buildProductGroup>
                        <ProductGroupCodeText><xsl:value-of select="$buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../ProductGroupCode/text()" /><xsl:text> - </xsl:text><xsl:value-of select="$ProductGroup" /></ProductGroupCodeText>

                        <ProductGroupQuantityText><xsl:value-of select="format-number(sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../Quantity/text()), '0')" />
                        </ProductGroupQuantityText>

                        <ProductGroupDiscountExclText><xsl:value-of
                                select="format-number(sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../DiscountExcl/text()), '###,##0.00')" />
                                </ProductGroupDiscountExclText>

                        <ProductGroupSalesExclText><xsl:value-of
                                select="format-number(sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../SalesExcl/text()), '###,##0.00')" />
                                </ProductGroupSalesExclText>

                        <ProductGroupSalesText><xsl:value-of
                                select="format-number(sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../Sales/text()), '###,##0.00')" />
                                </ProductGroupSalesText>

                      <ProductGroupSalesMinusSalesExclText><xsl:value-of select="format-number(sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../Sales/text()) - sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../SalesExcl/text()),'###,##0.00')" />
                      </ProductGroupSalesMinusSalesExclText>

                        <ProductGroupSalesExclPercentage>
                            <xsl:choose>
                                <xsl:when test="sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../SalesExcl)=0">
                                    <xsl:text>0.00</xsl:text>
                                </xsl:when>
                                <xsl:when test="sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../SalesExcl)!=0">
                                    <xsl:value-of
                                        select="format-number(number((  sum($buildProductGroupResults/ProductGroup[text()=$ProductGroup]/../SalesExcl)  div  (sum( $buildProductGroupResults/SalesExcl ) ) ) * 100), '###,##0.00')" />
                                </xsl:when>
                            </xsl:choose>
                            <xsl:text>%</xsl:text>
                        </ProductGroupSalesExclPercentage>

                    </buildProductGroup>
                </xsl:for-each>


</xsl:template>

</xsl:stylesheet>

我意识到这些非常厚实,但我相信我已经尽可能地减少它,同时保持它真实到我需要的东西。我们有十几个这样的部分,但我显然错过了让它发挥作用的基本步骤。

目的是为了“浓缩”阶段创建一个不同的xml,我所看到的称为“理想的”xml,用于格式化xsl来处理。不幸的是,似乎当我在eclipse中通过翻译器运行这些文件时,它只是混合了原始的xml并删除了所有格式并将它们拼凑在一块文本中。可能有几个想法我在这里被误解了,但是一旦我把这一段放下来,我想其他人会更容易陷入困境。

非常感谢

米奇。

[更新2013/08/06]

由于无法完成此操作所花费的时间以及我们在其他领域将获得的好处,因此决定花时间使我们的报告框架本身处理流水线xsls。因此,不再需要在一个样式表中使用此功能。

1 个答案:

答案 0 :(得分:1)

特别是您解决此问题的方法和XSLT一般都是错误的。

  • 您的输入已经处于理想状态,无需进行“清理”转换步骤。
  • 不使用命名模板,请使用模板匹配。
  • 不使用<xsl:for-each>,请使用模板匹配。
  • 使用<xsl:key>进行分组。
  • 不要混淆标记和布局。将与演示文稿相关的内容(align="right")移动到CSS。
  • 最后但并非最不重要:不要(!!!)使用浏览器端XSLT。 XSLT用于在服务器上运行。 (是的。我知道。从技术上讲,你可以在浏览器中运行XSLT。浏览器端的XSLT支持最多也是不一致的,让你自己很头疼,不要这样做。)

我认为没有办法逐步改进你的XSLT,所以我从头开始重写它。

这就是你所需要的:

<xsl:stylesheet 
  version="1.1"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:set="http://exslt.org/sets"
  extension-element-prefixes="set"
  exclude-result-prefixes="set"
>
  <xsl:output method="html" version="4.0" encoding="UTF-8" indent="yes" />

  <xsl:variable name="currencyFormat" select="'###,##0.00'" />
  <xsl:variable name="percentageFormat" select="'0.00%'" />

  <xsl:key name="kProductGroup" match="ProductGroupEntry" use="ProductGroupCode" />

  <xsl:template match="storeFinancialReport">
    <table>
      <xsl:apply-templates select="ProductGroupEntry" mode="byGroupCode" />
    </table>
  </xsl:template>

  <xsl:template match="ProductGroupEntry" mode="byGroupCode">
    <xsl:variable name="inThisGroup" select="key('kProductGroup', ProductGroupCode)" />

    <xsl:if test="generate-id(.) = generate-id($inThisGroup[1])">
      <xsl:variable name="sumQuantity"     select="sum($inThisGroup/Quantity)" />
      <xsl:variable name="sumDiscountExcl" select="sum($inThisGroup/DiscountExcl)" />
      <xsl:variable name="sumSalesExcl"    select="sum($inThisGroup/SalesExcl)" />
      <xsl:variable name="sumSales"        select="sum($inThisGroup/Sales)" />
      <xsl:variable name="netSales"        select="$sumSales - $sumSalesExcl" />
      <xsl:variable name="pctSalesExcl">
        <xsl:choose>
          <xsl:when test="$sumSales">
            <xsl:value-of select="$sumSalesExcl div $sumSales" />
          </xsl:when>
          <xsl:otherwise>0</xsl:otherwise>
        </xsl:choose>
      </xsl:variable>

      <tr>
        <td colspan="3"><xsl:value-of select="concat(ProductGroup, ' - ', ProductGroupCode)" /></td>
        <td class="num"><xsl:value-of select="count($inThisGroup)" /></td>
        <td class="num"><xsl:value-of select="format-number($sumQuantity, '0')" /></td>
        <td class="num"><xsl:value-of select="format-number($sumDiscountExcl, $currencyFormat)" /></td>
        <td class="num"><xsl:value-of select="format-number($sumSalesExcl, $currencyFormat)" /></td>
        <td class="num"><xsl:value-of select="format-number($sumSales, $currencyFormat)" /></td>
        <td class="num"><xsl:value-of select="format-number($netSales, $currencyFormat)" /></td>
        <td class="num"><xsl:value-of select="format-number($pctSalesExcl, $percentageFormat)" /></td>
      </tr>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

输出:

thing1 - xz1   1    0    0.00      30.70      35.00     4.30    87.71%
thing2 - xz2   1   13    0.00   1,480.97   1,688.31   207.34    87.72%
thing3 - xz3   1    2    0.00      50.73      57.83     7.10    87.72%
thing4 - xz4   1    2    0.00      40.45      46.11     5.66    87.73%
thing5 - xz5   1    2    1.00      18.00      18.00     0.00   100.00%
thing6 - xz6   1    4   10.08      62.90      68.42     5.52    91.93%

现场直播:http://www.xmlplayground.com/FmLo2Y

进一步阅读:

  • This answer解释了XSL密钥的工作原理以及它们在分组中的使用方式。
  • This answer解释了<xsl:apply-templates>的工作原理。