I am creating an XSLT and i want to select a particular node, only if one of its child element's value is between a range. The range is to be specified using parameters in the xsl file.
The XML file is like
<root>
<org>
<name>foo</name>
<chief>100</chief>
</org>
<org parent="foo">
<name>foo2</name>
<chief>106</chief>
</org>
</root>
The XSLT so far is
<xsl:param name="fromRange">99</xsl:param>
<xsl:param name="toRange">105</xsl:param>
<xsl:template match="/">
<xsl:element name="orgo">
<xsl:apply-templates select="//org[not(#parent)]"/>
</xsl:element>
</xsl:template>
I want to restrict the org node from being processed whose < chief > node's value is not in range
I want to select a particular node,
only if one of its child element's
value is between a range. The range is
to be specified using parameters in
the xsl file.
I also want the restriction that the
node should not have a parent
attribute along with the range
Use this expression as the value of the select attribute of <xsl:apply-templates>:
org[not(#parent) and chief >= $fromRange and not(chief > $toRange)]
In XSLT 2.0 it is legal to have variables/parameters in the match pattern.
Therefore, one could write:
<xsl:template match=
"org[#parent or not(chief >= $fromRange ) or chief > $toRange]"/>
thus effectively excluding all such org elements from processing.
Then the template matching the document node is simply:
<xsl:template match="/">
<orgo>
<xsl:apply-templates/>
</orgo>
</xsl:template>
This is better than the XSLT 1.0 solution, because it is more "push-style".
//org[chief < $fromRange and not(#parent)]
|//org[chief > $toRange and not(#parent)]
This expression will exclude all nodes that are in the range specified by fromRange and toRange.
<?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" indent="yes"/>
<xsl:param name="fromRange">99</xsl:param>
<xsl:param name="toRange">105</xsl:param>
<xsl:template match="/">
<xsl:element name="orgo">
<xsl:apply-templates select="//org[chief < $fromRange and not(#parent)]|//org[chief > $toRange and not(#parent)]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Related
I need to form an output xml in which I need to have the field value as a tag and then the subsequent next field value as value for the created tag.
<PrimaryKey>
<PK1FeildName>CONNO</PK1FeildName>
<PK1Value>001</PK1Value>
<PK2FeildName>INNO</PK2FeildName>
<PK2Value>123</PK2Value>
<PK3FeildName>CONNO</PK3FeildName>
<PK3Value>011</PK3Value>
</PrimaryKey>
Expected output:
<PrimaryKey>
<CONNO>001</CONNO>
<INNO>123</INNO>
<CONNO>011</CONNO>
</PrimaryKey>
If we consider that the elements will always appear in pairs where the value of the first element is the tag name and the second element is the value, then this works :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="PrimaryKey">
<xsl:element name="PrimaryKey">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:if test="count(preceding-sibling::*) mod 2 = 0">
<xsl:element name="{.}">
<xsl:value-of select="following-sibling::*[1]"/>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
You can try it here : https://xsltfiddle.liberty-development.net/bwdws3
Edit to answer comment
Example XML :
<Document>
<PrimaryKey>
<PK1FeildName>CONNO</PK1FeildName>
<PK1Value>001</PK1Value>
<PK2FeildName>INNO</PK2FeildName>
<PK2Value>123</PK2Value>
<PK3FeildName>CONNO</PK3FeildName>
<PK3Value>011</PK3Value>
</PrimaryKey>
<PrimaryKey>
<PK1FeildName>CONNO2</PK1FeildName>
<PK1Value>0012</PK1Value>
<PK2FeildName>INNO2</PK2FeildName>
<PK2Value>1232</PK2Value>
<PK3FeildName>CONNO2</PK3FeildName>
<PK3Value>0112</PK3Value>
</PrimaryKey>
</Document>
Modified XSLT :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:element name="PrimaryKey">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="PrimaryKey/*">
<xsl:if test="count(preceding-sibling::*) mod 2 = 0">
<xsl:element name="{.}">
<xsl:value-of select="following-sibling::*[1]"/>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bwdws3/1
For completeness, here's a solution that uses ESQL instead of XSLT:
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
-- Get a reference to the first instance of 'PK1FeildName'
DECLARE refPK REFERENCE TO InputRoot.XMLNSC.PrimaryKey.PK1FeildName[1];
WHILE LASTMOVE(refPK) DO
-- remember the field name
DECLARE keyName CHARACTER FIELDVALUE(refPK);
MOVE refPK NEXTSIBLING;
-- create the next field in the output
CREATE LASTCHILD OF OutputRoot.XMLNSC.Document.PrimaryKey TYPE NameValue NAME keyName VALUE FIELDVALUE(refPK);
MOVE refPK NEXTSIBLING;
END WHILE;
RETURN TRUE;
END;
Whether you use XSL or ESQL, I would recommend that you validate the incoming XML against an XSD because the mapping code depends on seeing specific tags in a specific order. You could add code to check the tag names, but XSD validation is a much easier solution.
I am trying to find an xslt solution to the following problem I have.
I want to find a set of 3 subsequent rows that share the node name and an attribute but have a different values. The first row in the input contains an identifier, the second and third row contain values from a source system. I want to find the sets where the second and third row have different values.
E.g.
<eba7:mi235 contextRef="I-2014-E-dim-x43-x9-x156-x51-x14">78923</eba7:mi235>
<eba7:mi235 contextRef="I-2014-E-dim-x43-x9-x156-x51-x14">1111</eba7:mi235>
<eba7:mi235 contextRef="I-2014-E-dim-x43-x9-x156-x51-x14">2222</eba7:mi235>
There might also be sets of rows with only an identifier, a set of a row with an identifier and only one row with a value from the source system or a set of rows where the second and third row have the same value.
E.g.
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x24-x195-x10-x4">78748</eba7:mi310>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x24-x195-x10-x4">0</eba7:mi310>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x25-x195-x10-x4">78804</eba7:mi310>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x25-x195-x10-x4">12345</eba7:mi310>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x25-x195-x10-x4">12345</eba7:mi310>
These I don't want to find in the output.
The output I want to create is
<eba7:mi235 id="78923" value1="1111" value2="2222" />
The structure of the input is such that the rows are always ordered like this. So I tried to access them using position, but that didn't work.
Could anybody point me in the right direction? Is using position the right way?
I have attached an file with the input data below
Thanks.
Paul.
<?xml version="1.0" encoding="utf-8"?>
<xbrl xml:lang="en" xmlns="http://www.xbrl.org/2003/instance" xmlns:eba7="http://www.eba.europa.eu/xbrl/crr/dict/met" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:link="http://www.xbrl.org/2003/linkbase">
<link:schemaRef xlink:type="simple" xlink:href="http://www.eba.europa.eu/eu/fr/xbrl/crr/fws/corep/its-2013-02/2014-07-31/mod/corep_con.xsd" />
<context id="I-2014-E">
<entity>
<identifier scheme="http://www.dnb.nl/id">578</identifier>
</entity>
<period>
<instant>2014-12-31</instant>
</period>
</context>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x24-x195-x10-x4">78748</eba7:mi310>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x24-x195-x10-x4">0</eba7:mi310>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x25-x195-x10-x4">78804</eba7:mi310>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x25-x195-x10-x4">12345</eba7:mi310>
<eba7:mi310 contextRef="I-2014-E-dim-x42-x9-x25-x195-x10-x4">12345</eba7:mi310>
<eba7:mi235 contextRef="I-2014-E-dim-x43-x9-x156-x51-x14">78923</eba7:mi235>
<eba7:mi235 contextRef="I-2014-E-dim-x43-x9-x156-x51-x14">1111</eba7:mi235>
<eba7:mi235 contextRef="I-2014-E-dim-x43-x9-x156-x51-x14">2222</eba7:mi235>
</xbrl>
I don't think the question is defined well enough; it can be interpreted in several ways.
If we assume that you want to:
Group all the given elements based on both the tag name and the #contextRef value being the same; with the mutual position of the elements being irrelevant for this purpose;
Count the distinct values in each group; if there are three or more, write an element with the common tag name to the output, and add a numbered attribute for each distinct value in this group;
then it would be probably best to do something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xbrli="http://www.xbrl.org/2003/instance"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="k1" match="*" use="concat(name(), '|', #contextRef)"/>
<xsl:key name="k2" match="*" use="concat(name(), '|', #contextRef, '|', .)"/>
<xsl:template match="/xbrli:xbrl">
<xsl:copy>
<xsl:for-each select="*[count(.|key('k1', concat(name(), '|', #contextRef))[1])=1]">
<xsl:variable name="distinct-values" select="key('k1', concat(name(), '|', #contextRef)) [count(.|key('k2', concat(name(), '|', #contextRef, '|', .))[1])=1]"/>
<xsl:if test="count($distinct-values) >= 3">
<xsl:copy>
<xsl:for-each select="$distinct-values">
<xsl:attribute name="value{position()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</xsl:copy>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to the following well-formed test input:
<xbrl xmlns="http://www.xbrl.org/2003/instance" xmlns:eba7="http://www.eba.europa.eu/xbrl/crr/dict/met">
<eba7:a contextRef="x">11</eba7:a>
<eba7:a contextRef="x">12</eba7:a>
<eba7:a contextRef="y">21</eba7:a>
<eba7:a contextRef="y">22</eba7:a>
<eba7:a contextRef="y">23</eba7:a>
<eba7:b contextRef="x">31</eba7:b>
<eba7:b contextRef="x">32</eba7:b>
<eba7:b contextRef="x">33</eba7:b>
<eba7:b contextRef="x">33</eba7:b>
<eba7:c contextRef="x">41</eba7:c>
<eba7:c contextRef="x">41</eba7:c>
<eba7:c contextRef="x">42</eba7:c>
<eba7:c contextRef="x">42</eba7:c>
</xbrl>
the result will be:
<?xml version="1.0" encoding="utf-8"?>
<xbrl xmlns="http://www.xbrl.org/2003/instance" xmlns:eba7="http://www.eba.europa.eu/xbrl/crr/dict/met">
<eba7:a value1="21" value2="22" value3="23"/>
<eba7:b value1="31" value2="32" value3="33"/>
</xbrl>
Note:
You must be familiar with the Muenchian grouping method in order to understand this;
Numbered attributes are not good XML practice. I would suggest you (or the powers that be) reconsider this requirement.
Would this stylesheet solve your problem:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xbrli="http://www.xbrl.org/2003/instance" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:key name="elements" match="*" use="#contextRef"/>
<xsl:template match="/xbrli:xbrl">
<xsl:copy>
<xsl:apply-templates select="*[#contextRef
and count(key('elements', #contextRef)) = 3
and key('elements', #contextRef)[2] != key('elements', #contextRef)[3]
and count(. | key('elements', #contextRef)[1]) = 1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="."/>
</xsl:attribute>
<xsl:attribute name="value1">
<xsl:value-of select="key('elements', #contextRef)[2]"/>
</xsl:attribute>
<xsl:attribute name="value2">
<xsl:value-of select="key('elements', #contextRef)[3]"/>
</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Here, a key is declared to match elements with #contextRef being the identifier. The first template applies templates to the first elements with unique #contextRef(and also those which match other conditions like total elements with that #contextRef must be 3, and the second and thrid elements must not have the same value).
The next template matches these elements(from the first template), and creates the further output.
I want to check if in my XML exists node that has type attribute containing string type_attachment_.
Is it a correct way to check it?
<xsl:if test="count(*[contains(#Type, 'type_attachment_')]) > 0">
something
</xsl:if>
I don't know how nested can this node be. It can be for example as simple as that:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl"?>
<hello-world>
<greeter>
<dsdsds>An XSLT Programmer
<greeting type = 'type_attachment_'>Hello, World!
</greeting>
</dsdsds>
</greeter>
</hello-world>
but can also contain this node nested in different other elements.
Expressions that match existing nodes are truthy. Expressions that do not match any nodes are falsy.
Therefore, you don't need to count the set of nodes returned. Simply test to see if anything matches.
<xsl:if test="*[contains(#Type, 'type_attachment')]">
something
</xsl:if>
Find out an example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:param name="filt">
<filters>
<ritem type="type_attachment_" relateditemnumber="8901037"/>
<ritem relateditemnumber="8901038"/>
<ritem type="type_attachment_" relateditemnumber="8901039"/>
<ritem relateditemnumber="8901040"/>
</filters>
</xsl:param>
<xsl:template match="/">
<xsl:for-each select="$filt/filters/ritem[#type='type_attachment_']">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
OUTPUT:
<ritem type="type_attachment_" relateditemnumber="8901037"/>
<ritem type="type_attachment_" relateditemnumber="8901039"/>
I'm using the Muenchian method to group nodes in an XML document.
As part of my output, I'd like to select all of the nodes to which I have not assigned a key.
I've tried
<xsl:apply-templates select="*[key('kcWWPN','')]"/>
but that doesn't appear to be working correctly, in that it is selecting no nodes.
Any suggestions on the right way to do this?
As part of my output, I'd like to select all of the nodes to which I
have not assigned a key.
Good question, +1.
Here are two different, but simple solutions:
For simplicity I will assume that we are keying only elements, but the two solutions below can naturally be extended to cover other types of nodes.
I. Simply, define a counter-key:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kDByClass" match="d" use="#class"/>
<xsl:key name="kCounterKey" match="*[not(self::d)]" use="."/>
<xsl:template match="*[key('kCounterKey', .)]">
Counter Keyed: <xsl:value-of select="name()"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when this transformation is applied on the following XML document (borrowed from #lwburk):
<root>
<d class="test">1</d>
<d class="test">2</d>
<d class="something">1</d>
<q>3</q>
</root>
the wanted, correct result is produced:
Counter Keyed: root
Counter Keyed: q
II. Use the set difference of all elements and all keyed elements (the latter is found using Muenchian grouping):
This solution is simpler than the first, because one doesn't need to compose a counter-key:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kDByClass" match="d" use="#class"/>
<xsl:key name="kCounterKey" match="*[not(self::d)]" use="."/>
<xsl:variable name="vKeyedValues" select=
"//*[generate-id()
= generate-id(key('kDByClass', #class)[1])
]
/#class
"/>
<xsl:variable name="vKeyedElements" select=
"key('kDByClass', $vKeyedValues)"/>
<xsl:variable name="vNonKeyedElements" select=
"//*[not(count(.|$vKeyedElements) = count($vKeyedElements))]
"/>
<xsl:template match="/">
<xsl:for-each select="$vNonKeyedElements">
Not Keyed: <xsl:value-of select="name()"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when this transformation is applied on the same XML document (above), again the same, correct result is produced:
Not Keyed: root
Not Keyed: q
Note: The last solution may not work when the key is not a node, but result of a function, such as substring-before(). In this case we simply modify the original key, so thet its use attribute is simply: use="." and use this solution with the modified original key. It can be prooven that this procedure produces the correct, wanted set of elements.
You simply need to re-create the key that would have been used for the nodes you want to select and attempt to retrieve the values for that key. If the set returned for that key does not contain the current node, then it should be selected.
For example, suppose the keys were created using the concatenation of each element's class attribute value and its string value. We could select all elements in the document that do not have an assigned key using the following:
<xsl:apply-templates
select="//*[not(count(.|key('byClass', concat(#class, '|', .)))=
count(key('byClass', concat(#class, '|', .))))]"/>
Here's a complete demo:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="byClass" match="d" use="concat(#class, '|', .)"/>
<xsl:template match="/">
<xsl:apply-templates
select="//*[not(count(.|key('byClass', concat(#class, '|', .)))=
count(key('byClass', concat(#class, '|', .))))]"
mode="test"/>
</xsl:template>
<xsl:template match="*" mode="test">
<xsl:value-of
select="concat('Node ', local-name(),
' not assigned a key', '
')"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Applied to this input:
<root>
<d class="test">1</d>
<d class="test">2</d>
<d class="something">1</d>
<q class="test">1</q>
</root>
It produces:
Node root not assigned a key
Node q not assigned a key
Note that the key produced by q is an actual key that maps to a group of nodes in the document, but this element is not in the set returned by key('byClass', 'test|1'), so we say that this node has not been assigned a key.
Note also that the empty string ('') is a perfectly valid key, which is why this doesn't do what you were hoping it would:
<xsl:apply-templates select="*[key('kcWWPN', '')]"/>
I have an xsl stylesheet giving just about what is needed, except there are values outputted outside the tags, . Is there a way to remove them? The scenario is that the desired output is a total invoice amount for invoices that appear more than once. Each time the xslt is executed the parameter p1 contains the InvoiceNumber to total. The code below shows that parameter, p1, hardcoded to '351510'.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Invoices/Invoice[InvoiceNumber=351510][1]/InvoiceNumber">
<xsl:copy>
<xsl:apply-templates select="/Invoices/Invoice[InvoiceNumber=351510][1]/InvoiceAmount"/>
</xsl:copy>
</xsl:template>
<xsl:param name="tempvar"/>
<xsl:template name="InvTotal" match="/Invoices/Invoice[InvoiceNumber=351510][1]/InvoiceNumber">
<xsl:variable name="p1" select="351510" />
<xsl:if test="/Invoices/Invoice/InvoiceNumber[. = $p1]">
<!--<xsl:if test="$test = $p1" >-->
<InvoiceAmount>
<xsl:value-of select="sum(../../Invoice[InvoiceNumber=351510]/InvoiceAmount)"/>
</InvoiceAmount>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Here is the input:
<Invoices>
- <Invoice>
<InvoiceNumber>351510</InvoiceNumber>
<InvoiceAmount>137.00</InvoiceAmount>
</Invoice>
- <Invoice>
<InvoiceNumber>351510</InvoiceNumber>
<InvoiceAmount>363.00</InvoiceAmount>
</Invoice>
- <Invoice>
<InvoiceNumber>351511</InvoiceNumber>
<InvoiceAmount>239.50</InvoiceAmount>
</Invoice>
</Invoices>
Here is the output:
<InvoiceAmount>500</InvoiceAmount>137.00351510363.00351511239.50
Here is desired output:
<InvoiceAmount>500</InvoiceAmount>
Also, thank you goes to lwburk who got me this far.
Adding
<xsl:template match="text()"/>
should help.
I do not get the same results as you posted (only 351510137.00351510363.00351511239.50, all the text nodes), and I do not know the purpose of tempvar (unused).
Since it appears that all you need is the sum of InvoiceAmount values for a specific InvoiceNumber, just keep it simple and ignore everything else:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="invoiceNumber"/>
<xsl:template match="/">
<InvoiceAmount>
<xsl:value-of select="sum(/Invoices/Invoice[InvoiceNumber=$invoiceNumber]/InvoiceAmount)"/>
</InvoiceAmount>
</xsl:template>
</xsl:stylesheet>
You can pass the InvoiceNumber to process via the parameter invoiceNumber, or you can hardcode it if you like (see version 1).
Note: should you prefer a number format like e.g. #.00 (fixed decimals) for the sum, then you can also use the format-number(…) function.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pNum" select="351510"/>
<xsl:key name="kInvAmmtByNumber" match="InvoiceAmount"
use="../InvoiceNumber"/>
<xsl:variable name="vInvoiceAmounts" select=
"key('kInvAmmtByNumber', $pNum)"/>
<xsl:variable name="vIdInvAmount1" select=
"generate-id($vInvoiceAmounts[1])"/>
<xsl:template match="InvoiceAmount">
<xsl:if test="generate-id() = $vIdInvAmount1">
<InvoiceAmount>
<xsl:value-of select="sum($vInvoiceAmounts)"/>
</InvoiceAmount>
</xsl:if>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided XML file:
<Invoices>
<Invoice>
<InvoiceNumber>351510</InvoiceNumber>
<InvoiceAmount>137.50</InvoiceAmount>
</Invoice>
<Invoice>
<InvoiceNumber>351510</InvoiceNumber>
<InvoiceAmount>362.50</InvoiceAmount>
</Invoice>
<Invoice>
<InvoiceNumber>351511</InvoiceNumber>
<InvoiceAmount>239.50</InvoiceAmount>
</Invoice>
</Invoices>
produces exactly the wanted, correct result:
<InvoiceAmount>500</InvoiceAmount>
Explanation:
The wanted invoice number is passed to the transformation as the value of the external/global parameter $pNum .
We use a key that indexes all InvoiceAmount elements by their corresponding InvoiceNumber values.
Using that key we define the variable $vInvoiceAmounts that contains the node-set of all InvoiceAmount elements the value of whose corresponding InvoiceNumber element is the same as the value of the external parameter $pNum.
We also define a variable ($vIdInvAmount1) that contains a unique Id of the first such InvoiceAmount element.
There is a template that matches any InvoiceAmount element. It checks if the matched element is the first of the elements contained in the node-set $vInvoiceAmounts. If so, a InvoiceAmount element is generated with a single text-node child, whose value is the sum of all InvoiceAmount elements contained in $vInvoiceAmounts. Otherwise nothing is done.
Finally, there is a second template that matches any text node and does nothing (deletes it in the output), effectively overriding the unwanted side effect of the default XSLT processing -- the outputting of unwanted text.