累积数组/列表中的值(XSL 2.0)

时间:2015-05-31 16:30:37

标签: arrays xml list xslt

我需要在数组/列表中累积值。我无法正确填充数组中的值以获取不同的Packed Items.Please帮助。

要求是对于每个容器,检查每个Packed Item,如果数量(在当前容器中 - > Packed Item)等于任何前面兄弟中的“Total”元素。 如是;总计=数量
其他;总计=总数+数量//总计最初为0

必须对所有不同的打包项目重复此过程。打包项由输入xml中的ItemId参数唯一标识。

输入XML

<?xml version="1.0" encoding="UTF-8"?>
<Shipment xmlns="http://www.example.org">
  <Container>
    <ContainerID>C1</ContainerID>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>4</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>4</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>4</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>2</Quantity>
      <Total>0</Total>
    </PackedItem>
  </Container>
  <Container>
    <ContainerID>C2</ContainerID>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>4</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>8</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>6</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>6</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>5</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>2</Quantity>
      <Total>0</Total>
    </PackedItem>
  </Container>
  <Container>
    <ContainerID>C3</ContainerID>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>3</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>3</Quantity>
      <Total>0</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>2</Quantity>
      <Total>0</Total>
    </PackedItem>
     <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>2</Quantity>
      <Total>0</Total>
    </PackedItem>
     <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>2</Quantity>
      <Total>0</Total>
    </PackedItem>
  </Container>
</Shipment>

输出XML

<?xml version="1.0" encoding="UTF-8"?>
<Shipment xmlns="http://www.example.org">
  <Container>
    <ContainerID>C1</ContainerID>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>4</Quantity>
      <Total>8</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>4</Quantity>
      <Total>8</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>4</Quantity>
      <Total>6</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>2</Quantity>
      <Total>6</Total>
    </PackedItem>
  </Container>
  <Container>
    <ContainerID>C2</ContainerID>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>4</Quantity>
      <Total>10</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>8</Quantity>
      <Total>8</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>6</Quantity>
      <Total>10</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>6</Quantity>
      <Total>6</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>5</Quantity>
      <Total>7</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>2</Quantity>
      <Total>7</Total>
    </PackedItem>
  </Container>
  <Container>
    <ContainerID>C3</ContainerID>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>3</Quantity>
      <Total>8</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>3</Quantity>
      <Total>8</Total>
    </PackedItem>
    <PackedItem>
      <ItemID>A123</ItemID>
      <Quantity>2</Quantity>
      <Total>8</Total>
    </PackedItem>
  </Container>
   <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>2</Quantity>
      <Total>4</Total>
    </PackedItem>
     <PackedItem>
      <ItemID>A111</ItemID>
      <Quantity>2</Quantity>
      <Total>4</Total>
    </PackedItem>
</Shipment>

应用于示例XML,意味着:

我们必须将每个PackedItem中的数量与前面每个兄弟“Total”值进行比较。如果数量不等于任何预先计算总数,那么我们将其视为一个参数,以形成Container中特定PackedItem的总数。 PackedItems基于其ItemID进行识别。

例如:如果你看到第一个容器,第一个容器PackedItem1(ItemID = A123)的总值将是8(两个量子的总和就像没有前面的兄弟一样)。类似地,对于第二个打包项(ItemID = A111),Total的值将为6(因为没有前面的兄弟)。所以现在我们的数组A123中应该有2条记录 - &gt; 8和A111-> 6

对于第二个容器,第一个packedItem(A123)的第一个数量是4.我们检查它是否等于同一个项目的前一个兄弟姐妹总数(即8)。由于它不相等,因此可以考虑获得总数。在第二个conatiner中进一步移动我们将不会考虑8(在项目A123中),因为它等于先前的conatiners sum。下一个PackedItem(A123)的值为6.这不等于前一个兄弟(A123)总和所以将是考虑总数。所以现在我们有4 + 6 = 10,它在第二个conatiner中形成了PackedItem A123的总和。在类似的路线上,我们必须处理PackedItem A111的数量。数量5和2将合计形成总数为7。

将对每个容器中的所有打包项重复此过程,更新数量不等于任何前一个兄弟的总和的项的总数。 PFB我正在处理的xslt(版本2.0)。

XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

                xmlns:ex="http://www.example.org"
                xmlns:ns0="http://www.example.org"
                xmlns:ns1="http://www.example.org" version="2.0">
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates select="ex:Container"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="node()|@*">
        <xsl:param name="computed.totals"></xsl:param>
        <xsl:param name="curr.total"></xsl:param>
        <xsl:param name="ItemID"></xsl:param>
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:with-param name="computed.totals"
                                select="$computed.totals"></xsl:with-param>
                <xsl:with-param name="curr.total" select="$curr.total"></xsl:with-param>
                <xsl:with-param name="ItemID" select="$ItemID"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="ex:Container">
        <!-- A trick to initialize to an empty node-set, see http://lists.w3.org/Archives/Public/xsl-editors/2000JulSep/0068.html -->
        <xsl:param name="computed.totals" select="/@empty-node-set"/>
        <ex:Container>
            <xsl:apply-templates>
                <xsl:with-param name="computed.totals"
                                select="$computed.totals"></xsl:with-param>
            </xsl:apply-templates>
        </ex:Container>
    </xsl:template>
    <xsl:template match="ex:PackedItem">
        <xsl:param name="computed.totals"/>
        <!-- Process the current total, taking into accound the totals of preceding siblings -->
        <xsl:variable name="ItemID" select="./ex:ItemID"/>
        <xsl:variable name="curr.total"
                      select="sum(../ex:PackedItem[./ex:ItemID=$ItemID]/ex:Quantity[(($computed.totals)/MyTotal[@ItemID=$ItemID]/@value != .) or not(($computed.totals)/*)])"></xsl:variable>
        <!-- Process current Container elements -->
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:with-param name="computed.totals"
                                select="$computed.totals"></xsl:with-param>
                <xsl:with-param name="curr.total" select="$curr.total"></xsl:with-param>
                <xsl:with-param name="ItemID" select="$ItemID"/>
            </xsl:apply-templates>
        </xsl:copy>
        <!-- Process next container, with the updated list of Total already computed -->
        <xsl:apply-templates select="following-sibling::ex:Container[1]">
            <xsl:with-param name="computed.totals">
                <xsl:copy-of select="$computed.totals"/>
                <MyTotal>
                    <xsl:attribute name="ContainerID">
                        <xsl:value-of select="ex:ContainerID"/>
                    </xsl:attribute>
                    <xsl:attribute name="value">
                        <xsl:value-of select="$curr.total"/>
                    </xsl:attribute>
                    <xsl:attribute name="ItemID">
                        <xsl:value-of select="$ItemID"/>
                    </xsl:attribute>
                </MyTotal>
            </xsl:with-param>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'Total')]">
        <xsl:param name="computed.totals"></xsl:param>
        <!-- store the current total of the Container, will be used only when processing the Total -->
        <xsl:param name="curr.total"></xsl:param>
        <xsl:param name="ItemID"/>
        <xsl:variable name="ContainerID" select="../../ns0:ContainerID"/>
        <xsl:variable name="CurrentItemID" select="../ns0:ItemID"/>
        <xsl:variable name="Quantity" select="../ns0:Quantity"/>
        <xsl:choose>
            <xsl:when test="($computed.totals)/MyTotal[@ItemID=$CurrentItemID and @value = $Quantity]">
                <xsl:copy-of select="$Quantity"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:element name="Total" namespace="http://www.example.org">
                    <xsl:value-of select="$curr.total"/>
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

1 个答案:

答案 0 :(得分:0)

这是一个试图实现总值计算的XSLT 2.0样式表:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="http://www.example.org"
  exclude-result-prefixes="xs"
  xpath-default-namespace="http://www.example.org">

<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>

<xsl:key name="cont-pack" match="Container/PackedItem" use="ItemID"/>

<xsl:template match="@* | node()" mode="#all">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()" mode="#current"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Shipment">
  <xsl:copy>
    <xsl:apply-templates select="Container[1]"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Container">
  <xsl:param name="computed" as="element(Container)*" select="()"/>

  <xsl:variable name="this-computed" as="element(Container)">
    <xsl:choose>
      <xsl:when test="not($computed)">
        <xsl:apply-templates select="." mode="compute-simple"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." mode="compute">
          <xsl:with-param name="prev" select="$computed[last()]" tunnel="yes"/>
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>  

  <xsl:choose>
    <xsl:when test="not(following-sibling::Container[1])">
      <xsl:copy-of select="$computed, $this-computed"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="following-sibling::Container[1]">
        <xsl:with-param name="computed" select="$computed, $this-computed"/>
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>

</xsl:template>

<xsl:template match="PackedItem/Total" mode="compute-simple">
  <Total changed="true">
    <xsl:value-of select="sum(key('cont-pack', ../ItemID, ancestor::Container)/Quantity)"/>
  </Total>
</xsl:template>

<xsl:template match="PackedItem/Total" mode="compute">
  <xsl:param name="prev" as="element(Container)" tunnel="yes"/>
  <xsl:variable name="prev-total" select="$prev/PackedItem[ItemID = current()/../ItemID]/Total[@changed = 'true']"/>
  <Total changed="{if ($prev-total = ../Quantity) then 'false' else 'true'}">
    <xsl:value-of select="sum(key('cont-pack', ../ItemID, ancestor::Container)/Quantity[not(. = $prev-total)])"/>
  </Total>
</xsl:template>

</xsl:stylesheet>

当您将Saxon 9.6 HE应用于输入样本时,我会得到输出

<Shipment xmlns="http://www.example.org">
   <Container>
      <ContainerID>C1</ContainerID>
      <PackedItem>
         <ItemID>A123</ItemID>
         <Quantity>4</Quantity>
         <Total changed="true">8</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A123</ItemID>
         <Quantity>4</Quantity>
         <Total changed="true">8</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A111</ItemID>
         <Quantity>4</Quantity>
         <Total changed="true">6</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A111</ItemID>
         <Quantity>2</Quantity>
         <Total changed="true">6</Total>
      </PackedItem>
   </Container>
   <Container>
      <ContainerID>C2</ContainerID>
      <PackedItem>
         <ItemID>A123</ItemID>
         <Quantity>4</Quantity>
         <Total changed="true">10</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A123</ItemID>
         <Quantity>8</Quantity>
         <Total changed="false">10</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A123</ItemID>
         <Quantity>6</Quantity>
         <Total changed="true">10</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A111</ItemID>
         <Quantity>6</Quantity>
         <Total changed="false">7</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A111</ItemID>
         <Quantity>5</Quantity>
         <Total changed="true">7</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A111</ItemID>
         <Quantity>2</Quantity>
         <Total changed="true">7</Total>
      </PackedItem>
   </Container>
   <Container>
      <ContainerID>C3</ContainerID>
      <PackedItem>
         <ItemID>A123</ItemID>
         <Quantity>3</Quantity>
         <Total changed="true">8</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A123</ItemID>
         <Quantity>3</Quantity>
         <Total changed="true">8</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A123</ItemID>
         <Quantity>2</Quantity>
         <Total changed="true">8</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A111</ItemID>
         <Quantity>2</Quantity>
         <Total changed="true">4</Total>
      </PackedItem>
      <PackedItem>
         <ItemID>A111</ItemID>
         <Quantity>2</Quantity>
         <Total changed="true">4</Total>
      </PackedItem>
   </Container>
</Shipment>

请检查是否会显示您要查找的总计的结果。我意识到属性changed="true"/"false"不属于最终结果,但如果中间结果在总数方面很好,那么实现剥离这些属性的最后一个处理步骤并不困难:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="http://www.example.org"
  exclude-result-prefixes="xs"
  xpath-default-namespace="http://www.example.org">

<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>

<xsl:key name="cont-pack" match="Container/PackedItem" use="ItemID"/>

<xsl:template match="@* | node()" mode="#all">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()" mode="#current"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Shipment">
  <xsl:copy>
    <xsl:apply-templates select="Container[1]"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Container">
  <xsl:param name="computed" as="element(Container)*" select="()"/>

  <xsl:variable name="this-computed" as="element(Container)">
    <xsl:choose>
      <xsl:when test="not($computed)">
        <xsl:apply-templates select="." mode="compute-simple"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." mode="compute">
          <xsl:with-param name="prev" select="$computed[last()]" tunnel="yes"/>
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>  

  <xsl:choose>
    <xsl:when test="not(following-sibling::Container[1])">
      <xsl:apply-templates select="$computed, $this-computed" mode="strip"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="following-sibling::Container[1]">
        <xsl:with-param name="computed" select="$computed, $this-computed"/>
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>

</xsl:template>

<xsl:template match="PackedItem/Total" mode="compute-simple">
  <Total changed="true">
    <xsl:value-of select="sum(key('cont-pack', ../ItemID, ancestor::Container)/Quantity)"/>
  </Total>
</xsl:template>

<xsl:template match="PackedItem/Total" mode="compute">
  <xsl:param name="prev" as="element(Container)" tunnel="yes"/>
  <xsl:variable name="prev-total" select="$prev/PackedItem[ItemID = current()/../ItemID]/Total[@changed = 'true']"/>
  <Total changed="{if ($prev-total = ../Quantity) then 'false' else 'true'}">
    <xsl:value-of select="sum(key('cont-pack', ../ItemID, ancestor::Container)/Quantity[not(. = $prev-total)])"/>
  </Total>
</xsl:template>

<xsl:template match="PackedItem/Total/@changed" mode="strip"/>

</xsl:stylesheet>