Question

I am having a problem converting this XML

<?xml version="1.0"?>
<Report username="TONYB" sessionId="20140425154921">
  <Result>
    <Request Type="InventoryShip">
      .... Omitted for brevity
    </Request>
    <Information>
      .... Omitted for brevity
    </Information>
    <Inventory>
      <Sku>PBM118-00</Sku>
      <RevisionNo/>
      <OnHand>34010</OnHand>
      .... Omitted for brevity
    </Inventory>
   <Inventory>
      <Sku>PFC00345</Sku>
      <RevisionNo/>
      <OnHand>0</OnHand>
      .... Omitted for brevity
    </Inventory>
    <Inventory>
      <Sku>PFC00476</Sku>
      <RevisionNo/>
      <OnHand>2025</OnHand>
      .... Omitted for brevity
    </Inventory>
    <Item>
      <Sku>PBM118-00</Sku>
      <CustomerPart>CP-0004</CustomerPart>
      <Description>PACKING SLIP GE-M CHANGED XX WITH A LONG DESC</Description>
      <Groups>
        <Group>BOOK</Group>
        .... Omitted for brevity
        <Group>HR</Group>
        .... Omitted for brevity
      </Groups>
      .... Omitted for brevity
      <LowestUOM>EA</LowestUOM>
    </Item>
    <Item>
      <Sku>PFC00345</Sku>
      <CustomerPart>1001</CustomerPart>
      <Description>item description 8/29/2011 16:06</Description>
      <Groups>
        <Group>F60</Group>
        <Group>FAKE2</Group>
        <Group>HR</Group>
        <Group>TYPE</Group>
      </Groups>
      .... Omitted for brevity
      <LowestUOM>EA</LowestUOM>
    </Item>
    <Item>
      <Sku>PFC00476</Sku>
      <CustomerPart>PBM119-88</CustomerPart>
      <Description>PBM118-99 NEW ITEM</Description>
      <Groups>
        <Group>BOOK</Group>
        <Group>HR</Group>
      </Groups>
      .... Omitted for brevity
      <LowestUOM>EA</LowestUOM>
    </Item>
    <Usage>
      .... Omitted for brevity
    </Usage>
    <Usage>
      <DateRange>
        <Start>01/01/2014</Start>
        <End>12/31/2014</End>
      </DateRange>
      <OrderedBySku>
        <Sku>PBM118-00</Sku>
        <ShippedQty>951</ShippedQty>
        <User/>
      </OrderedBySku>
      <OrderedBySku>
        <Sku>PFC00476</Sku>
        <ShippedQty>0</ShippedQty>
        <User/>
      </OrderedBySku>
    </Usage>
    <Usage>
      <DateRange>
        <End>12/31/2014</End>
      </DateRange>
      <OrderedBySku>
        <Sku>PFC00476</Sku>
        <ShippedQty>0</ShippedQty>
        <User/>
      </OrderedBySku>
      <OrderedBySku>
        <Sku>PBM118-00</Sku>
        <ShippedQty>116668</ShippedQty>
        <User/>
      </OrderedBySku>
    </Usage>
  </Result>
</Report>

into something like this, where there is an empty line between each transition of the first "Group" field:

<html>
..... Omitted for brevity
<tbody>
<tr class="">
<td><img src="/wmsImages/SALIX/products/PBM118-00_THM.jpg"></td><td>CP-0004</td><td>PACKING SLIP GE-M CHANGED XX WITH A LONG DESC</td><td>U</td><td>Y</td><td class="number">34,010EA</td><td class="number">116,668</td><td class="number">100CA</td><td>BOOK</td><td>CORP</td><td>FAKE</td><td>FAKE2</td><td>FAKE3</td><td>FAKEDEPT</td><td>HR</td><td>REPORT</td><td>fake</td><td></td><td></td>
</tr>
<tr class="">
<td></td><td>PBM119-88</td><td>PBM118-99 NEW ITEM</td><td>U</td><td>Y</td><td class="number">2,025EA</td><td class="number">0</td><td class="number">EA</td><td>BOOK</td><td>HR</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
<tr class="">
<td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
<tr class="">
<td></td><td>1001</td><td>item description 8/29/2011 16:06</td><td>U</td><td>Y</td><td class="number">0EA</td><td class="number">0</td><td class="number">10RL</td><td>F60</td><td>FAKE2</td><td>HR</td><td>TYPE</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
<tr class="">
<td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
</tbody>
..... Omitted for brevity
</html>

I keep getting this:

<html>
..... Omitted for brevity
<tbody>
<tr class="">
<td><img src="/wmsImages/SALIX/products/PBM118-00_THM.jpg"></td><td>CP-0004</td><td>PACKING SLIP GE-M CHANGED XX WITH A LONG DESC</td><td>U</td><td>Y</td><td class="number">34,010EA</td><td class="number">116,668</td><td class="number">100CA</td><td>BOOK</td><td>CORP</td><td>FAKE</td><td>FAKE2</td><td>FAKE3</td><td>FAKEDEPT</td><td>HR</td><td>REPORT</td><td>fake</td><td></td><td></td>
</tr>
<tr class="">
<td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
<tr class="">
<td></td><td>PBM119-88</td><td>PBM118-99 NEW ITEM</td><td>U</td><td>Y</td><td class="number">2,025EA</td><td class="number">0</td><td class="number">EA</td><td>BOOK</td><td>HR</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
<tr class="">
<td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
<tr class="">
<td></td><td>1001</td><td>item description 8/29/2011 16:06</td><td>U</td><td>Y</td><td class="number">0EA</td><td class="number">0</td><td class="number">10RL</td><td>F60</td><td>FAKE2</td><td>HR</td><td>TYPE</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
<tr class="">
<td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
</tr>
</tbody>
..... Omitted for brevity
</html>

So what am I doing wrong. Yes, the "Group" tag is optional, and we could have 0 or more. But in this case, as you can see, there is something in the first group column for all 3 items.

Here is my xslt:

<?xml version='1.0' encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import   href="report_common.xsl"/>
  <xsl:param    name="csvLink"                      />
  <xsl:param    name="thumbs"                       />
  <xsl:param    name="showOutOfStock" select="'Yes'"/>

  <xsl:variable name="xxshowRevision" select="'Junk'"           />
  <xsl:variable name="ThumbNames"     select="document($thumbs)"/>
  <xsl:variable name="maxGroupCols"   select="10"/>



  <!-- Define a rule to suppress header elements. -->
  <xsl:template mode="header"
      match="BelowLowPoint|Discontinued|Inactive|OrderStatus|Sku|DateRange">
  </xsl:template>

  <!-- Define the root template for this report. -->
  <xsl:template match="/Report">
<html>
  <head>
    <title>Inventory with Shipping Data</title>
    <meta http-equiv="Content-Type"
        content="text/html"/>
    <link href="/css/layout.css"
        rel="stylesheet" type="text/css" />
    <link href="/css/markup.css"
        rel="stylesheet" type="text/css" />
    <link href="/css/printer.css"
        rel="stylesheet" type="text/css" media='printer' />
    <link href="/css/SALIX.css"
        rel="stylesheet" type="text/css" />
    <style type="text/css">
.OutOfStock{
    background-color: red
}
    </style>
  </head>
  <body>
    <form action="">
      <input type="button" class="cssbutton no-print"
          onClick="window.print()"
          value="Print Screen"/>
    </form>
    <xsl:apply-templates select="Result"/>
    <div class="no-print">
      <xsl:element name="form">
        <xsl:attribute name="name"  >csvForm</xsl:attribute>
        <xsl:attribute name="method">post</xsl:attribute>
        <xsl:attribute name="action">
          <xsl:value-of select="concat('/staticreports/',$csvLink)"/>
        </xsl:attribute>
        <input type="hidden" name="reportType"
            value="StaticReport"/>

        <xsl:element name="input">
          <xsl:attribute name="type">hidden</xsl:attribute>
          <xsl:attribute name="name">reportTitle</xsl:attribute>
          <xsl:attribute name="value">
            <xsl:value-of select="$reportTitle"/>
          </xsl:attribute>

        </xsl:element>
        <a href="#"
            onClick="document.csvForm.submit();return false"
          >Click here to export into spreadsheet.</a>
      </xsl:element>
    </div>
  </body>
</html>
  </xsl:template>

  <xsl:key name="byGroup" match="Inventory" use="../Item/Sku[.=current()/Sku]/../Groups/Group[1]" />

  <xsl:template match="Result">
    <xsl:apply-imports/>
<table class="report data">
  <col class="itemNo"/>
  <thead>
    <tr>
      <td/>
      <th>Client Lit Code #</th>
      <th>Description</th>
      <xsl:if test="$xxshowRevision='Yes'">
      <th>Revision</th>
      </xsl:if>
      <th>ACC CDE</th>
      <th>Allow Web</th>
      <th class="number">Available Balance</th>
      <th class="number">Total Shipped</th>
      <th class="number">UOM</th>
    <xsl:call-template name="groupHead">
      <xsl:with-param name="i">1</xsl:with-param>
      <xsl:with-param name="count">
        <xsl:value-of select="$maxGroupCols"/>
      </xsl:with-param>
    </xsl:call-template>
    </tr>
  </thead>
  <tbody>
    <xsl:for-each select="Inventory[ count(. | key( 'byGroup', '../Item/Sku[.=current()/Sku]/../Groups/Group[1]')[1]) = 1 ]">
      <xsl:sort select="../Item/Sku[.=current()/Sku]/../Groups"/>
      <xsl:sort select="../Item/Sku[.=current()/Sku]/../CustomerPart"/>
      <xsl:sort select="Sku"/>
      <xsl:sort select="RevisionNo"/>

      <xsl:variable name="Item"
          select="../Item[./Sku=current()/Sku]"/>

      <xsl:element name="tr">
        <xsl:attribute name="class">
          <xsl:choose>
            <xsl:when test="position()!=last() and position() mod 5 = 0"
              >bgLight </xsl:when>
          </xsl:choose>
        </xsl:attribute>
        <xsl:apply-templates select="." mode="item"/>
      </xsl:element>

      <xsl:element name="tr">
        <xsl:attribute name="class">
          <xsl:choose>
            <xsl:when test="position()!=last() and position() mod 5 = 0"
              >bgLight </xsl:when>
          </xsl:choose>
        </xsl:attribute>
        <xsl:apply-templates select="." mode="group" />
      </xsl:element>
    </xsl:for-each>
  </tbody>
</table>
  </xsl:template>

  <!-- Define a rule for each inventory group -->
  <xsl:template match="Inventory" mode="group">
<td></td> <!-- ThumbImage -->
<td></td> <!-- PartNo -->
<td></td> <!-- Description -->
    <xsl:if test="$xxshowRevision='Yes'">
<td></td> <!-- RevisionNo -->
    </xsl:if>
<td></td> <!-- Access -->
<td></td> <!-- Web -->
<td></td> <!-- OnHand, LowestUOM -->
<td></td> <!-- OrderedBySku -->
<td></td> <!-- UomQty, UOM -->

  <xsl:call-template name="emptyGroupCols">
    <xsl:with-param name="i">1</xsl:with-param>
    <xsl:with-param name="count">
        <xsl:value-of select="$maxGroupCols"/>
    </xsl:with-param>
  </xsl:call-template>

  </xsl:template>

  <!-- Define a rule for each inventory item -->
  <xsl:template match="Inventory" mode="item">
    <xsl:variable name="Item"
        select="../Item[./Sku=current()/Sku]"/>

    <xsl:variable name="Revision"
        select="../Revision[./Sku=current()/Sku and
                            ./RevisionNo=current()/RevisionNo]"/>

    <xsl:variable name="CustPart"
        select="normalize-space($Item/CustomerPart)"/>

    <xsl:variable name="PartNo">
      <xsl:choose>
        <xsl:when test="string-length($CustPart)>0">
          <xsl:value-of select="$CustPart"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="Sku"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <!-- Date format must be MM/DD/YYYY to work! -->
    <xsl:variable name="Year2Date"
        select="../Usage[starts-with(DateRange/Start, '01/01/')]
                        [starts-with(DateRange/End,   '12/31/')]"/>

    <xsl:variable name="Month2Date"
        select="../Usage[DateRange/Start != $Year2Date/DateRange/Start or
                         DateRange/End   != $Year2Date/DateRange/End    ]
                        [substring(DateRange/Start, 1, 3) =
                         substring(DateRange/End,   1, 3)]"/>

    <xsl:variable name="AllDates"
        select="../Usage[not(DateRange/Start)]
                        [starts-with(DateRange/End,   '12/31/')]"/>

    <xsl:variable name="SkuThumbImage"
         select=" $ThumbNames/ThumbList/ThumbFile[@Item=$Item/Sku]/File"/>

    <xsl:variable name="ThumbImage">
        <xsl:choose>
            <xsl:when test="string-length($SkuThumbImage)>0">
                <xsl:value-of select="$SkuThumbImage"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select=" $ThumbNames/ThumbList/ThumbFile[@Item=$Item/CustomerPart]/File"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>

    <xsl:variable name="UOM">
        <xsl:choose>
            <xsl:when test="string-length(normalize-space($Item/UOM))>0">
                <xsl:value-of select="$Item/UOM"/>
            </xsl:when>
            <xsl:when test="string-length(normalize-space($Item/LowestUOM))>0">
                <xsl:value-of select="$Item/LowestUOM"/>
            </xsl:when>
            <xsl:otherwise>EA</xsl:otherwise>
        </xsl:choose>
    </xsl:variable>

    <xsl:variable name="LowestUOM">
        <xsl:choose>
            <xsl:when test="string-length(normalize-space($Item/LowestUOM))>0">
                <xsl:value-of select="$Item/LowestUOM"/>
            </xsl:when>
            <xsl:otherwise>EA</xsl:otherwise>
        </xsl:choose>
    </xsl:variable>

<td><xsl:if test="normalize-space($ThumbImage) != ''">
    <xsl:element name="img">
        <xsl:attribute name="src">
            <xsl:text>/wmsImages/SALIX/products/</xsl:text>
            <xsl:value-of select="$ThumbImage" />
        </xsl:attribute>
    </xsl:element>
    </xsl:if></td>
<td><xsl:value-of select="$PartNo"/></td>
<td><xsl:value-of select="$Item/Description"/></td>
    <xsl:if test="$xxshowRevision='Yes'">
<td><xsl:value-of select="RevisionNo"/></td>
    </xsl:if>
<td><xsl:value-of select="$Item/Access"/></td>
<td><xsl:value-of select="$Item/Web"/></td>
<td class='number'><xsl:value-of
    select="concat( format-number(OnHand,$rptqty,'f0'), $LowestUOM )"/></td>
<td class='number'><xsl:value-of
    select="format-number(sum($AllDates/OrderedBySku
            [Sku=current()/Sku]/ShippedQty),
            $rptqty,'f0')"/></td>
<td class='number'><xsl:value-of
    select="concat( $Item/UomQty, $UOM )"/></td>

  <xsl:variable name="countGroup"
    select="count($Item/Groups/Group )"/>

  <xsl:variable name="useNumGroupCols">
    <xsl:choose>
        <xsl:when test="$countGroup &gt;= $maxGroupCols">
            <xsl:value-of select="$maxGroupCols"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$countGroup"/>
        </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:for-each select="$Item/Groups/Group">
    <xsl:if test="position() &lt;= $useNumGroupCols">
        <td><xsl:value-of select="."/></td>
    </xsl:if>
  </xsl:for-each>

  <xsl:if test="$maxGroupCols &gt; $useNumGroupCols">
    <xsl:call-template name="emptyGroupCols">
      <xsl:with-param name="i">
        <xsl:value-of select="$useNumGroupCols"/>
        </xsl:with-param>
      <xsl:with-param name="count">
        <xsl:value-of select="$maxGroupCols"/>
      </xsl:with-param>
    </xsl:call-template>
  </xsl:if>

  </xsl:template>

  <xsl:template name="emptyGroupCols">
    <xsl:param name="i" />
    <xsl:param name="count" />

    <xsl:if test="$i &lt;= $count">
      <td></td>
    </xsl:if>

    <!--begin_: RepeatTheLoopUntilFinished-->
    <xsl:if test="$i &lt;= $count">
        <xsl:call-template name="emptyGroupCols">
            <xsl:with-param name="i">
                <xsl:value-of select="$i + 1"/>
            </xsl:with-param>
            <xsl:with-param name="count">
                <xsl:value-of select="$count"/>
            </xsl:with-param>
        </xsl:call-template>
    </xsl:if>

  </xsl:template>

  <xsl:template name="groupHead">
    <xsl:param name="i" />
    <xsl:param name="count" />

    <xsl:if test="$i &lt;= $count">
      <th>Group <xsl:value-of select="$i"/></th>
    </xsl:if>

    <!--begin_: RepeatTheLoopUntilFinished-->
    <xsl:if test="$i &lt;= $count">
        <xsl:call-template name="groupHead">
            <xsl:with-param name="i">
                <xsl:value-of select="$i + 1"/>
            </xsl:with-param>
            <xsl:with-param name="count">
                <xsl:value-of select="$count"/>
            </xsl:with-param>
        </xsl:call-template>
    </xsl:if>

  </xsl:template>

</xsl:stylesheet>

Thanks for any help you can give.

EDIT ( 04/28/2014 ): Here is a zip containing all necessary files, or try here if non-zip is easier. Note that the "StaticFormatcsv.xsl" file has other problems, and I only include it because it is referenced in the command I run. You can ignore it for now. Once I fix the main xsl, I will apply the same fixes to the "csv" xsl.

Also, here is the command I run:

java -cp /usr/share/java/xalan.jar org.apache.xalan.xslt.Process -in StaticData20140425154921 -out StaticReport20140425154921.html -PARAM csvLink StaticReport20140425154921.csv -PARAM thumbs ThumbList.xsl -xsl StaticFormat.xsl

where "xalan.jar" refers/points to "xalan-2.4.1.jar".

Was it helpful?

Solution

I started off by simplifying your XSLT to this....

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/Report">
    <html>
      <head>
        <title>Inventory with Shipping Data</title>
      </head>
      <body>
        <xsl:apply-templates select="Result"/>
      </body>
    </html>
  </xsl:template>

  <xsl:key name="byGroup" match="Inventory" use="../Item/Sku[.=current()/Sku]/../Groups/Group[1]" />

  <xsl:template match="Result">
    <table class="report data">
      <tbody>
        <xsl:for-each select="Inventory[ count(. | key( 'byGroup', '../Item/Sku[.=current()/Sku]/../Groups/Group[1]')[1]) = 1 ]">
          <xsl:element name="tr">
            <xsl:apply-templates select="." mode="item"/>
          </xsl:element>

          <xsl:element name="tr">
            <xsl:apply-templates select="." mode="group" />
          </xsl:element>
        </xsl:for-each>
      </tbody>
    </table>
  </xsl:template>

  <xsl:template match="Inventory" mode="item">
      <td><xsl:value-of select="../Item/Sku[.=current()/Sku]/../Groups/Group[1]" /></td>
  </xsl:template>

  <xsl:template match="Inventory" mode="group">
      <td><xsl:value-of select="Sku" /></td>
  </xsl:template>
</xsl:stylesheet>

The first thing to note is that for each Inventory element in your XML, you output two tr elements, which is why you get six rows in your output. You are always going to get an even number of rows this way. Perhaps you want you want is to output a row for each distinct Item/Groups/Group[1] (of which there are two distinct values), and then output a row for each Inventory which have a matching Sku.

To start with, your key can be simplified to this

<xsl:key name="byGroup" match="Inventory" 
         use="../Item[Sku=current()/Sku]/Groups/Group[1]" />

But there is a problem with how you use this key...

<xsl:for-each 
     select="Inventory[ count(. | key( 'byGroup', '../Item/Sku[.=current()/Sku]/../Groups/Group[1]')[1]) = 1 ]">

You should not be using apostrophes for the second argument of the key function, otherwise it will be treated as a literal. You should be doing this.... (Using the simplified key shown above)

<xsl:for-each 
     select="Inventory[ count(. | key( 'byGroup', ../Item[Sku=current()/Sku]/Groups/Group[1])[1]) = 1 ]">

But even this has a problem. In the xsl:key statement, current() will refer to the Inventory item currently being matched. But in the xsl:for-each statement, current() refers to the node on which you are currently positioned when the xsl:for-each is executed, which is the Result element.

To get around this, define a key to allow you to look up Item elements by Sku

<xsl:key name="itemBySku" match="Item" use="Sku" />

Then, the xsl:for-each can be written like this:

<xsl:for-each 
     select="Inventory[ count(. | key('byGroup', key('itemBySku', Sku)/Groups/Group[1])[1]) = 1 ]">

Finally, to output all the Inventory elements in the current group, you would then do this:

<xsl:apply-templates 
     select="key('byGroup', key('itemBySku', Sku)/Groups/Group[1])" mode="group" />

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/Report">
    <html>
      <head>
        <title>Inventory with Shipping Data</title>
      </head>
      <body>
        <xsl:apply-templates select="Result"/>
      </body>
    </html>
  </xsl:template>

  <xsl:key name="byGroup" match="Inventory" use="../Item[Sku=current()/Sku]/Groups/Group[1]" />
  <xsl:key name="itemBySku" match="Item" use="Sku" />

  <xsl:template match="Result">
    <table class="report data">
      <tbody>
        <xsl:for-each select="Inventory[ count(. | key('byGroup', key('itemBySku', Sku)/Groups/Group[1])[1]) = 1 ]">
          <tr>
            <xsl:apply-templates select="." mode="item"/>
          </tr>

          <xsl:apply-templates select="key('byGroup', key('itemBySku', Sku)/Groups/Group[1])" mode="group" />
        </xsl:for-each>
      </tbody>
    </table>
  </xsl:template>

  <xsl:template match="Inventory" mode="item">
    <td><xsl:value-of select="key('itemBySku', Sku)/Groups/Group[1]" /></td>
  </xsl:template>

  <xsl:template match="Inventory" mode="group">
    <tr>
      <td><xsl:value-of select="Sku" /></td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

Possibly this is not actually want you are looking for, but it should at least show you how to simplify your XSLT, and give you something to use as a basis for adding in all the other functionality you need that wasn't related to the underlying issue at hand.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top