Trying to understand XSLT Muenchian Method transformation - xslt

I came across the Muenchian Method when looking for a way of grouping elements in an XML file that has been produced by converted a CSV file.
Source
<file>
<patient>
<Lab_Specimen_Number>L,18.1342718.Y</Lab_Specimen_Number>
<Patient_Number>LOC0000015</Patient_Number>
<ORG/>
<Specimen/>
<Antibiotic_Amox_Ampicillin/>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1342727.V</Lab_Specimen_Number>
<Patient_Number>LOC0000001</Patient_Number>
<ORG>Coliform</ORG>
<Specimen>L,18.1342727.VA</Specimen>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</patient>
<patient>
<Lab_Specimen_Number/>
<Patient_Number/>
<ORG>Staphylococcus aureus</ORG>
<Specimen>L,18.1342727.VA</Specimen>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1346290.T</Lab_Specimen_Number>
<Patient_Number>LOC0000001</Patient_Number>
<ORG>Coliform</ORG>
<Specimen>L,18.1346290.TA</Specimen>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1342713.X</Lab_Specimen_Number>
<Patient_Number>LOC0000009</Patient_Number>
<ORG/>
<Specimen/>
<Antibiotic_Amox_Ampicillin/>
</patient>
</file>
Based on the article I have changed the match when assigning a key to patient[Specimen != ''] instead of just patient as it is possible for the Specimen value to be blank and these would be missing from the final output if just patient was used.
Transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="patients-by-specimen" match="patient[Specimen != '']" use="Specimen" />
<xsl:template match="file">
<file>
<xsl:for-each select="patient[count(. | key('patients-by-specimen', Specimen)[1]) = 1]">
<patient>
<xsl:copy-of select="Lab_Specimen_Number" />
<xsl:copy-of select="Patient_Number" />
<Specimen>
<xsl:copy-of select="Specimen" />
<Organisms>
<xsl:for-each select="key('patients-by-specimen', Specimen)">
<Organism>
<xsl:copy-of select="ORG"/>
<xsl:copy-of select="Antibiotic_Amox_Ampicillin"/>
</Organism>
</xsl:for-each>
</Organisms>
</Specimen>
</patient>
</xsl:for-each>
</file>
</xsl:template>
</xsl:stylesheet>
Whilst the transformation above gives me the desired output I don't fully understand how this line is working:
<xsl:for-each select="patient[count(. | key('patients-by-specimen', Specimen)[1]) = 1]">
Could someone explain this iteration in the context of my source file?
Output
<file>
<patient>
<Lab_Specimen_Number>L,18.1342718.Y</Lab_Specimen_Number>
<Patient_Number>LOC0000015</Patient_Number>
<Specimen>
<Specimen/>
<Organisms/>
</Specimen>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1342727.V</Lab_Specimen_Number>
<Patient_Number>LOC0000001</Patient_Number>
<Specimen>
<Specimen>L,18.1342727.VA</Specimen>
<Organisms>
<Organism>
<ORG>Coliform</ORG>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</Organism>
<Organism>
<ORG>Staphylococcus aureus</ORG>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</Organism>
</Organisms>
</Specimen>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1346290.T</Lab_Specimen_Number>
<Patient_Number>LOC0000001</Patient_Number>
<Specimen>
<Specimen>L,18.1346290.TA</Specimen>
<Organisms>
<Organism>
<ORG>Coliform</ORG>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</Organism>
</Organisms>
</Specimen>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1342713.X</Lab_Specimen_Number>
<Patient_Number>LOC0000009</Patient_Number>
<Specimen>
<Specimen/>
<Organisms/>
</Specimen>
</patient>
</file>

Related

xslt, group by node with specific value (multiple nodes with same name in each line) (key problem)

I have an xml that looks like below.
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<cac:InvoiceLine>
<cbc:ID></cbc:ID>
<cbc:LineExtensionAmount currencyID="EUR">93.46</cbc:LineExtensionAmount>
<cac:Item>
<cac:AdditionalItemProperty>
<cbc:Name>Total Hours</cbc:Name>
<cbc:Value>43</cbc:Value>
</cac:AdditionalItemProperty>
<cac:AdditionalItemProperty>
<cbc:Name>naamKandidaat</cbc:Name>
<cbc:Value>Kees Netelvrees</cbc:Value>
</cac:AdditionalItemProperty>
</cac:Item>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID></cbc:ID>
<cbc:LineExtensionAmount currencyID="EUR">2.77</cbc:LineExtensionAmount>
<cac:Item>
<cac:AdditionalItemProperty>
<cbc:Name>Total Hours</cbc:Name>
<cbc:Value>43</cbc:Value>
</cac:AdditionalItemProperty>
<cac:AdditionalItemProperty>
**<cbc:Name>naamKandidaat</cbc:Name>**
<cbc:Value>Jaap Aap</cbc:Value>
</cac:AdditionalItemProperty>
</cac:Item>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID></cbc:ID>
<cbc:LineExtensionAmount currencyID="EUR">100.00</cbc:LineExtensionAmount>
<cac:Item>
<cac:AdditionalItemProperty>
<cbc:Name>Total Hours</cbc:Name>
<cbc:Value>43</cbc:Value>
</cac:AdditionalItemProperty>
<cac:AdditionalItemProperty>
**<cbc:Name>naamKandidaat</cbc:Name>**
<cbc:Value>Jaap Aap</cbc:Value>
</cac:AdditionalItemProperty>
</cac:Item>
</cac:InvoiceLine>
</Invoice>
I need to group cac:InvoiceLine/cac:Item/cac:AdditionalItemProperty/cbc:Value where InvoiceLine/cac:Item/cac:AdditionalItemProperty/cbc:Name**=naamKandidaat**.
The purpose is to sum nodes within cac:InvoiceLine where cbc:Name is the same.
The problem is that I do not know how to create the key
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ccts="urn:un:unece:uncefact:documentation:2" xmlns:qdt="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2" xmlns:udt="urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2" xmlns:default="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" exclude-result-prefixes="cac cbc ccts qdt udt default">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:key name="PerAdditionalItemProperty" match="cac:InvoiceLine" use="cac:Item/cac:AdditionalItemProperty/cbc:Value"/>
<xsl:template match="/">
<PurchaseInvoices_version_1.0>
<xsl:for-each select="default:Invoice">
<PurchaseInvoice>
<xsl:for-each select="cac:InvoiceLine[generate-id(.)=generate-id(key('PerAdditionalItemProperty',cac:Item/cac:AdditionalItemProperty/cbc:Value)[1])]">
<xsl:for-each select="key('PerAdditionalItemProperty',cac:Item/cac:AdditionalItemProperty/cbc:Value)">
<xsl:if test="position()=1">
<Line>
<Item><xsl:value-of select="cac:Item/cac:AdditionalItemProperty/cbc:Value"/></Item>
<LineAmount><xsl:value-of select="format-number(sum(key('PerAdditionalItemProperty',cac:Item/cac:AdditionalItemProperty/cbc:Value)/cbc:LineExtensionAmount[number(.) = number(.)]),'#.##')"/></LineAmount>
</Line>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</PurchaseInvoice>
</xsl:for-each>
</PurchaseInvoices_version_1.0>
</xsl:template>
</xsl:stylesheet>
The output that I expect is:
<?xml version="1.0" encoding="utf-8"?>
<PurchaseInvoices_version_1.0>
<PurchaseInvoice>
<Line>
<Item>Kees Netelvrees</Item>
<LineAmount>93.46</LineAmount>
</Line>
<Line>
<Item>Jaap Aap</Item>
<LineAmount>102.77</LineAmount>
</Line>
</PurchaseInvoice>
</PurchaseInvoices_version_1.0
You just need to put the conditions into the key expression:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
version="1.0">
<xsl:output indent="yes"/>
<xsl:key name="group" match="cac:InvoiceLine" use="cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value"/>
<xsl:template match="cac:InvoiceLine[generate-id() = generate-id(key('group', cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value)[1])]">
<Line>
<Item>
<xsl:value-of select="cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value"/>
</Item>
<LineAmount>
<xsl:value-of select="sum(key('group', cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value)/cbc:LineExtensionAmount)"/>
</LineAmount>
</Line>
</xsl:template>
<xsl:template match="cac:InvoiceLine[not(generate-id() = generate-id(key('group', cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value)[1]))]"/>
<xsl:template match="/">
<PurchaseInvoices_version_1.0>
<PurchaseInvoice>
<xsl:apply-templates/>
</PurchaseInvoice>
</PurchaseInvoices_version_1.0>
</xsl:template>
</xsl:stylesheet>

Not select a particular node child Elements in xslt using not

My XML is below. Is it possible to do this in same XSLT?
<response context="XXXX" type="abcd" errorCode="0" >
<output>
<Applicants>
<Applicant>
<IndividualEmployments/>
<Addresses/>
</Applicant>
</Applicants>
<Assets>
<Asset id="12345"></Asset>
</Assets>
<Liabilities>
<Liability id="8765"></Liability>
</Liabilities>
</output>
Desired output should be like below. I want two response nodes, one with Assets and the other with Liabilities.
<response context="XXXX" type="abcd" errorCode="0">
<output>
<Applicants>
<Applicant>
<IndividualEmployments/>
<Addresses/>
</Applicant>
</Applicants>
<Assets>
<Asset id="12345"></Asset>
</Assets>
</output>
<response context="XXXX" type="abcd" errorCode="0">
<output>
<Applicants>
<Applicant>
<IndividualEmployments/>
<Addresses/>
</Applicant>
</Applicants>
<Liabilities>
<Liability id="8765"></Liability>
</Liabilities>
</output>
You need to process the response element and output it twice, making sure the content is different, for instance by passing a parameter:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="response">
<xsl:next-match>
<xsl:with-param name="exclude" tunnel="yes" select="descendant::Liabilities"/>
</xsl:next-match>
<xsl:next-match>
<xsl:with-param name="exclude" tunnel="yes" select="descendant::Assets"/>
</xsl:next-match>
</xsl:template>
<xsl:template match="output">
<xsl:param name="exclude" tunnel="yes"/>
<xsl:copy>
<xsl:apply-templates select="#*, node() except $exclude"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If needed or wanted you can of course wrap each xsl:next-match I have in an xsl:result-document.

XSLT3 Streaming for appending integer position of node

I have a large XML file to transform using XSLT to append the integer position of sibling node . I’m using XSLT3 streaming and accumulators. I did get desired output. However, my code looks so lengthy that I’m unable to simplify my code. I also need to group same sibling nodes as sibling nodes in the source xml is not grouped always. Could someone help me here please?
Requirement: Sibling nodes such as Positions, Payments etc.. need to be appended with their corresponding integer position such as <Locations1>, <Locations2>etc.<Payments1>,< Payments2> etc..
Now that I have declared two accumulators, each for each sibling nodes. However, my source XML has many sibling nodes.. I’m not sure if I need to use as many accumulators and template match as my sibling nodes.
Input XML
``
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Payments>
<amount>1000</amount>
<currency>USD</currency>
</Payments>
<Payments>
<amount>1000</amount>
<currency>USD</currency>
</Payments>
<Locations>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations>
<Locations>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Payments>
<amount>1500</amount>
<currency>USD</currency>
</Payments>
<Payments>
<amount>1800</amount>
<currency>USD</currency>
</Payments>
</Member>
</Members>
``
Expected Output
``
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations_1>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Locations_3>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations_3>
<Locations_4>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations_4>
<Payments_1>
<amount>1000</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1000</amount>
<currency>USD</currency>
</Payments_2>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations_1>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1500</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1800</amount>
<currency>USD</currency>
</Payments_2>
</Member>
</Members>
``
Current code
``
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:mode streamable="yes" on-no-match="shallow-copy" use-accumulators="#all"/>
<xsl:accumulator name="loc-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Member" select="0"/>
<xsl:accumulator-rule match="Member/Locations" select="$value + 1"/>
</xsl:accumulator>
<xsl:accumulator name="pay-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Member" select="0"/>
<xsl:accumulator-rule match="Member/Payments" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="Locations">
<xsl:element name="Locations_{accumulator-before('loc-count')}">
<xsl:copy-of select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="Payments">
<xsl:element name="Payments_{accumulator-before('pay-count')}">
<xsl:copy-of select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
``
Current Output
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations_1>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1000</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1000</amount>
<currency>USD</currency>
</Payments_2>
<Locations_3>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations_3>
<Locations_4>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations_4>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations_1>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1500</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1800</amount>
<currency>USD</currency>
</Payments_2>
</Member>
</Members>
If you want to group the Member child elements by node-name() then I think you need to wrap the xsl:for-each-group into xsl:fork:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy" streamable="yes" use-accumulators="counters"/>
<xsl:accumulator name="counters" as="map(xs:QName, xs:integer)" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="Member" select="map{}"/>
<xsl:accumulator-rule match="Member/*"
select="map:put($value, node-name(), if (map:contains($value, node-name())) then map:get($value, node-name()) + 1 else 1)"/>
</xsl:accumulator>
<xsl:template match="Member">
<xsl:copy>
<xsl:fork>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</xsl:fork>
</xsl:copy>
</xsl:template>
<xsl:template match="Member/*">
<xsl:element name="{node-name()}_{accumulator-before('counters')(node-name())}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This approach only shows the grouping, it doesn't try to special case Name elements or some other way to not output an index if there is only one such element.
Firstly, my sympathy. XML that uses names like Payments_1 and Payments_2 is really bad news, someone is going to hate you for generating it like this. But if that's the kind of XML you've been told to produce, I guess it's not your job to question it.
As far as the requirements are concerned, you haven't made it clear whether the various kinds of sibling nodes are always grouped as in your example (all Locations, then all Payments, etc), or whether they can be interleaved.
One way you might be able to reduce the volume of code is by having a single accumulator holding a map. The map would use element names as the key and the current sibling count for that element as the value.
<accumulator name="counters" as="map(xs:QName, xs:integer)" initial-value="map{}">
<xsl:accumulator-rule match="Member" select="map{}"/>
<xsl:accumulator-rule match="Member/*" select="map:put($value, node-name(.), if (map:contains($value, node-name(.)) then map:get($value, node-name(.))+1 else 1"/>
</accumulator>
<xsl:template match="Members/*">
<xsl:element name="{name()}_{accumulator-before('counters')(node-name(.))}">
....
Another way to do the conditional map:put is
map:put($value, node-name(.), ($value(node-name(.)), 0)[1] + 1)

complex variables in xslt template v1, v2

I have source xml looking like this :
<Data>
<ActionPlaces>
<ActionPlace>
<ActionPlaceID>74</ActionPlaceID>
<PlaceName>Theatre Of Classic</PlaceName>
</ActionPlace>
</ActionPlaces>
<Actions>
<CommonAction Id="2075" Name="King">
<Action>
<ActionID>4706</ActionID>
<ActionPlaceID>74</ActionPlaceID>
</Action>
</CommonAction>
</Actions>
</Data>
Which is to transform to this:
<category name="King">
<name>King</name>
<parent name="Theatre Of Classic" />
</category>
I want to use variable :
<xsl:template match="ActionPlaces">
<xsl:variable name="id" select="/ActionPlace/ActionPlaceID"/>
<xsl:template match="CommonAction" >
<category name="<xsl:value-of select="#name"/> >
<name><xsl:value-of select="#name"/></name>
<parent <xsl:if test="/Action/ActionPlaceID = $id">
name=/Action/ActionPlaceID/> <- how to get name of theatre here?
</xsl:template>
Can variable store not only id but name also? And how to get it? What is the most common approach to handle this ?
Here's one option using XSL keys (as #michael-hor257k suggested):
Input
<Root>
<ActionPlaces>
<ActionPlace>
<ActionPlaceID>74</ActionPlaceID>
<PlaceName>Theatre Of Classic</PlaceName>
</ActionPlace>
</ActionPlaces>
<Actions>
<CommonAction Id="2075" Name="King">
<Action>
<ActionID>4706</ActionID>
<ActionPlaceID>74</ActionPlaceID>
</Action>
</CommonAction>
</Actions>
</Root>
Stylesheet
<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"/>
<!-- Collect all <ActionPlace> elements into an XSL key -->
<xsl:key name="ActionPlaceById" match="ActionPlace" use="ActionPlaceID"/>
<xsl:template match="/">
<xsl:apply-templates select="Root/Actions/CommonAction"/>
</xsl:template>
<xsl:template match="CommonAction">
<category name="{#Name}">
<name>
<xsl:value-of select="#Name"/>
</name>
<!--
Using the ActionPlaceById key we created earlier, fetch the <ActionPlace>
element that has an <ActionPlaceID> child that has the same value as the
<ActionPlaceID> descendant of the current <CommonAction> element.
-->
<parent name="{key('ActionPlaceById', Action/ActionPlaceID)/PlaceName}"/>
</category>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="utf-8"?>
<category name="King">
<name>King</name>
<parent name="Theatre Of Classic"/>
</category>

XSLT : Cummulative Sum (Conditional)

I need to do a conditional sum using XSLT. The sum of 'Oty' for each 'SKU' should be calculated only for providers listed within the 'Provider' node. In the provided example, the Qty for providerCode 4 should be skipped as its not in the 'Providers' list. I'm restricted to using XSLT 1.0.
I would appreciate any help. Thanks!
Here is the sample XML.
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Providers>
<ProviderCode>1</ProviderCode>
<ProviderCode>2</ProviderCode>
<ProviderCode>3</ProviderCode>
</Providers>
<SKU>
<SKU>XYZ</SKU>
<Description>XYZ Description</Description>
<Provider>
<ProviderCode>1</ProviderCode>
<Qty>100</Qty>
</Provider>
<Provider>
<ProviderCode>2</ProviderCode>
<Qty>67</Qty>
</Provider>
<Provider>
<ProviderCode>3</ProviderCode>
<Qty>74</Qty>
</Provider>
<Provider>
<ProviderCode>4</ProviderCode>
<Qty>62</Qty>
</Provider>
</SKU>
<SKU>
<SKU>ABC</SKU>
<Description>ABC Description</Description>
<Provider>
<ProviderCode>1</ProviderCode>
<Qty>20</Qty>
</Provider>
<Provider>
<ProviderCode>2</ProviderCode>
<Qty>77</Qty>
</Provider>
<Provider>
<ProviderCode>3</ProviderCode>
<Qty>42</Qty>
</Provider>
<Provider>
<ProviderCode>4</ProviderCode>
<Qty>631</Qty>
</Provider>
</SKU>
</Root>
Here is the required output.
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<SKU>
<SKU>XYZ</SKU>
<Qty>241</Qty>
</SKU>
<SKU>
<SKU>ABC</SKU>
<Qty>139</Qty>
</SKU>
</Root>
You can simply use sum on the nodes you want, either by comparing sum(Provider[ProviderCode = //Providers/ProviderCode]/Qty) or by using a key:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="prov" match="Providers/ProviderCode" use="."/>
<xsl:template match="Root">
<xsl:copy>
<xsl:apply-templates select="SKU"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Root/SKU">
<xsl:copy>
<xsl:copy-of select="SKU"/>
<Qty><xsl:value-of select="sum(Provider[key('prov', ProviderCode)]/Qty)"/></Qty>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>