tags:

views:

143

answers:

2

I'm trying to sum the qty field in my xml using xslt 2.0 and it's outputting the qty of one of my pieces of equipment with multiple entries without summing by the equipment group. Any ideas about how this can be fixed?

This is the XSLT I'm using:

<xsl:for-each-group select="//node()" group-by="*[local-name()='lin_id']/text()">
    <Results>
     <xsl:element name="LIN_ID"><xsl:value-of select="*[local-name()='lin_id']"/></xsl:element>
     <xsl:element name="Count"><xsl:value-of select="sum(current-group()/*[local-name()='qty'])"/></xsl:element>
    </Results>
</xsl:for-each-group>

Here's the source XML file:

  <Equipment>
    <lin_id>C18312</lin_id>
    <qty>5</qty>
  </Equipment>
  <Equipment>
    <lin_id>C18345</lin_id>
    <qty>22</qty>
  </Equipment>
  <Equipment>
    <lin_id>C18378</lin_id>
    <qty>43</qty>
  </Equipment>
  <Equipment>
    <lin_id>C18378</lin_id>
    <qty>208</qty>
  </Equipment>

And here's what the output is currently:

<Results>
     <LIN_ID>C18312</LIN_Name>
     <Count>5</Count>
    </Results>
    <Results>
     <LIN_ID>C18345</LIN_Name>
     <Count>22</Count>
    </Results>
     <Results>
     <LIN_ID>C18378</LIN_Name>
     <Count>43</Count>
    </Results>

So you can see that it's doing the grouping but for LIN_ID C18378 it should be summing the qty of the 2 entries and outputting a count of 251 but it instead it's just displaying one of the values.

+1  A: 

First off - what's <xsl:for-each-group select="//node()" > supposed to do? Try:

<xsl:for-each-group select="//Equipment" group-by="lin_id">
  <Results>
    <LIN_ID>
      <xsl:value-of select="current-grouping-key()"/>
    </LIN_ID>
    <Count>
      <xsl:value-of select="sum(current-group()/qty)"/>
    </Count>
  </Results>
</xsl:for-each-group>

If all that *[local-name()='…'] business is because of namespaces, I suggest you get your namespace declarations straight. Because this is unnecessary and borderline horrible. ;-)

Also: There is no need to write <xsl:element name="LIN_ID">, you can just write <LIN_ID> directly, as shown above.

For debugging purposes, you could do:

<Count qty_nodes="{count(current-group()/qty)}">
  <xsl:value-of select="sum(current-group()/qty)"/>
</Count>

to see how many nodes current-group()/qty returns.

Tomalak
I guess //node() get's the current node? I have no idea. I inherited this xslt file and am just learning so I take no offense at the bordeline horrible statement. I'd have to aggree and some of the other xslt files have a mix of namespace declarations and I don't know what else. What you gave me worked well. Thanks!
John
No, //node() selects *every single node* in the entire document. *Everything*. You should do some reading on XPath. ;-) Also, the "borderline horrible" has as smiley as well. <hint>If my answer worked, you can mark it as accepted.</hint>
Tomalak
Gotcha. I'd like to mark it as an answer but I need a 15 reputation and only have 10. I'll come back and do it when I have a few more points.
John
+1  A: 

This transformation:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
>
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="/*">
      <top>
       <xsl:for-each-group select="Equipment/lin_id" group-by=".">
         <Results>
           <LIN_ID>
             <xsl:sequence select="current-grouping-key()"/>
           </LIN_ID>
           <Count>
             <xsl:sequence select="sum(current-group()/../qty/xs:integer(.))"/>
           </Count>
         </Results>
       </xsl:for-each-group>
      </top>
    </xsl:template>
</xsl:stylesheet>

when applied against the provided input (corrected to be a wellformed XML file):

<t>
    <Equipment>
     <lin_id>C18312</lin_id>
     <qty>5</qty>
    </Equipment>
    <Equipment>
     <lin_id>C18345</lin_id>
     <qty>22</qty>
    </Equipment>
    <Equipment>
     <lin_id>C18378</lin_id>
     <qty>43</qty>
    </Equipment>
    <Equipment>
     <lin_id>C18378</lin_id>
     <qty>208</qty>
    </Equipment>
</t>

produces the wanted result:

<top>
   <Results>
      <LIN_ID>C18312</LIN_ID>
      <Count>5</Count>
   </Results>
   <Results>
      <LIN_ID>C18345</LIN_ID>
      <Count>22</Count>
   </Results>
   <Results>
      <LIN_ID>C18378</LIN_ID>
      <Count>251</Count>
   </Results>
</top>
Dimitre Novatchev
Why `<xsl:sequence>` instead of `<xsl:value-of>`?
Tomalak
It is a good habit in XSLT 2.0 to always use <xsl:sequence> and only use <xsl:value-of> to produce a text node.They are equivalent only when we are sure the value will be in the final output. Even then, if the value is not the *complete* output, it is better to use <xsl:sequence>. If we used <xsl:value-of>, then the created text node would have to be merged with other text nodes to produce the final output.In short: Always use <xsl:sequence>.I use <xsl:value-of> in some special cases: when I am outputting multiple values and want to use the @separator attribute.
Dimitre Novatchev