xslt call template with dynamic match - xslt

i am trying to paas the dynamic parameter while calling the template to suppress nodes from the xml.
I would call this template like:
transform employee.xml suppress.xsl ElementsToSuppress=id,fname
employee.xml
<?xml version="1.0" encoding="utf-8" ?>
<Employees>
<Employee>
<id>1</id>
<firstname>xyz</firstname>
<lastname>abc</lastname>
<age>32</age>
<department>xyz</department>
</Employee>
<Employee>
<id>2</id>
<firstname>XY</firstname>
<lastname>Z</lastname>
<age>21</age>
<department>xyz</department>
</Employee>
</Employees>
Suppress.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" xmlns:elements="http://localhost">
<elements:name abbrev="id">id</elements:name>
<elements:name abbrev="fname">firstname</elements:name>
<xsl:param name="ElementsToSuppress" ></xsl:param>
<xsl:variable name="tokenizedSample" select="tokenize($ElementsToSuppress,',')"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
<xsl:for-each select="$tokenizedSample">
<xsl:call-template name ="Suppress" >
<xsl:with-param name="parElementName">
<xsl:value-of select="."/>
</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="Suppress">
<xsl:param name="parElementName" select="''"></xsl:param>
<xsl:variable name="extNode" select="document('')/*/elements:name[#abbrev=$parElementName]"/>
<xsl:call-template name="test" >
<xsl:with-param name="parElementName" >
<xsl:value-of select="$extNode"/>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="test" match="*[name() = $parElementName]" >
<xsl:param name="parElementName" select="''"></xsl:param>
<xsl:call-template name="SuppressElement" />
</xsl:template>
<xsl:template name="SuppressElement" />
</xsl:stylesheet>
Can we achieve output by using this or some other way? The ideal way is to pass the comma separated abbreviations of nodes and suppress them in one call.
Any help will be appreciated.
Regards,
AB

You don't need XSLT 2.0 for this processing.
This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pNodesToSuppress"
select="'id,fname,pi'"/>
<my:toSuppress>
<name abbrev="id">id</name>
<name abbrev="fname">firstname</name>
<name abbrev="pi">somePI</name>
</my:toSuppress>
<xsl:variable name="vToSuppressTable" select=
"document('')/*/my:toSuppress/*"/>
<xsl:variable name="vNamesToSupress">
<xsl:for-each select=
"$vToSuppressTable
[contains(concat(',',$pNodesToSuppress,','),
concat(',',#abbrev, ',')
)
]">
<xsl:value-of select="concat(',',.)"/>
</xsl:for-each>
<xsl:text>,</xsl:text>
</xsl:variable>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()" priority="0.01">
<xsl:if test=
"not(contains($vNamesToSupress,
concat(',',name(),',')
)
)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document (with an added processing instruction to demonstrate how we can also delete PIs -- not only elements):
<Employees>
<Employee>
<id>1</id>
<firstname>xyz</firstname>
<lastname>abc</lastname>
<age>32</age>
<department>xyz</department>
</Employee>
<?somePI This PI will be deleted ?>
<Employee>
<id>2</id>
<firstname>XY</firstname>
<lastname>Z</lastname>
<age>21</age>
<department>xyz</department>
</Employee>
</Employees>
produces the wanted, correct results:
<Employees>
<Employee>
<lastname>abc</lastname>
<age>32</age>
<department>xyz</department>
</Employee>
<Employee>
<lastname>Z</lastname>
<age>21</age>
<department>xyz</department>
</Employee>
</Employees>
Do note:
This is a pure XSLT 1.0 transformation.
2.Not only elements, but also processing instructions are deleted, when their name is specified via the external parameter $pNodesToSuppress.

I don't get why the parameter value "fname" would suppress an element called "firstname" but apart from that you might simply want
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:param name="ElementsToSuppress" as="xs:string" select="'id,firstname'"/>
<xsl:variable name="names-to-suppress" as="xs:QName*"
select="for $s in tokenize($ElementsToSuppress, ',') return QName('', $s)"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[node-name(.) = $names-to-suppress]"/>
</xsl:stylesheet>
[edit]
I missed that your sample stylesheet seemed to contain a mapping from those parameters to the elements names in the sample input. In that case here is an adapted version of the stylesheet to handle that case:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:data="http://example.com/data"
xmlns:elements="http://example.com/elements"
exclude-result-prefixes="xs data elements"
version="2.0">
<data:data>
<elements:name abbrev="id">id</elements:name>
<elements:name abbrev="fname">firstname</elements:name>
</data:data>
<xsl:key name="e-by-abbrev" match="elements:name" use="#abbrev"/>
<xsl:param name="ElementsToSuppress" as="xs:string" select="'id,fname'"/>
<xsl:variable name="names-to-suppress" as="xs:QName*"
select="for $s in tokenize($ElementsToSuppress, ',') return QName('', key('e-by-abbrev', $s, document('')/xsl:stylesheet/data:data))"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[node-name(.) = $names-to-suppress]"/>
</xsl:stylesheet>
[second edit to implement further request] Here is a sample that also checks the relationship attribute values:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:data="http://example.com/data"
xmlns:elements="http://example.com/elements"
exclude-result-prefixes="xs data elements"
version="2.0">
<xsl:param name="ElementsToSuppress" as="xs:string" select="'id'"/>
<xsl:param name="parVAObjectRelationship" as="xs:string" select="'a,b'"/>
<xsl:variable name="names-to-suppress" as="xs:QName*"
select="for $s in tokenize($ElementsToSuppress, ',') return QName('', key('e-by-abbrev', $s, document('')/xsl:stylesheet/data:data))"/>
<xsl:variable name="att-values-to-suppress" as="xs:string*"
select="tokenize($parVAObjectRelationship, ',')"/>
<data:data>
<elements:name abbrev="id">id</elements:name>
<elements:name abbrev="fname">firstname</elements:name>
</data:data>
<xsl:key name="e-by-abbrev" match="elements:name" use="#abbrev"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#relationship = $att-values-to-suppress]"/>
<xsl:template match="*[node-name(.) = $names-to-suppress]"/>
</xsl:stylesheet>
When applied to
<Employees>
<Employee>
<id>1</id>
<firstname>xyz</firstname>
<lastname relationship="a">abc</lastname>
<age relationship="b">32</age>
<department>xyz</department>
</Employee>
</Employees>
the output is
<Employees>
<Employee>
<firstname>xyz</firstname>
<department>xyz</department>
</Employee>
</Employees>
You might want to add strip-space and output indent="yes" to prevent the blank lines.

If you are using the old Saxon-B or newer Saxon-PE or Saxon-EE as XSLT processor, you can use a saxon extension to achieve dynamic template calls:
<saxon:call-template name="{$templateName}"/>
Don't forget to declare the saxon-Namespace in xsl-stylesheet element:
<xsl:stylesheet xmlns:saxon="http://saxon.sf.net/" [...] >

Related

Create copy of record based on node that occurs multiple times

I have a requirement to create a copy of an XML file based on a field that occurs multiple times.
Input XML: There are two EmpEmployment nodes in the XML. I need to separate them and copy the rest of the nodes so that I have two PerPerson records with one EmpEmployment each.
<PerPerson>
<PerPerson>
<personalInfoNav>
<PerPersonal/>
</personalInfoNav>
<nationalIdNav>
<PerNationalId/>
</nationalIdNav>
<personIdExternal>AA</personIdExternal>
<personEmpTerminationInfoNav>
<PersonEmpTerminationInfo/>
</personEmpTerminationInfoNav>
<phoneNav>
<PerPhone/>
</phoneNav>
<employmentNav>
<EmpEmployment>
<compInfoNav>
<EmpCompensation/>
</compInfoNav>
<jobInfoNav>
<EmpJob/>
</jobInfoNav>
</EmpEmployment>
<EmpEmployment>
<compInfoNav>
<EmpCompensation/>
</compInfoNav>
<jobInfoNav>
<EmpJob/>
</jobInfoNav>
</EmpEmployment>
</employmentNav>
<homeAddressNavDEFLT>
<PerAddressDEFLT/>
</homeAddressNavDEFLT>
</PerPerson>
</PerPerson>
I am trying to do this using XSLT 1.0
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:hci="http://sap.com/it/" exclude-result-prefixes="hci">
<xsl:strip-space elements="*"/>
<xsl:output encoding="utf-8" indent="yes" method="xml"/>
<xsl:template match="/">
<PerPerson>
<xsl:for-each select="PerPerson/PerPerson">
<xsl:variable name="var_person" select="./*[not(name()='EmpEmployment')]"/>
<xsl:for-each select="employmentNav/EmpEmployment">
<xsl:variable name="var_empInfo" select="."/>
<PerPerson>
<xsl:copy-of select="$var_person"/>
<xsl:copy-of select="$var_empInfo"/>
</PerPerson>
</xsl:for-each>
</xsl:for-each>
</PerPerson>
</xsl:template>
</xsl:stylesheet>
Its not working as expected. I am unable to create the desired output below:
<PerPerson>
<PerPerson>
<personalInfoNav>
<PerPersonal/>
</personalInfoNav>
<nationalIdNav>
<PerNationalId/>
</nationalIdNav>
<personIdExternal>AA</personIdExternal>
<personEmpTerminationInfoNav>
<PersonEmpTerminationInfo/>
</personEmpTerminationInfoNav>
<phoneNav>
<PerPhone/>
</phoneNav>
<employmentNav>
<EmpEmployment>
<compInfoNav>
<EmpCompensation/>
</compInfoNav>
<jobInfoNav>
<EmpJob/>
</jobInfoNav>
</EmpEmployment>
</employmentNav>
<homeAddressNavDEFLT>
<PerAddressDEFLT/>
</homeAddressNavDEFLT>
</PerPerson>
<PerPerson>
<personalInfoNav>
<PerPersonal/>
</personalInfoNav>
<nationalIdNav>
<PerNationalId/>
</nationalIdNav>
<personIdExternal>AA</personIdExternal>
<personEmpTerminationInfoNav>
<PersonEmpTerminationInfo/>
</personEmpTerminationInfoNav>
<phoneNav>
<PerPhone/>
</phoneNav>
<employmentNav>
<EmpEmployment>
<compInfoNav>
<EmpCompensation/>
</compInfoNav>
<jobInfoNav>
<EmpJob/>
</jobInfoNav>
</EmpEmployment>
</employmentNav>
<homeAddressNavDEFLT>
<PerAddressDEFLT/>
</homeAddressNavDEFLT>
</PerPerson>
</PerPerson>
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="/PerPerson">
<PerPerson>
<xsl:for-each select="PerPerson/employmentNav/EmpEmployment">
<PerPerson>
<xsl:copy-of select="../preceding-sibling::*"/>
<employmentNav>
<xsl:copy-of select="."/>
</employmentNav>
<xsl:copy-of select="../following-sibling::*"/>
</PerPerson>
</xsl:for-each>
</PerPerson>
</xsl:template>
</xsl:stylesheet>
In XSLT 2.0 you can use tunnel-parameters.
And then just use the template macht engine like below
<?xml version='1.0' encoding='UTF-8'?>
<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="#all">
<xsl:template match="#*|node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PerPerson/PerPerson">
<xsl:variable name="person" select="."/>
<xsl:for-each select="employmentNav/EmpEmployment">
<xsl:apply-templates select="$person" mode="denormalize">
<xsl:with-param name="position" as="xs:integer" select="position()" tunnel="yes"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="employmentNav" mode="denormalize">
<xsl:param name="position" as="xs:integer" tunnel="yes"/>
<xsl:copy>
<xsl:apply-templates select="EmpEmployment[$position]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT remove duplicate and select higher value

I am new in XSLT and looking for help to remove duplicates of <EMP> from an xml document on the basis of their children's combined value. From each group of elements with the same value for this, the one with highest value for AIB_Position/AIB must be output. Below is my sample xml document and the corresponding desired output.
<Row_entry>
<Employees>
<Emp>
<Emp_id>E1</Emp_id>
<Emp_Name>Name1</Emp_Name>
<Country>C1</Country>
<AIB_Position>
<AIB>1500</AIB>
</AIB_Position>
</Emp>
<Emp>
<Emp_id>E2</Emp_id>
<Emp_Name>Name2</Emp_Name>
<Country>C2</Country>
<AIB_Position>
<AIB>1700</AIB>
</AIB_Position>
</Emp>
<Emp>
<Emp_id>E2</Emp_id>
<Emp_Name>Name2</Emp_Name>
<Country>C2</Country>
<AIB_Position>
<AIB>1800</AIB>
</AIB_Position>
</Emp>
</Employees>
</Row_entry>
Desired output(Removed duplicate Emp elements based on the combined <Emp_id>, <Emp_Name>, <Country> value):
<Row_entry>
<Employees>
<Emp>
<Emp_id>E1</Emp_id>
<Emp_Name>Name1</Emp_Name>
<Country>C1</Country>
<AIB_Position>
<AIB>1500</AIB>
</AIB_Position>
</Emp>
<Emp>
<Emp_id>E2</Emp_id>
<Emp_Name>Name2</Emp_Name>
<Country>C2</Country>
<AIB_Position>
<AIB>1800</AIB>
</AIB_Position>
</Emp>
</Employees>
</Row_entry>
I think you want this (directly using the XPath 2.0 max() function):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output indent="yes"/>
<xsl:template match="Employees">
<xsl:copy>
<xsl:for-each-group select="Emp" group-by="concat(Emp_id, '+', Emp_Name, '+', Country)">
<xsl:copy-of select="current-group()
[AIB_Position/AIB/number() = max(current-group()/AIB_Position/AIB/number())][1]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And if you suspect your XSLT processor of idiocy, such as calculating the max() more than once, use this more precisely directing transformation:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output indent="yes"/>
<xsl:template match="Employees">
<xsl:copy>
<xsl:for-each-group select="Emp"
group-by="concat(Emp_id, '+', Emp_Name, '+', Country)">
<xsl:copy-of select=
"for $max in max(current-group()/AIB_Position/AIB/number())
return
current-group()[AIB_Position/AIB/number() = $max][1]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In XSLT 2 or later, use for-each-group, for instance in XSLT 3 with a composite grouping key, then sort each group and output the maximum value:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="Employees">
<xsl:copy>
<xsl:for-each-group select="Emp" composite="yes" group-by="Emp_id, Emp_Name, Country">
<xsl:for-each select="current-group()">
<xsl:sort select="AIB_Position/AIB" order="descending"/>
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With an XSLT 3 processor supporting the higher order sort function you could shorten that code to use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="Employees">
<xsl:copy>
<xsl:for-each-group select="Emp" composite="yes" group-by="Emp_id, Emp_Name, Country">
<xsl:sequence select="sort(current-group(), (), function($emp) { xs:integer($emp/AIB_Position/AIB) })[last()]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://stackoverflow.com/tags/xslt-grouping/info has some details on how to implement a composite grouping key of XSLT 3 in XSLT 2 by string-joining the components of the key, if you are limited to XSLT 2.

xslt copy child note and add extra nodes

For the following xml:
<root>
<employees>
<employee>
<Name>ABC</Name>
<Dept>CS</Dept>
<Designation>sse</Designation>
</employee>
</employees>
</root>
I use this xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Identity transform -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Name">
<xsl:copy-of select="."/>
<Age>34</Age>
</xsl:template>
<xsl:template match="Dept">
<xsl:copy-of select="."/>
<Domain>Insurance</Domain>
</xsl:template>
</xsl:stylesheet>
I would like to achieve the following output
<employee>
<Name>ABC</Name>
<Age>34</Age>
<Dept>CS</Dept>
<Domain>Insurance</Domain>
<Designation>sse</Designation>
</employee>
I don't know what xpath shall I use to limit the result only to employee node.
Well, processing starts at the document node (root node) denoted by / thus if you write a template
<xsl:template match="/">
<xsl:apply-templates select="//employee"/>
</xsl:template>
you only process any employee descendants of the document with the rest of the templates.
See https://xsltfiddle.liberty-development.net/pPqsHT6 for a working demo of above suggestion integrated into your stylesheet, the whole code is
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Identity transform -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Name">
<xsl:copy-of select="."/>
<Age>34</Age>
</xsl:template>
<xsl:template match="Dept">
<xsl:copy-of select="."/>
<Domain>Insurance</Domain>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="//employee"/>
</xsl:template>
</xsl:stylesheet>

Call templates with same match string in different context

I want to transform a source xml into a target xml where certain matches from the source xml are included in different context in the target xml. For example I have a source xml like:
<shiporder>
<shipto>orderperson1</shipto>
<shipto>orderperson1</shipto>
<city>London</city>
</shiporder>
On this source xml I apply the following stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:call-template name="root" />
</xsl:template>
<xsl:template name="root">
<root>
<xsl:apply-templates select="/shiporder"/>
<xsl:call-template name="Customer"/>
</root>
</xsl:template>
<xsl:template name="Customer">
<Customer>
<!--<xsl:apply-templates select="/shiporder"/>-->
</Customer>
</xsl:template>
<xsl:template match="/shiporder">
<xsl:apply-templates select="shipto"/>
</xsl:template>
<xsl:template match="/shiporder/shipto">
<Address>
<xsl:apply-templates select="text()"/>
</Address>
</xsl:template>
</xsl:stylesheet>
In the template of name Customer I like to apply a template like:
<xsl:template match="/shiporder">
<xsl:apply-templates select="city"/>
</xsl:template>
<xsl:template match="/shiporder/city">
<City>
<xsl:apply-templates select="text()"/>
</City>
</xsl:template>
But I already defined a template with match /shiporder. So I don't know how to design a stylesheet where both templates with the same match exists in their own context?
If you use mode, like #michael.hor257k suggested you can differentiate between two or more templates that match on the same element but with different results.
In your case that could end up looking like this:
<?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" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:call-template name="root" />
</xsl:template>
<xsl:template name="root">
<root>
<xsl:apply-templates select="/shiporder" mode="root"/>
<xsl:call-template name="Customer"/>
</root>
</xsl:template>
<xsl:template name="Customer">
<Customer>
<xsl:apply-templates select="/shiporder" mode="customer"/>
</Customer>
</xsl:template>
<xsl:template match="/shiporder" mode="root">
<xsl:apply-templates select="shipto"/>
</xsl:template>
<xsl:template match="/shiporder" mode="customer">
<xsl:apply-templates select="city"/>
</xsl:template>
<xsl:template match="shipto">
<Address>
<xsl:apply-templates select="text()"/>
</Address>
</xsl:template>
<xsl:template match="city">
<City>
<xsl:apply-templates select="text()"/>
</City>
</xsl:template>
</xsl:stylesheet>
Obviously all credits here go to Michael for pointing this out first.

Grouping the info from two XMLs, where XMLs having common element for mapping

Suggest for grouping the info from two XMLs. In my post, based on employees shift and Route number other information are to be grouped. See the required OutPut for quick review. (XSLT 2)
Input XML (EmpShift.xml is input to the XSLT):
<MPS>
<emp>
<name>Rudramuni TP</name>
<ID>MAC000424</ID>
<Shift>Second</Shift>
</emp>
<emp>
<name>Mohan</name>
<ID>MAC000425</ID>
<Shift>Second</Shift>
</emp>
<emp>
<name>Vijay</name>
<ID>MAC000426</ID>
<Shift>First</Shift>
</emp>
<emp>
<name>Shankar</name>
<ID>MAC000427</ID>
<Shift>First</Shift>
</emp>
<emp>
<name>Prasad</name>
<ID>MAC000428</ID>
<Shift>Second</Shift>
</emp>
</MPS>
Second Input (Called XML EmpAddressInfo.xml):
<Addess_Info>
<emp>
<name>Rudramuni TP</name>
<ID>MAC000424</ID>
<address>Nandini Layout, B-96</address>
<Route_No>10</Route_No>
</emp>
<emp>
<name>Mohan</name>
<ID>MAC000425</ID>
<address>Banashankari</address>
<Route_No>11</Route_No>
</emp>
<emp>
<name>Vijay</name>
<ID>MAC000426</ID>
<address>Marathahalli</address>
<Route_No>10</Route_No>
</emp>
<emp>
<name>Shankar</name>
<ID>MAC000427</ID>
<address>Yelahanka</address>
<Route_No>11</Route_No>
</emp>
<emp>
<name>Prasad</name>
<ID>MAC000428</ID>
<address>Marathahalli</address>
<Route_No>10</Route_No>
</emp>
</Addess_Info>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="varDocAdress" select="document('EmpAddressInfo.xml')"/>
<xsl:template match="MPS">
<xsl:for-each-group select="//emp" group-by="Shift">
<!--xsl:for-each-group select="current-group()" group-by="$varDocAdress/Route_No"-->
<Shift>
<Shift-Name><xsl:value-of select="current-grouping-key()"/></Shift-Name>
<empDetails><xsl:apply-templates select="current-group()/name|current-group()/ID"/></empDetails>
</Shift>
<!--/xsl:for-each-group-->
</xsl:for-each-group>
</xsl:template>
<xsl:template match="name">
<name1><xsl:apply-templates/></name1>
</xsl:template>
<xsl:template match="ID">
<ID><xsl:apply-templates/></ID>
</xsl:template>
</xsl:stylesheet>
Required Result:
<MPS>
<Shift>
<Shift-Name>Second</Shift-Name>
<Route_No><title>10</title>
<empDetails>
<name1>Rudramuni TP</name1><ID>MAC000424</ID><address>Nandini Layout, B-96</address>
</empDetails>
<empDetails>
<name1>Prasad</name1><ID>MAC000428</ID><address>Marathahalli</address>
</empDetails>
</Route_No>
<Route_No><title>11</title>
<empDetails>
<name1>Mohan</name1><ID>MAC000425</ID><address>Banashankari</address>
</empDetails>
</Route_No>
</Shift>
<Shift>
<Shift-Name>First</Shift-Name>
<Route_No><title>10</title>
<empDetails>
<name1>Vijay</name1><ID>MAC000426</ID><address>Marathahalli</address>
</empDetails>
</Route_No>
<Route_No><title>11</title>
<empDetails>
<name1>Shankar</name1><ID>MAC000427</ID><address>Yelahanka</address>
</empDetails>
</Route_No>
</Shift>
</MPS>
Try this XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="varDocAdress" select="document('EmpAddressInfo.xml')"/>
<xsl:template match="MPS">
<xsl:copy>
<xsl:for-each-group select="//emp" group-by="Shift">
<Shift>
<Shift-Name>
<xsl:value-of select="current-grouping-key()"/>
</Shift-Name>
<xsl:for-each-group select="$varDocAdress/Addess_Info/emp[ID = current-group()/ID]" group-by="Route_No">
<Route_No>
<title><xsl:value-of select="current-grouping-key()"/></title>
<xsl:for-each select="current-group()">
<empDetails>
<xsl:apply-templates select="current()"/>
</empDetails>
</xsl:for-each>
</Route_No>
</xsl:for-each-group>
</Shift>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="emp">
<name1>
<xsl:value-of select="name"/>
</name1>
<xsl:copy-of select="ID | address"/>
</xsl:template>
</xsl:stylesheet>
I would use a key for the cross-reference:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf"
version="2.0">
<xsl:param name="address-url" select="'test2014112603.xml'"/>
<xsl:variable name="address-doc" select="doc($address-url)"/>
<xsl:output indent="yes"/>
<xsl:key name="emp-by-id" match="emp" use="ID"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="MPS">
<xsl:copy>
<xsl:for-each-group select="emp" group-by="Shift">
<Shift>
<Shift-Name><xsl:value-of select="current-grouping-key()"/></Shift-Name>
<xsl:for-each-group select="current-group()" group-by="key('emp-by-id', ID, $address-doc)/Route_No">
<Route_No>
<title><xsl:value-of select="current-grouping-key()"/></title>
<xsl:apply-templates select="current-group()"/>
</Route_No>
</xsl:for-each-group>
</Shift>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="emp">
<empDetails>
<xsl:apply-templates select="name, ID, key('emp-by-id', ID, $address-doc)/address"/>
</empDetails>
</xsl:template>
<xsl:template match="name">
<name1><xsl:apply-templates/></name1>
</xsl:template>
</xsl:stylesheet>
I would suggest you simplify this by using a key:
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:variable name="varDocAdress" select="document('EmpAddressInfo.xml')"/>
<xsl:key name="emp-by-id" match="emp" use="ID" />
<xsl:template match="/MPS">
<xsl:for-each-group select="emp" group-by="Shift">
<Shift>
<Shift-Name><xsl:value-of select="current-grouping-key()"/></Shift-Name>
<xsl:for-each-group select="current-group()" group-by="key('emp-by-id', ID, $varDocAdress)/Route_No">
<Route_No>
<title><xsl:value-of select="current-grouping-key()"/></title>
<xsl:apply-templates select="current-group()"/>
</Route_No>
</xsl:for-each-group>
</Shift>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="emp">
<empDetails>
<xsl:copy-of select="name | ID | key('emp-by-id', ID, $varDocAdress)/address "/>
</empDetails>
</xsl:template>
</xsl:stylesheet>