XSLT-索引值后的分组元素

时间:2019-05-02 21:18:08

标签: xslt xslt-1.0

我正在编写一个XSLT,它需要将某些数据元素输出到固定长度的容器中。虽然通常源数据将包含比固定长度少的元素,但源数据可能包含比固定长度更多的元素。当发生后者时,我需要将元素分别输出到某个索引,然后根据属性对其余的兄弟姐妹进行分组,以便可以在组上使用串联和求和。除了给定索引值后的兄弟姐妹分组之外,我都能实现所有这些功能。

下面是我要实现的目标的简化版本。假设容器的固定长度为5,并且仅要输出的Type值为12,而省略了3

输入XML示例

<?xml version="1.0" encoding="utf-8"?>
<Company>
  <Locations>
    <Location>
      <Id>1</Id>
      <Shoppers>
        <Shopper>
          <Products>
            <Product>
              <Amount>5.00</Amount>
              <Id>1</Id>
              <Name>A</Name>
              <Type>1</Type>
            </Product>
            <Product>
              <Amount>10.00</Amount>
              <Id>1</Id>
              <Name>B</Name>
              <Type>2</Type>
            </Product>
            <Product>
              <Amount>15.00</Amount>
              <Id>1</Id>
              <Name>C</Name>
              <Type>3</Type>
            </Product>
            <Product>
              <Amount>20.00</Amount>
              <Id>1</Id>
              <Name>D</Name>
              <Type>1</Type>
            </Product>
          </Products>
        </Shopper>
        <Shopper>
          <Products>
            <Product>
              <Amount>25.00</Amount>
              <Id>1</Id>
              <Name>E</Name>
              <Type>2</Type>
            </Product>
            <Product>
              <Amount>30.00</Amount>
              <Id>1</Id>
              <Name>F</Name>
              <Type>1</Type>
            </Product>
            <Product>
              <Amount>35.00</Amount>
              <Id>1</Id>
              <Name>G</Name>
              <Type>2</Type>
            </Product>
            <Product>
              <Amount>40.00</Amount>
              <Id>1</Id>
              <Name>H</Name>
              <Type>1</Type>
            </Product>
          </Products>
        </Shopper>
      </Shoppers>
    </Location>
    <Location>
      <Id>2</Id>
      <Shoppers>
        <Shopper>
          <Products>
            <Product>
              <Amount>5.00</Amount>
              <Id>2</Id>
              <Name>I</Name>
              <Type>A</Type>
            </Product>
            <Product>
              <Amount>10.00</Amount>
              <Id>2</Id>
              <Name>J</Name>
              <Type>B</Type>
            </Product>
          </Products>
        </Shopper>
        <Shopper>
          <Products>
            <Product>
              <Amount>25.00</Amount>
              <Id>2</Id>
              <Name>K</Name>
              <Type>A</Type>
            </Product>
            <Product>
              <Amount>30.00</Amount>
              <Id>2</Id>
              <Name>L</Name>
              <Type>B</Type>
            </Product>
            <Product>
              <Amount>35.00</Amount>
              <Id>2</Id>
              <Name>M</Name>
              <Type>B</Type>
            </Product>
          </Products>
        </Shopper>
      </Shoppers>
    </Location>
  </Locations>
</Company>

XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl customCode"  xmlns:customCode="urn:customCode">
  <xsl:output method="xml" indent="yes" encoding="utf-8"/>

  <xsl:key name="product-distinct" match="Product" use="concat(Id,Type)"/>

  <xsl:template match="/">
    <Locations>
      <xsl:for-each select="Company/Locations/Location">
        <xsl:variable name="id" select="Id"/>
        <!--Ideally, I would like to use a variable as to not repeat the same XPath throughout the code to simplify any necessary changes-->
        <xsl:variable name="productList" select="Shoppers/Shopper/Products/Product[Type != '3']"/>
        <Location>
          <Products>
            <xsl:choose>
              <xsl:when test="count($productList) &gt; 5">
                <xsl:choose>
                  <xsl:when test="count($productList[generate-id() = generate-id(key('product-distinct', concat($id,Type))[1])]) = 2">
                    <xsl:for-each select="$productList[position() &lt; 4]">
                      <xsl:call-template name="Product">
                        <xsl:with-param name="amount" select="Amount"/>
                        <xsl:with-param name="productName" select="Name"/>
                        <xsl:with-param name="type" select="Type"/>
                      </xsl:call-template>
                    </xsl:for-each>
                    <xsl:choose>
                      <xsl:when test="count($productList[position() &gt; 3][generate-id() = generate-id(key('product-distinct', concat($id,Type))[1])]) = 2">
                        <xsl:for-each select="$productList[position() &gt; 3][generate-id() = generate-id(key('product-distinct', concat($id,Type))[1])]">
                          <xsl:variable name="keyGroup" select="key('product-distinct', concat($id,Type))"/>
                          <xsl:call-template name="Product">
                            <xsl:with-param name="amount" select="sum($keyGroup/Amount)"/>
                            <xsl:with-param name="productName" select="$keyGroup"/>
                            <xsl:with-param name="type" select="$keyGroup/Type"/>
                          </xsl:call-template>
                        </xsl:for-each>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:call-template name="Product">
                          <xsl:with-param name="amount" select="$productList[4]/Amount"/>
                          <xsl:with-param name="productName" select="$productList[4]/Name"/>
                          <xsl:with-param name="type" select="$productList[4]/Type"/>
                        </xsl:call-template>
                        <xsl:call-template name="Product">
                          <xsl:with-param name="amount" select="sum($productList[position() &gt; 4]/Amount)"/>
                          <xsl:with-param name="productName">
                            <xsl:call-template name="CombineProductNames">
                              <xsl:with-param name="products" select="$productList[position() &gt; 4]"/>
                            </xsl:call-template>
                          </xsl:with-param>
                          <xsl:with-param name="type" select="$productList[5]/Type"/>
                        </xsl:call-template>
                      </xsl:otherwise>
                    </xsl:choose>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:for-each select="$productList[position() &lt; 5]">
                      <xsl:call-template name="Product">
                        <xsl:with-param name="amount" select="Amount"/>
                        <xsl:with-param name="productName" select="Name"/>
                        <xsl:with-param name="type" select="Type"/>
                      </xsl:call-template>
                    </xsl:for-each>
                    <xsl:call-template name="Product">
                      <xsl:with-param name="amount" select="sum($productList[position() &gt; 4]/Amount)"/>
                      <xsl:with-param name="productName">
                        <xsl:call-template name="CombineProductNames">
                          <xsl:with-param name="products" select="$productList[position() &gt; 4]"/>
                        </xsl:call-template>
                      </xsl:with-param>
                      <xsl:with-param name="type" select="$productList[5]/Type"/>
                    </xsl:call-template>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:when>
              <xsl:otherwise>
                <xsl:for-each select="$productList">
                  <xsl:call-template name="Product">
                    <xsl:with-param name="amount" select="Amount"/>
                    <xsl:with-param name="productName" select="Name"/>
                    <xsl:with-param name="type" select="Type"/>
                  </xsl:call-template>
                </xsl:for-each>
              </xsl:otherwise>
            </xsl:choose>
          </Products>
        </Location>
      </xsl:for-each>
    </Locations>
  </xsl:template>

  <xsl:template name="Product">
    <xsl:param name="amount"/>
    <xsl:param name="productName"/>
    <xsl:param name="type"/>
    <Product>
      <xsl:attribute name="Amount">
        <xsl:value-of select="format-number($amount, '0.00')"/>
      </xsl:attribute>
      <xsl:attribute name="Name">
        <xsl:value-of select="$productName"/>
      </xsl:attribute>
      <xsl:attribute name="Type">
        <xsl:value-of select="$type"/>
      </xsl:attribute>
    </Product>
  </xsl:template>

  <xsl:template name="CombineProductNames">
    <xsl:param name="products"/>
    <xsl:for-each select="$products/Name">
      <xsl:value-of select="."/>
      <xsl:if test="position() != last()">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

电流输出

<?xml version="1.0" encoding="utf-8"?>
<Locations>
  <Location>
    <Products>
      <Product Amount="5.00" Name="A" Type="1" />
      <Product Amount="10.00" Name="B" Type="2" />
      <Product Amount="20.00" Name="D" Type="1" />
      <Product Amount="25.00" Name="E" Type="2" />
      <Product Amount="105.00" Name="F, G, H" Type="1" />
    </Products>
  </Location>
  <Location>
    <Products>
      <Product Amount="5.00" Name="I" Type="A" />
      <Product Amount="10.00" Name="J" Type="B" />
      <Product Amount="25.00" Name="K" Type="A" />
      <Product Amount="30.00" Name="L" Type="B" />
      <Product Amount="35.00" Name="M" Type="B" />
    </Products>
  </Location>
</Locations>

预期产量

<?xml version="1.0" encoding="utf-8"?>
<Locations>
  <Location>
    <Products>
      <Product Amount="5.00" Name="A" Type="1" />
      <Product Amount="10.00" Name="B" Type="2" />
      <Product Amount="20.00" Name="D" Type="1" />
      <Product Amount="60.00" Name="E, G" Type="2" />
      <Product Amount="70.00" Name="F, H" Type="1" />
    </Products>
  </Location>
  <Location>
    <Products>
      <Product Amount="5.00" Name="I" Type="A" />
      <Product Amount="10.00" Name="J" Type="B" />
      <Product Amount="25.00" Name="K" Type="A" />
      <Product Amount="30.00" Name="L" Type="B" />
      <Product Amount="35.00" Name="M" Type="B" />
    </Products>
  </Location>
</Locations>

0 个答案:

没有答案