在XSLT中求和材料量的BIll

时间:2012-05-03 16:25:37

标签: xml xslt recursion

我正在处理XSLT报告,需要查找物料清单中组件的总计。输入XML由Items和Links组成。对于每个项目,我需要获取每个使用它的链接的数量。更复杂的是,我需要上升层次结构并将数量乘以父数量。该示例仅显示两个级别,但可能比此更深。例如,Item assy1使用5个assy2和1个螺丝项。每个物品assy2使用2个螺丝。因此总共会有5个assy2和11个螺钉(1个由assy1使用,每个assy2使用2个(2X5))。我已经弄清楚如何总计项目数量,但不知道如何将它们乘以层次结构。

以下是源XML:

<items>
<item id="93516">
<attrs><attr name="FILE_ID">assy1</attr></attrs>
</item>
<item id="93515">
<attrs><attr name="FILE_ID">assy2</attr></attrs>
</item>
<item id="93514">
<attrs><attr name="FILE_ID">screw</attr></attrs>
</item>
</items>

<links>
<link source="93516" destination="93514">
<attrs><attr name="QUANTITY">5</attr></attrs>
</link>
<link source="93516" destination="93515">
<attrs><attr name="QUANTITY">1</attr></attrs>
</link>
<link source="93515" destination="93514">
<attrs><attr name="QUANTITY">2</attr></attrs>
</link>
</links>

以下是我发现的一些代码,用于对数量求和,但它不会乘以父数量:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:template match="/">
    <xsl:apply-templates select="/items/item"/>
</xsl:template>

<xsl:template match="item">
    <xsl:value-of select="attrs/attr[@name='FILE_ID']"/> - 
    <xsl:variable name="item_id" select="@id"/>
    <xsl:call-template name="bomQty">
        <xsl:with-param name="itemLinks" select="/links/link[@destination=$item_id]"/>
    </xsl:call-template> - 
</xsl:template>

<xsl:template name="bomQty">
  <xsl:param name="itemLinks"/>
    <xsl:choose>
      <xsl:when test="$itemLinks">
        <xsl:variable name="recursive_result">
          <xsl:call-template name="bomQty">
            <xsl:with-param name="itemLinks" select="$itemLinks[position() > 1]"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="number($itemLinks[1]/attrs/attr[@name='QUANTITY']) + $recursive_result"/>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:1)

此转化

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common">
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kItemById" match="item" use="@id"/>

 <xsl:key name="kUses" match="@destination"
                       use="../@source"/>
 <xsl:key name="kLink" match="link"
           use="concat(@source, '+', @destination)"/>
 <xsl:key name="kelemByName" match="*/*" use="name()"/>

 <xsl:template match="/">

Bill of Materials for the production of one <xsl:text/>

 <xsl:text/> "<xsl:value-of select="/*/items/item[1]/*/*"/>":

  <xsl:variable name="vrtfPass1">
      <xsl:call-template name="generate">
       <xsl:with-param name="pId"
          select="/*/items/item[1]/@id"/>
      </xsl:call-template>
  </xsl:variable>

  <xsl:apply-templates mode="pass2" select=
                       "ext:node-set($vrtfPass1)/*"/>
 </xsl:template>

 <xsl:template name="generate">
  <xsl:param name="pId"/>
  <xsl:param name="pQty" select="1"/>

  <xsl:element name="{string(key('kItemById', $pId))}">
    <xsl:attribute name="qty">
      <xsl:value-of select="$pQty"/>
    </xsl:attribute>
    <xsl:for-each select="key('kUses', $pId)">
      <xsl:call-template name="generate">
        <xsl:with-param name="pId" select="."/>
        <xsl:with-param name="pQty" select=
        "key('kLink', concat($pId, '+', .))
              /attrs/attr[@name='QUANTITY']"/>
      </xsl:call-template>
    </xsl:for-each>
  </xsl:element>
 </xsl:template>

 <xsl:template match="/*" mode="pass2">
  <xsl:for-each select=
   "descendant::*
      [generate-id()
      =
       generate-id(key('kelemByName', name())[1])
      ]">

    <xsl:value-of select=
     "concat(name(), ': ')"/>

      <xsl:variable name="vrtfQuantities">
          <xsl:for-each select="key('kelemByName', name())">
              <xsl:call-template name="totalQuantity"/>
          </xsl:for-each>
      </xsl:variable>

            <xsl:value-of select=
             "concat(sum(ext:node-set($vrtfQuantities)/total), '&#xA;')"/>

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

 <xsl:template name="totalQuantity">
  <xsl:param name="pNode" select="."/>

  <xsl:choose>
   <xsl:when test="not($pNode/parent::*)">
    <total><xsl:value-of select="$pNode/@qty"/></total>
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="vAncTotal">
     <xsl:call-template name="totalQuantity">
      <xsl:with-param name="pNode" select="$pNode/.."/>
     </xsl:call-template>
    </xsl:variable>

    <total><xsl:value-of select="$pNode/@qty * $vAncTotal"/></total>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

</xsl:stylesheet>

应用于提供的XML文档时:

<bm>
    <items>
        <item id="93516">
            <attrs>
                <attr name="FILE_ID">assy1</attr>
            </attrs>
        </item>
        <item id="93514">
            <attrs>
                <attr name="FILE_ID">assy2</attr>
            </attrs>
        </item>
        <item id="93515">
            <attrs>
                <attr name="FILE_ID">screw</attr>
            </attrs>
        </item>
    </items>
    <links>
        <link source="93516" destination="93514">
            <attrs>
                <attr name="QUANTITY">5</attr>
            </attrs>
        </link>
        <link source="93516" destination="93515">
            <attrs>
                <attr name="QUANTITY">1</attr>
            </attrs>
        </link>
        <link source="93514" destination="93515">
            <attrs>
                <attr name="QUANTITY">2</attr>
            </attrs>
        </link>
    </links>
</bm>

会产生想要的正确结果:

Bill of Materials for the production of one  "assy1":

assy2: 5
screw: 11