My sample xml looks below: I need to get the distinct states from xml. I am using xslt 1.0 in vs 2010 editor.
<?xml version="1.0" encoding="utf-8" ?>
<states>
<node>
<value>2</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>NJ</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
</states>
My xslt looks like below:
<?xml version="1.0" encoding="utf-8"?>
<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"
xmlns:user="urn:my-scripts">
<xsl:output method="text" indent="yes"/>
<xsl:key name="st" match="//states/node/state" use="." />
<xsl:variable name="disst">
<xsl:for-each select="//states/node[contains(value,1)]/state[generate-id()=generate-id(key('st',.)[1])]" >
<xsl:choose>
<xsl:when test="(position() != 1)">
<xsl:value-of select="concat(', ',.)" disable-output-escaping="yes"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/" >
<xsl:value-of disable-output-escaping="yes" select="$disst"/>
</xsl:template>
</xsl:stylesheet>
Output: DE,NJ,NY
My above xml looks good for the above test xml.
If I change the xml as below:
<?xml version="1.0" encoding="utf-8" ?>
<states>
<node>
<value>2</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>NJ</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
</states>
It in not picking the state DE. Can any one suggest the suitable solution.Thanks in advance.
I need to find out the distinct states from the xml.
The problem here is your use of a predicate in your Muenchian grouping XPath:
[contains(value,1)]
This will often make Muenchian grouping fail to find all of the available distinct values. Instead, you should add the predicate to the key:
<xsl:key name="st" match="//states/node[contains(value, 1)]/state" use="." />
Alternatively, you can apply the predicate inside the grouping statement:
<xsl:apply-templates
select="//states/node
/state[generate-id() =
generate-id(key('st',.)[contains(../value, 1)][1])]" />
Full XSLT (with some improvements):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:user="urn:my-scripts">
<xsl:output method="text" indent="yes"/>
<xsl:key name="st" match="//states/node/state" use="." />
<xsl:variable name="a" select="1" />
<xsl:variable name="disst">
<xsl:apply-templates
select="//states/node
/state[generate-id() =
generate-id(key('st',.)[contains(../value, $a)][1])]" />
</xsl:variable>
<xsl:template match="state">
<xsl:if test="position() > 1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:value-of select ="." disable-output-escaping="yes" />
</xsl:template>
<xsl:template match="/" >
<xsl:value-of disable-output-escaping="yes" select="$disst"/>
</xsl:template>
</xsl:stylesheet>
Result when run on your sample XML:
DE,NJ,NY
Related
I am new to XSLT, and I am trying to create new group based on node value eventType so if eventType is alert, create new group event.
I am checking for last sibling
Input XML
<?xml version="1.0" encoding="UTF-8"?><Rowsets >
<Row>
<eventId>2</eventId>
<plantId>1020</plantId>
<workCenter>WC1</workCenter>
<eventType>alert</eventType>
<eventText>Downtime</eventText>
<eventDesc>WorkcenterDown</eventDesc>
</Row>
<Row>
<eventId>3</eventId>
<plantId>1021</plantId>
<workCenter>WC1</workCenter>
<eventType>alert</eventType>
<eventText>Downtime</eventText>
<eventDesc>WorkcenterDown</eventDesc>
</Row>
<Row>
<eventId>4</eventId>
<plantId>1020</plantId>
<workCenter>WC2</workCenter>
<eventType>incident</eventType>
<eventText>eventtext</eventText>
<eventDesc>failed</eventDesc>
</Row>
<Row>
<plantId>1020</plantId>
<workCenter>WC2</workCenter>
<eventType>incident</eventType>
<eventText>Text</eventText>
<eventDesc>failed</eventDesc>
</Row>
</Rowsets>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<Rowsets>
<Alert>
<element>
<Title>Downtime:DIA01</Title>
<eventDesc>WorkcenterDown</eventDesc>
</element>
<element>
<Title>Downtime:DIA01</Title>
<eventDesc>WorkcenterDown</eventDesc>
</element>
</Alert>
<Incident>
<element>
<Title>YAT < 60%:DIA01</Title>
<eventDesc>7 Parts in 60 minutes have failed</eventDesc>
</element>
<element>
<Title>YAT < 60%:DIA01</Title>
<eventDesc>7 Parts in 60 minutes have failed</eventDesc>
</element>
</Incident>
</Rowsets>
Based on eventType, I want to generate group.
I am using this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" />
<xsl:template match="/">
<Rowsets>
<Rowset>
<xsl:variable name="Type" select="'alert'"/>
<xsl:for-each select="/Rowsets/Rowset/Row">
<xsl:choose>
<xsl:when test="$Type = eventType">
<element>
<xsl:variable name="text" select="eventText"/>
<xsl:variable name="WC" select="workCenter"/>
<Title><xsl:value-of select="concat($text,':',$WC)" /></Title>
<xsl:copy-of select="eventDesc"/>
</element>
</xsl:when>
<xsl:otherwise>
<element>
<xsl:variable name="text" select="eventText"/>
<xsl:variable name="WC" select="workCenter"/>
<Title><xsl:value-of select="concat($text,':',$WC)" /></Title>
<xsl:copy-of select="eventDesc"/>
</element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Rowset>
</Rowsets>
</xsl:template>
</xsl:stylesheet>
Need help in generating id and key based on eventType
This is a grouping problem - and a rather trivial one at that. In XSLT 2.0 you could do simply:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="Rowsets">
<Rowsets>
<xsl:for-each-group select="Row" group-by="eventType">
<xsl:element name="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<element>
<Title>
<xsl:value-of select="eventText, workCenter" separator=":"/>
</Title>
<xsl:copy-of select="eventDesc"/>
</element>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</Rowsets>
</xsl:template>
</xsl:stylesheet>
Alternatively, with only two possible types, you could do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="Rowsets">
<Rowsets>
<Alert>
<xsl:apply-templates select="Row[eventType='alert']"/>
</Alert>
<Incident>
<xsl:apply-templates select="Row[eventType='incident']"/>
</Incident>
</Rowsets>
</xsl:template>
<xsl:template match="Row">
<element>
<Title>
<xsl:value-of select="eventText"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="workCenter"/>
</Title>
<xsl:copy-of select="eventDesc"/>
</element>
</xsl:template>
</xsl:stylesheet>
This is assuming you don't mind creating a group even if it is empty. Otherwise you would do:
<xsl:template match="Rowsets">
<Rowsets>
<xsl:variable name="alerts" select="Row[eventType='alert']"/>
<xsl:variable name="incidents" select="Row[eventType='incident']"/>
<xsl:if test="$alerts">
<Alert>
<xsl:apply-templates select="Row[eventType='alert']"/>
</Alert>
</xsl:if>
<xsl:if test="$incidents">
<Incident>
<xsl:apply-templates select="Row[eventType='incident']"/>
</Incident>
</xsl:if>
</Rowsets>
</xsl:template>
P.S. Note that XML is case-sensitive: <Alert> is not the same as <alert>.
I am having a failure of imagination in how to effectively solve this problem. My actual data set has thousands of thousands of records. Each record indicates its location in a taxonomic structure. I need to create that taxonomic structure and place the records within that structure (e.g., records that indicate they go in "/a/b/c" end up in the "/a/b/c" but there is only one each of the taxonomic levels "a", "b", and "c"). Due to client confidentiality, I've posted a naive representation of this here.
Using XSLT 3 is desirable. I know there is a solution to this using xsl:iterate but I cannot figure it out.
Input:
<outer>
<record>
<id>rec1</id>
<taxNodes>
<node>
<id>1</id>
<note>First level</note>
<node>
<id>node2a</id>
<note>Second level Entry A</note>
</node>
</node>
</taxNodes>
</record>
<record>
<id>rec3</id>
<taxNodes>
<node>
<id>1</id>
<note>First level</note>
<node>
<id>node2b</id>
<note>Second level Entry B</note>
</node>
</node>
</taxNodes>
</record>
<record>
<id>rec4</id>
<taxNodes>
<node>
<id>1</id>
<note>First level</note>
<node>
<id>node2b</id>
<note>Second level Entry B</note>
</node>
</node>
</taxNodes>
</record>
</outer>
Desired Output:
<outer>
<node>
<id>1</id>
<note>First level</note>
<node>
<id>node2a</id>
<note>Second level Entry A</note>
<records>
<record>
<id>rec1</id>
</record>
</records>
</node>
<node>
<id>node2b</id>
<note>Second level Entry B</note>
<records>
<record>
<id>rec3</id>
</record>
<record>
<id>rec4</id>
</record>
</records>
</node>
</node>
</outer>
I think this can be seen as a grouping problem and then solved using a recursive function using xsl:for-each-group:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs math mf"
version="3.0">
<xsl:output indent="yes"/>
<xsl:function name="mf:group" as="node()*">
<xsl:param name="input-nodes" as="element(node)*"/>
<xsl:for-each-group select="$input-nodes" group-by="id">
<xsl:copy>
<xsl:copy-of select="id, note"/>
<xsl:choose>
<xsl:when test="current-group()/node">
<xsl:sequence select="mf:group(current-group()/node)"/>
</xsl:when>
<xsl:otherwise>
<records>
<xsl:apply-templates select="current-group()/ancestor::record"/>
</records>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="outer">
<xsl:copy>
<xsl:sequence select="mf:group(record/taxNodes/node)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="record">
<xsl:copy>
<xsl:copy-of select="id"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
That gives the desired result I think for the input sample you have posted, I am not sure it will do for other inputs, mainly as I am not sure how variable the input can be, I think if I understand the spec 'there is only one each of the taxonomic levels "a", "b", and "c"' correctly then it should work fine.
As for having a huge input file and using XSLT 3.0 (with streaming?), I am not sure that a streaming solution is possible, due to the nature of the problem where we need to recursively group the whole set of input nodes.
This stylesheet provides a solution but does so using a function for looping.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:local="http://www.local.com"
exclude-result-prefixes="xs local"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="maxTaxonomyDepth" select="max(//node[not(node)]/count(ancestor-or-self::node))" as="xs:integer"/>
<xsl:sequence select="local:getTaxNodesForDepth(outer, 1, $maxTaxonomyDepth, '')" />
</xsl:template>
<xsl:function name="local:getTaxNodesForDepth">
<xsl:param name="out" as="element()" />
<xsl:param name="curDepth" as="xs:integer" />
<xsl:param name="maxDepth" as="xs:integer" />
<xsl:param name="parentId" as="xs:string*" />
<xsl:for-each select="distinct-values($out/record/taxNodes//node[count(ancestor-or-self::node) = $curDepth]
[if ($curDepth > 1) then parent::node/id/normalize-space(.) = $parentId else true()]
/id/normalize-space(.))">
<xsl:variable name="context" select="." as="xs:string" />
<node>
<xsl:sequence select="($out/record/taxNodes//node[count(ancestor-or-self::node) = $curDepth][id/normalize-space(.) = $context])[1]/(id | descriptor)" />
<xsl:apply-templates select="$out/record[taxNodes/descendant::node[last()][id/normalize-space(.) = $context]]" />
<xsl:choose>
<xsl:when test="$curDepth < $maxDepth">
<xsl:sequence select="local:getTaxNodesForDepth($out, $curDepth + 1, $maxDepth,
($out/record/taxNodes//node[count(ancestor-or-self::node) = $curDepth][id/normalize-space(.) = $context])[1]/id/normalize-space(.))" />
</xsl:when>
</xsl:choose>
</node>
</xsl:for-each>
</xsl:function>
<xsl:template match="taxNodes"/>
<xsl:template match="#*|node()" mode="#default">
<xsl:copy>
<xsl:apply-templates select="#*" mode="#current"/>
<xsl:apply-templates select="node()" mode="#current"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
having performance issues with my xslt code:
this is my input file:
<?xml version="1.0" encoding="UTF-8"?>
<Products>
<Product ID="111111" Type="Item" ParentID="7402">
<Name>ABC</Name>
<Values>
<Value AttributeID="11">8.00</Value>
<Value AttributeID="12">8.00</Value>
<Value AttributeID="13">0.18</Value>
</Values>
<Product ID="B582B65D" Type="UID" ParentID="111111">
<Values>
<Value AttributeID="11">8.00</Value>
<Value AttributeID="12">8.00</Value>
<Value AttributeID="13">0.18</Value>
<Value AttributeID="14">0.18</Value>
</Values>
</Product>
</Product>
<Product ID="222222" Type="Item" ParentID="7402">
<Name>XYZ</Name>
<Values>
<Value AttributeID="12">8.00</Value>
<Value AttributeID="13">8.00</Value>
<Value AttributeID="15">0.18</Value>
</Values>
<Product ID="B582B65D" Type="UID" ParentID="111111">
<Values>
<Value AttributeID="11">8.00</Value>
<Value AttributeID="12">8.00</Value>
<Value AttributeID="16">0.18</Value>
<Value AttributeID="18">0.18</Value>
</Values>
</Product>
</Product>
</Products>
and this is my transformation code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math"
extension-element-prefixes="math">
<xsl:output method="xml" indent="yes" />
<xsl:param name="file2" select="document('Mapping.xml')" />
<xsl:template match="/Products">
<Products>
<xsl:for-each select="Product">
<xsl:call-template name="item" />
</xsl:for-each>
</Products>
</xsl:template>
<xsl:template name="item">
<Product type="{./#Type}" ID="{./#ID}">
<xsl:for-each select="./Values/Value">
<xsl:variable name="Idval" select="#AttributeID" />
<xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]/#group}">
<xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]}">
<xsl:attribute name="ID"><xsl:value-of select="$Idval"/></xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:element>
</xsl:for-each>
<xsl:call-template name="uid" />
</Product>
</xsl:template>
<xsl:template name="uid">
<Product type="{./Product/#Type}" ParentId="{./Product/#ParentID}">
<xsl:for-each select="./Product/Values/Value">
<xsl:variable name="Idval" select="#AttributeID" />
<xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]/#group}">
<xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]}">
<xsl:attribute name="ID"><xsl:value-of select="$Idval"/></xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:element>
</xsl:for-each>
</Product>
</xsl:template>
</xsl:stylesheet>
above xslt is using below xml file for mapping attribute id to corresponding name and group
Mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<Groups>
<AttributeID ID="11" group="Pack1">Height</AttributeID>
<AttributeID ID="12" group="Pack2">Width</AttributeID>
<AttributeID ID="13" group="Pack1">Depth</AttributeID>
<AttributeID ID="14" group="Pack3">Length</AttributeID>
<AttributeID ID="15" group="Pack3">Lbs</AttributeID>
<AttributeID ID="16" group="Pack4">Litre</AttributeID>
</Groups>
Replace the use of expressions like
select="$file2//Groups/AttributeID[#ID=$Idval]"
with a key:
<xsl:key name="ID" match="Groups/AttributeID" use="#ID"/>
and then
select="key('ID', $IDval, $file)"/>
Alternatively, Saxon-EE will do this optimization for you automatically.
The key() function with 3 arguments is XSLT 2.0 syntax. If you have the misfortune to be using XSLT 1.0, you have to write a dummy xsl:for-each that makes $file the context item, because key() will only select within the document containing the context item.
Define a key for the cross document lookup: <xsl:key name="by-id" match="Groups/AttributeID" use="#ID"/>, then (assuming an XSLT 2.0 processor) you can simplify expressions like <xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]/#group}"> to <xsl:element name="{key('by-id', #AttributeID, $file2)/#group">. Make the same change for the other cross references you have, i.e. all those $file2//Groups/AttributeID[#ID=$Idval] expressions should use the key lookup.
Making the simplified assumption that your second file isn't too big, you want to fold the values there into your template. It would work with XSLT 1.0 too. Something like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math"
extension-element-prefixes="math">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<Products>
<xsl:apply-templates select="/Products/Product" />
</Products>
</xsl:template>
<xsl:template match="Product">
<xsl:element name="Product">
<xsl:apply-templates select="#*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Name">
<Name>
<xsl:value-of select="." />
</Name>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="Values">
<Values>
<xsl:apply-templates />
</Values>
</xsl:template>
<!-- Templates for individual AttributeIDs, only when there are few -->
<xsl:template match="Value[#AttributeID='11']">
<Pack1>
<xsl:element name="Height">
<xsl:attribute name="ID">
<xsl:value-of select="#AttributeID" />
</xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</Pack1>
</xsl:template>
<!-- Repeat for the other AttributeID values -->
</xsl:stylesheet>
(Typed off my head, will contain typos)
Of course if it is big Michael's advice is the best course of action.
I have a node set and I'd like to use a predicate to select from it only those nodes which have a template's current node as their parent.
I'm using XSL 1.0 and msxsl.
In the following non-working code, the predicate [parent::current()] does not work as I want it to as current() is not a valid NodeTest:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:variable name="set" select="//node[#attr1 = 'bbb']"/>
<xsl:template name="TemplateA">
<xsl:for-each select="$set[parent::current()]">
<result-node>
<xsl:value-of select="#value"/>
<xsl:text>A</xsl:text>
</result-node>
</xsl:for-each>
</xsl:template>
<xsl:template name="TemplateB">
<xsl:for-each select="$set[parent::current()]">
<result-node>
<xsl:value-of select="#value"/>
<xsl:text>B</xsl:text>
</result-node>
</xsl:for-each>
</xsl:template>
<xsl:template match="/body">
<result>
<xsl:for-each select="parentNode">
<xsl:call-template name="TemplateA" />
<xsl:call-template name="TemplateB" />
</xsl:for-each>
</result>
</xsl:template>
</xsl:transform>
Here is a sample XML to feed the above:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<parentNode>
<node attr1="aaa" value="1" />
<node attr1="bbb" value="2" />
</parentNode>
<parentNode>
<node attr1="aaa" value="3" />
<node attr1="bbb" value="4" />
</parentNode>
</body>
Here is the desired output:
<result>
<result-node>2A</result-node>
<result-node>2B</result-node>
<result-node>4A</result-node>
<result-node>4B</result-node>
</result>
The above example can also be found here: http://xsltransform.net/jyH9rMg
The way to compare nodes for identity in XSLT 1.0 is to use generate-id():
<xsl:for-each select="$set[generate-id(..) = generate-id(current())]">
.. is a shorthand for parent::node() and refers to the parent of the context node (the node being tested by this execution of the predicate).
Hi I am pretty new to XSLT so need some help on simple XSL code.
My input XML
<?xml version="1.0" encoding="ASCII"?>
<Node Name="Person" Received="1" Good="1" Bad="0" Condition="byPerson:1111">
</Node>
<Node Name="Person" Received="1" Good="1" Bad="0" Condition="byPerson:1111">
</Node>
<Node Name="Person" Received="1" Good="1" Bad="0" Condition="byPerson:2222">
</Node>
<Node Name="Person" Received="1" Good="1" Bad="0" Condition="byPerson:2222">
</Node>
<Node Name="Person" Received="1" Good="1" Bad="0" Condition="byPerson:3333">
</Node>
And i am expecting the result as sum of all Received , good and Bad but that need to added only once per unique condition.
Something like this
<?xml version="1.0" encoding="ASCII"?>
<Received>3</Received >
<Good>3</Good>
<Bad>0</Bad>
i was trying below code but no success so far just getting sum of everything, would like to get sum on only each 'Condition' only once.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:value-of select= "sum(Node#Received)"/>
<xsl:value-of select= "sum(Node/#Good)"/>
<xsl:value-of select= "sum(Node/#Bad)"/>
</xsl:template>
The following stylesheet uses an xsl:key to group the <node> elements by the value of the #Condition. Using the Meunchien method with key() and generate-id(), to select the first node element for each unique #Condition and then generate the sum() of the attributes of the selected node elements.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:key name="nodesByCondition" match="Node" use="#Condition"/>
<xsl:template match="/">
<results>
<xsl:variable name="distinctNodes"
select="*/Node[generate-id() =
generate-id(key('nodesByCondition', #Condition)[1])]"/>
<Received>
<xsl:value-of select= "sum($distinctNodes/#Received)"/>
</Received>
<Good><xsl:value-of select= "sum($distinctNodes/#Good)"/></Good>
<Bad><xsl:value-of select= "sum($distinctNodes/#Bad)"/></Bad>
</results>
</xsl:template>
</xsl:stylesheet>
in XSLT 2.0 you can use distinct-values()