我正在编写一个XSLT,它需要将某些数据元素输出到固定长度的容器中。虽然通常源数据将包含比固定长度少的元素,但源数据可能包含比固定长度更多的元素。当发生后者时,我需要将元素分别输出到某个索引,然后根据属性对其余的兄弟姐妹进行分组,以便可以在组上使用串联和求和。除了给定索引值后的兄弟姐妹分组之外,我都能实现所有这些功能。
下面是我要实现的目标的简化版本。假设容器的固定长度为5
,并且仅要输出的Type
值为1
和2
,而省略了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) > 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() < 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() > 3][generate-id() = generate-id(key('product-distinct', concat($id,Type))[1])]) = 2">
<xsl:for-each select="$productList[position() > 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() > 4]/Amount)"/>
<xsl:with-param name="productName">
<xsl:call-template name="CombineProductNames">
<xsl:with-param name="products" select="$productList[position() > 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() < 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() > 4]/Amount)"/>
<xsl:with-param name="productName">
<xsl:call-template name="CombineProductNames">
<xsl:with-param name="products" select="$productList[position() > 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>