How to group and Sum of hrs based on the key value in XSLT - xslt-2.0

Can you please suggest xslt code on how to get the results based on the below
xml on group by Week value. The expected output should be:
Year Week Tot Hrs Ben Hrs
2015 33 80.7 0
2015 34 120.7 0
Advance thanks for your help
XML:
<?xml version="1.0" encoding="UTF-8"?>
-<wd:Report_Data xmlns:wd="urn:com.workday.report/TLB024_TEST">
-<wd:Report_Entry>
-<wd:PAYROLL_RESULT_LINE>
<wd:FISCAL_YEAR>2015</wd:FISCAL_YEAR>
<wd:FISCAL_WEEK>33</wd:FISCAL_WEEK>
<wd:TOT_HRS>40</wd:TOT_HRS>
<wd:BEN_HRS>0</wd:BEN_HRS>
</wd:PAYROLL_RESULT_LINE>
-<wd:PAYROLL_RESULT_LINE>
<wd:FISCAL_YEAR>2015</wd:FISCAL_YEAR>
<wd:FISCAL_WEEK>33</wd:FISCAL_WEEK>
<wd:TOT_HRS>0.07</wd:TOT_HRS>
<wd:BEN_HRS>0</wd:BEN_HRS>
</wd:PAYROLL_RESULT_LINE>
-<wd:PAYROLL_RESULT_LINE>
<wd:FISCAL_YEAR>2015</wd:FISCAL_YEAR>
<wd:FISCAL_WEEK>34</wd:FISCAL_WEEK>
<wd:TOT_HRS>40</wd:TOT_HRS>
<wd:BEN_HRS>0</wd:BEN_HRS>
</wd:PAYROLL_RESULT_LINE>
-<wd:PAYROLL_RESULT_LINE>
<wd:FISCAL_YEAR>2015</wd:FISCAL_YEAR>
<wd:FISCAL_WEEK>34</wd:FISCAL_WEEK>
<wd:TOT_HRS>0.07</wd:TOT_HRS>
<wd:BEN_HRS>0</wd:BEN_HRS>
</wd:PAYROLL_RESULT_LINE>
</wd:Report_Entry>
-<wd:Report_Entry>
-<wd:PAYROLL_RESULT_LINE>
<wd:FISCAL_YEAR>2015</wd:FISCAL_YEAR>
<wd:FISCAL_WEEK>33</wd:FISCAL_WEEK>
<wd:TOT_HRS>40</wd:TOT_HRS>
<wd:BEN_HRS>0</wd:BEN_HRS>
</wd:PAYROLL_RESULT_LINE>
-<wd:PAYROLL_RESULT_LINE>
<wd:FISCAL_YEAR>2015</wd:FISCAL_YEAR>
<wd:FISCAL_WEEK>34</wd:FISCAL_WEEK>
<wd:TOT_HRS>40</wd:TOT_HRS>
<wd:BEN_HRS>0</wd:BEN_HRS>
</wd:PAYROLL_RESULT_LINE>
</wd:Report_Entry>
-<wd:Report_Entry>
</wd:Report_Data>
XSLT :
This is the xslt code, I tried not able to get the desire result.
<?xml version='1.0'?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wd="urn:com.workday.report/TLB024_TEST" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:template match="/|*">
<xsl:copy>
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
<xsl:template match="wd:Report_Entry">
<File xmlns:xtt="urn:com.workday/xtt">
<xsl:for-each select="wd:PAYROLL_RESULT_LINE">
<Record>
<xsl:variable name="Group" select="../wd:PAYROLL_RESULT_LINE[((wd:FISCAL_YEAR=current()/wd:FISCAL_YEAR) and (wd:FISCAL_WEEK=current()/wd:FISCAL_WEEK)]" />
<xsl:if test="generate-id()=generate-id($Group[1])">
<xsl:copy>
<fiscal_year xtt:align="left"><xsl:value-of select="wd:FISCAL_YEAR"/></fiscal_year>
<fiscal_week xtt:align="left"><xsl:value-of select="wd:FISCAL_WEEK"/></fiscal_week>
<tot_hrs xtt:fixedLength="7" xtt:align="left"><xsl:value-of select="format-number(sum($Group/wd:TOT_HRS), '0.##')"/></tot_hrs>
<ben_hrs xtt:fixedLength="7" xtt:align="left"><xsl:value-of select="format-number(sum($Group/wd:BEN_HRS), '0.##')"/></ben_hrs>
<Filler><xsl:text>
</xsl:text></Filler>
</xsl:copy>
</xsl:if>
</Record>
</xsl:for-each>
</File>
</xsl:template>
</xsl:stylesheet>

If you are using XSLT 2.0, this is a very simple grouping problem:
<xsl:for-each-group select="wd:PAYROLL_RESULT_LINE"
group-by="concat(wd:FISCAL_YEAR, '/', wd:FISCAL_WEEK)">
<Record>
<fiscal_year xtt:align="left"><xsl:value-of select="wd:FISCAL_YEAR"/></fiscal_year>
<fiscal_week xtt:align="left"><xsl:value-of select="wd:FISCAL_WEEK"/></fiscal_week>
<tot_hrs xtt:fixedLength="7" xtt:align="left"><xsl:value-of select="format-number(sum(current-group()/wd:TOT_HRS), '0.##')"/></tot_hrs>
<ben_hrs xtt:fixedLength="7" xtt:align="left"><xsl:value-of select="format-number(sum(current-group()/wd:BEN_HRS), '0.##')"/></ben_hrs>
<Filler><xsl:text>
</xsl:text></Filler>
</Record>
</xsl:for-each-group>

Related

How to match template based on condition?

Here is my source XML which I'm trying to transform based on values of <City>
<?xml version="1.0" encoding="UTF-8"?>
<Workers>
<Worker>
<EmpID>12345</EmpID>
<City>NYC</City>
<Allowance>
<Type>Meal</Type>
<Amount>150</Amount>
</Allowance>
<Allowance>
<Type>Gym</Type>
<Amount>200</Amount>
</Allowance>
</Worker>
<Worker>
<EmpID>56789</EmpID>
<City>SFO</City>
<Base>
<BaseType>General</BaseType>
<BaseAmount>1000</BaseAmount>
</Base>
</Worker>
<Worker>
<EmpID>18978</EmpID>
<City>LAX</City>
<Base>
<BaseType>General</BaseType>
<BaseAmount>3000</BaseAmount>
</Base>
</Worker>
</Workers>
I'm attempting to transform as below. Transformation doesn't need be to applied if the value of <City> is either NYC or SFO
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<SOAP-ENV:Body xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope">
<m:GetQuotationResponse xmlns:m = "http://www.example.com">
<m:Worker>12345</m:Worker>
<m:Location>NYC</m:Location>
<m:Expense>
<m:ExpenseType>Meal</m:ExpenseType>
<m:Amount>150</m:Amount>
</m:Expense>
<m:Expense>
<m:ExpenseType>Gym</m:ExpenseType>
<m:Amount>200</m:Amount>
</m:Expense>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
<SOAP-ENV:Body xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope">
<m:GetQuotationResponse xmlns:m = "http://www.example.com">
<m:Worker>56789</m:Worker>
<m:Location>SFO</m:Location>
<m:Expense>
<m:ExpenseType>General</m:ExpenseType>
<m:Amount>1000</m:Amount>
</m:Expense>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</Root>
This is my attempt
<?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"
xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
xmlns:m = "http://www.example.com"
exclude-result-prefixes="xs m SOAP-ENV"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Worker[City='NYC']">
<Root>
<SOAP-ENV:Body xmlns:m = "http://www.example.com">
<m:GetQuotationResponse>
<m:Worker><xsl:value-of select="EmpID"/></m:Worker>
<m:Location><xsl:value-of select="City"/></m:Location>
<xsl:for-each select="Allowance">
<m:Expense>
<m:ExpenseType><xsl:value-of select="Type"/></m:ExpenseType>
<m:Amount><xsl:value-of select="Amount"/></m:Amount>
</m:Expense>
</xsl:for-each>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</Root>
</xsl:template>
<xsl:template match="Worker[City='SFO']">
<Root>
<SOAP-ENV:Body xmlns:m = "http://www.example.com">
<m:GetQuotationResponse>
<m:Worker><xsl:value-of select="EmpID"/></m:Worker>
<m:Location><xsl:value-of select="City"/></m:Location>
<xsl:for-each select="Base">
<m:Expense>
<m:ExpenseType><xsl:value-of select="BaseType"/></m:ExpenseType>
<m:Amount><xsl:value-of select="BaseAmount"/></m:Amount>
</m:Expense>
</xsl:for-each>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</Root>
</xsl:template>
</xsl:stylesheet>
I have two issues(or more)
Couldn't get <Root> as the Parent node
Templates which are not matching values of <City> also returned. I wanted to match only <xsl:template match="Worker[City='NYC']"> or <xsl:template match="Worker[City='SFO']">
Current Output
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<SOAP-ENV:Body xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope">
<m:GetQuotationResponse xmlns:m="http://www.example.com">
<m:Worker>12345</m:Worker>
<m:Location>NYC</m:Location>
<m:Expense>
<m:ExpenseType>Meal</m:ExpenseType>
<m:Amount>150</m:Amount>
</m:Expense>
<m:Expense>
<m:ExpenseType>Gym</m:ExpenseType>
<m:Amount>200</m:Amount>
</m:Expense>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</Root>
<Root>
<SOAP-ENV:Body xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope">
<m:GetQuotationResponse xmlns:m="http://www.example.com">
<m:Worker>56789</m:Worker>
<m:Location>SFO</m:Location>
<m:Expense>
<m:ExpenseType>General</m:ExpenseType>
<m:Amount>1000</m:Amount>
</m:Expense>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</Root>
18978
LAX
General
3000
Any help is appreciated to get this working using xslt 2.0 or xslt 3.0. Thank you
I'm posting the first solution that Martin Honnen has suggested.
<?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"
xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
xmlns:m = "http://www.example.com"
exclude-result-prefixes="xs m SOAP-ENV"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Workers">
<Root>
<xsl:apply-templates/>
</Root>
</xsl:template>
<xsl:template match="Worker[City='NYC']">
<SOAP-ENV:Body xmlns:m = "http://www.example.com">
<m:GetQuotationResponse>
<m:Worker><xsl:value-of select="EmpID"/></m:Worker>
<m:Location><xsl:value-of select="City"/></m:Location>
<xsl:for-each select="Allowance">
<m:Expense>
<m:ExpenseType><xsl:value-of select="Type"/></m:ExpenseType>
<m:Amount><xsl:value-of select="Amount"/></m:Amount>
</m:Expense>
</xsl:for-each>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</xsl:template>
<xsl:template match="Worker[City='SFO']">
<SOAP-ENV:Body xmlns:m = "http://www.example.com">
<m:GetQuotationResponse>
<m:Worker><xsl:value-of select="EmpID"/></m:Worker>
<m:Location><xsl:value-of select="City"/></m:Location>
<xsl:for-each select="Base">
<m:Expense>
<m:ExpenseType><xsl:value-of select="BaseType"/></m:ExpenseType>
<m:Amount><xsl:value-of select="BaseAmount"/></m:Amount>
</m:Expense>
</xsl:for-each>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</xsl:template>
<xsl:template match="Worker[not(City = ('SFO', 'NYC'))]"/>
</xsl:stylesheet>
Here is what alternate solution returns. I'm not sure how this solution can be modified to get expected output since City='SFO' has different sibling node <Base> than City='NYC'
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<SOAP-ENV:Body xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope">
<m:GetQuotationResponse xmlns:m="http://www.example.com">
<m:Worker>12345</m:Worker>
<m:Location>NYC</m:Location>
<m:Expense>
<m:ExpenseType>Meal</m:ExpenseType>
<m:Amount>150</m:Amount>
</m:Expense>
<m:Expense>
<m:ExpenseType>Gym</m:ExpenseType>
<m:Amount>200</m:Amount>
</m:Expense>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
<SOAP-ENV:Body xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope">
<m:GetQuotationResponse xmlns:m="http://www.example.com">
<m:Worker>56789</m:Worker>
<m:Location>SFO</m:Location>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</Root>
Following part is missing from the output
<m:Expense>
<m:ExpenseType>General</m:ExpenseType>
<m:Amount>1000</m:Amount>
</m:Expense>
Alternate solution suggested by #Martin Honnen with some minor tweaks also provide desired output.
<?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"
xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
xmlns:m = "http://www.example.com"
exclude-result-prefixes="xs m SOAP-ENV"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Workers">
<Root>
<xsl:apply-templates select="Worker[City = ('SFO', 'NYC')]"/>
</Root>
</xsl:template>
<xsl:template match="Worker">
<SOAP-ENV:Body xmlns:m = "http://www.example.com">
<m:GetQuotationResponse>
<m:Worker><xsl:value-of select="EmpID"/></m:Worker>
<m:Location><xsl:value-of select="City"/></m:Location>
<xsl:for-each select="Allowance">
<m:Expense>
<m:ExpenseType><xsl:value-of select="Type"/></m:ExpenseType>
<m:Amount><xsl:value-of select="Amount"/></m:Amount>
</m:Expense>
</xsl:for-each>
<xsl:for-each select="Base"> <!-- This part was included to get desired output without having to use more than one templates -->
<m:Expense>
<m:ExpenseType><xsl:value-of select="BaseType"/></m:ExpenseType>
<m:Amount><xsl:value-of select="BaseAmount"/></m:Amount>
</m:Expense>
</xsl:for-each>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</xsl:template>
</xsl:stylesheet>
Start with a template
<xsl:template match="Workers">
<Root>
<xsl:apply-templates/>
</Root>
</xsl:template>
then map your selected Workers to a SOAP body with a single template using e.g. <xsl:template match="Worker[City = ('SFO', 'NYC')]"> or <xsl:template match="Worker[City = 'SFO'] | Worker[City = 'NYC']">, if you prefer.
For other Workers, set up an empty template e.g. <xsl:template match="Worker[not(City = ('SFO', 'NYC'))]"/>.
As an alternative, you can of course just use a template matching Worker to map to a SOAP body and make your desired selection in the apply-templates of the first template I have show, i.e. change that to <xsl:apply-templates select="Worker[City = ('SFO', 'NYC')]"/>, that way you also ensure that only the wanted Workers are processed e.g.
<xsl:template match="Workers">
<Root>
<xsl:apply-templates select="Worker[City = ('SFO', 'NYC')]"/>
</Root>
</xsl:template>
<xsl:template match="Worker">
<SOAP-ENV:Body xmlns:m = "http://www.example.com">
<m:GetQuotationResponse>
<m:Worker><xsl:value-of select="EmpID"/></m:Worker>
<m:Location><xsl:value-of select="City"/></m:Location>
<xsl:for-each select="Allowance">
<m:Expense>
<m:ExpenseType><xsl:value-of select="Type"/></m:ExpenseType>
<m:Amount><xsl:value-of select="Amount"/></m:Amount>
</m:Expense>
</xsl:for-each>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</xsl:template>

How to access variable defined under for-each loop/If condition in another for each loop

I have to generate the output in sequence and so I wanted to know how to access the variable defined under For-each loop/If condition and then value of select inside another for loop.
As per my example how to access partn and date3? Please help and suggest.
what is the concept for achieving the same..I have tried with-param as well, but didn't work for me.
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:func="myfunc"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions" >
<xsl:output method="text" encoding="utf-8" />
<xsl:output omit-xml-declaration="yes" />
<xsl:param name="break" select="'
'" />
<xsl:template match="ZGS/ID">
<xsl:for-each select="E1">
<xsl:if test="PA = 'CE'">
<xsl:variable name="partn" select="PAN"/>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="E13">
<xsl:if test="ID = 033">
<xsl:variable name="date3"
select="substring(DAT,3,8)"/>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="E1E">
<xsl:text>823</xsl:text>
<xsl:text>03</xsl:text>
<xsl:for-each select="E1ED">
<xsl:if test="QU = 012 ">
<xsl:value-of select="BEL"/>
</xsl:if>
</xsl:for-each>
<xsl:value-of select="$partn"/>
<xsl:value-of select="$date3"/>
</xsl:for-each>
</xsl:template>
INPUT:
<?xml version='1.0' encoding='utf-8'?>
<ZGS>
<ID BEGIN="1">
<E1 SEGMENT="1">
<PA>AG</PA>
<NAME>ABC</NAME>
<SP>E</SP>
<AND>0004</AND>
</E1>
<E1 SEGMENT="1">
<PA>RE</PA>
<PAN>IUIOP</PAN>
<NAME>ABC1</NAME>
<SP>EQ</SP>
<AND>0005</AND>
<EKA3 SEGMENT="1">
<QU>009</QU>
</EKA3>
</E1>
<E1 SEGMENT="1">
<PA>CE</PA>
<PAN>PODW</PAN>
<NAME>ABC2</NAME>
<SP>EP</SP>
<AND>0006</AND>
</E1>
<E13 SEGMENT="1">
<ID>001</ID>
<DAT>20190329</DAT>
</E13>
<E13 SEGMENT="1">
<ID>002</ID>
<DAT>20190429</DAT>
</E13>
<E13 SEGMENT="1">
<IDD>033</IDD>
<DAT>20190529</DAT>
</E13>
<E1E>
<E1ED>
</E1ED>
<E1ED>
</E1ED>
</E1E>
In XSLT, variables once declared/defined, they cannot be changed. And exists only in the loop they are defined.
You might not need the xsl:for-each loop here. Instead the variables can be globally defined, so that you can use them where you want in your xslt.
You can try the following:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:func="myfunc"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="text" encoding="utf-8" />
<xsl:output omit-xml-declaration="yes" />
<xsl:param name="break" select="'
'" />
<xsl:variable name="partn" select="/ZGS/ID/E1[PA = 'CE']/PAN" />
<xsl:variable name="date3" select="substring(/ZGS/ID/E13[ID = '033']/DAT,3,8)" />
<xsl:template match="ZGS/ID">
<xsl:for-each select="E1E">
<xsl:text>823</xsl:text>
<xsl:text>03</xsl:text>
<xsl:for-each select="E1ED">
<xsl:if test="QU = 012 ">
<xsl:value-of select="BEL" />
</xsl:if>
</xsl:for-each>
<xsl:value-of select="$partn" />
<xsl:value-of select="$date3" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/jyRYYiu
Using xsl:param, it can be achieved as
<xsl:param name="partn" select="/ZGS/ID/E1[PA = 'CE']/PAN" />
<xsl:param name="date3" select="substring(/ZGS/ID/E13[ID = '033']/DAT,3,8)" />
<xsl:template match="ZGS/ID">
<xsl:for-each select="E1E">
<xsl:text>823</xsl:text>
<xsl:text>03</xsl:text>
<xsl:for-each select="E1ED">
<xsl:if test="QU = 012 ">
<xsl:value-of select="BEL" />
</xsl:if>
</xsl:for-each>
<xsl:for-each select="$partn">
<xsl:value-of select="." />
</xsl:for-each>
<xsl:value-of select="$date3" />
</xsl:for-each>
</xsl:template>
https://xsltfiddle.liberty-development.net/jyRYYiu/1

XSL:KEY Function for external file

I have an issue in key function, key function not running in following code.
my input is
XML (Keys.xml)
<?xml version="1.0" encoding="UTF-8"?>
<Keys>
<Key year="2001" name="ABC"/>
<Key year="2002" name="BCA"/>
</Keys>
XML to convert
<?xml version="1.0" encoding="UTF-8"?>
<p>
<text> .. .. <key>ABC</key> ...</text>
<text> .. .. <key>BCA</key> ...</text>
</p>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:key name="keydata" match="*[name() = document('keys.xml')]/Keys/Key" use="#name"/>
<xsl:template match="key">
<xsl:copy>
<xsl:attribute name="ref"><xsl:value-of select="key('keydata', .)/#year"/></xsl:attribute>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output
<p>
<text> .. .. <key ref="">ABC</key> ...</text>
<text> .. .. <key ref="">BCA</key> ...</text>
</p>
Desired Ouput
<p>
<text> .. .. <key ref="2001">ABC</key> ...</text>
<text> .. .. <key ref="2002">BCA</key> ...</text>
</p>
This should work:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:variable name="keys" select="document('keys.xml')" as="document-node()"/>
<xsl:key name="keydata" match="Key" use="#name"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="key">
<xsl:copy>
<xsl:attribute name="ref"><xsl:value-of select="$keys/key('keydata', current())/#year"/></xsl:attribute>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In the XSL specification, there's an example entitled "Example: Using Keys to Reference other Documents" that exactly matches your use case.
This is the resulting document:
<?xml version="1.0" encoding="UTF-8"?>
<p>
<text> .. .. <key ref="2001">ABC</key> ...</text>
<text> .. .. <key ref="2002">BCA</key> ...</text>
</p>
Change <xsl:key name="keydata" match="*[name() = document('keys.xml')]/Keys/Key" use="#name"/> to <xsl:key name="keydata" match="Keys/Key" use="#name"/> and then <xsl:attribute name="ref"><xsl:value-of select="key('keydata', .)/#year"/></xsl:attribute> to <xsl:attribute name="ref" select="key('keydata', . doc('Keys.xml'))/#year"/></xsl:attribute>.

Extracting value from External xml based on some rule in xsl

This is input xml input.xml
<root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
This is Prop.xml
<?xml version="1.0" encoding="utf-8"?>
<properties>
<code dpsi="0BZG" docid="asdww">HKBL1.0001.lohk.CAP65</code>
<code dpsi="0BZH" docid="navin">HKBL1.0002.aohk.CAP383</code>
<code no="3">345</code>
</properties>
This is desired output
<root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65" dpsi="0BZG" docid="asdww">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
IF prop.xml code/text matches remotelink/#refptid than copy attribute of prop.xml to remotelink otherwise no changes in remotelink.
This is the XSLT I have written. So far I am not getting result for unmatched condition:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlsx="http://www.stylusstudio.com/XSLT/XLSX" xmlns:spml="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:saxon="http://saxon.sf.net/" version="2.0">
<xsl:template match="#*|node()" name="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="remotelink[#service='DOC-ID']" name="t-remote">
<xsl:variable name="refptid" select="./#refpt"/>
<xsl:variable name="path" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')"/>
<xsl:for-each select="$path/properties/code">
<xsl:choose>
<xsl:when test="./text()=$refptid">
<xsl:element name="remotelink">
<xsl:attribute name="DOC-ID" select="./#docid"/>
<xsl:attribute name="dpsi" select="./#dpsi"/>
<xsl:attribute name="refpt" select="$refptid"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:if test="./#docrefid"/>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="remotelink[#service='DOC-ID']"> will never never be triggered, there's no service attribute on the <remotelink> element. Moreover, you don't retrieve the correct attribute (refpt instead of refptid)
This XSL transformation should do the job:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlsx="http://www.stylusstudio.com/XSLT/XLSX" xmlns:spml="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:saxon="http://saxon.sf.net/" version="2.0">
<xsl:variable name="props.doc" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')/properties" />
<xsl:template match="#*|node()" name="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="remotelink" name="t-remote">
<xsl:variable name="refptid" select="./#refptid"/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:if test="$props.doc/code[. = $refptid]">
<xsl:apply-templates select="$props.doc/code[. = $refptid]/#*"/>
</xsl:if>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is the result of the transformation:
<?xml version="1.0" encoding="UTF-8"?><root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65" dpsi="0BZG" docid="asdww">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
I would simply define a global parameter or variable <xsl:variable name="path" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')"/>, then set up a key <xsl:key name="prop" match="code" use="."/>, and then use that in a template
<xsl:template match="remotelink[key('prop', #refptid, $path)]">
<xsl:copy>
<xsl:copy-of select="key('prop', #refptid, $path)/#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
that, together with your first template, should suffice.

Compare the different line item fields and map the value which is in the same xml using xslt2.0

below one is the XML input,
<?xml version="1.0" encoding="utf-8"?>
<GSK_Canonical_MESGX2>
<header SEGMENT="1">
<orderNumber>002001454979</orderNumber>
<batchNumber>0000617944</batchNumber>
<BOM SEGMENT="1">
<operationNumber>0030</operationNumber>
<phaseIndicator>0011</phaseIndicator>
</BOM>
<BOM SEGMENT="1">
<operationNumber>0040</operationNumber>
<phaseIndicator>0012</phaseIndicator>
</BOM>
<recipe SEGMENT="1">
<phase>0011</phase>
<parentOperation>0030</parentOperation>
<workcenter>MANUOHD1</workcenter>
</recipe>
<recipe SEGMENT="1">
<phase>0012</phase>
<parentOperation>0040</parentOperation>
<workcenter>COSTOHD1</workcenter>
</recipe>
</header>
</GSK_Canonical_MESGX2>
I have an below xslt,
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:set="http://exslt.org/sets" xmlns:str="http://exslt.org/strings" xmlns:java="http://xml.apache.org/xslt/java" xmlns:saxon="http://saxon.sf.net/" exclude-result-prefixes="exsl set str java saxon">
<xsl:output method="text"/>
<xsl:variable name="VarHash" select="'#'"/>
<xsl:variable name="VarBreak" select="'
'"/>
<xsl:variable name="pipeFieldDelimiter" select="'\|'"/>
<xsl:template match="/">
<xsl:text>HEADER</xsl:text>
<xsl:value-of select="$VarHash"/>
<xsl:value-of select="GSK_Canonical_MESGX2/header/orderNumber"/>
<xsl:value-of select="$VarHash"/>
<xsl:value-of select="GSK_Canonical_MESGX2/header/batchNumber"/>
<xsl:value-of select="$VarBreak"/>
<xsl:for-each select="GSK_Canonical_MESGX2/header/BOM">
<!--GSK_Canonical_MESGX2/Header/BOM/OperationNumber = GSK_Canonical_MESGX2/header/recipe/parentOperation and GSK_Canonical_MESGX2/Header/BOM/phaseIndicator = GSK_Canonical_MESGX2/header/recipe/phase then <xsl:value-of select="GSK_Canonical_MESGX2/header/recipe/workcenter"/> This needs to be implemented for each line item of BOM tag -->
<xsl:if test="position() != last()">
<xsl:value-of select="$VarBreak"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
below one is the expected output,
HEADER#002001454979#0000617944
MANUOHD1
COSTOHD1
now need to implement for each BOM line item,we need to compare BOM with Recipe tags and select workcenter value if the condition satisfied.
Header/BOM/OperationNumber = header/recipe/parentOperation
and
Header/BOM/phaseIndicator = header/recipe/phase
then
<xsl:value-of select="GSK_Canonical_MESGX2/header/recipe/workcenter"/>
Please help me to achieve this.Thanks
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kBomByPhaseAndOperation" match="BOM"
use="concat(operationNumber, '|', phaseIndicator)"/>
<xsl:template match=
"recipe[key('kBomByPhaseAndOperation',
concat(parentOperation, '|', phase))
]">
<xsl:value-of select="concat('
', workcenter)"/>
</xsl:template>
<xsl:template match="header">
<xsl:text>HEADER</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="orderNumber|batchNumber">
<xsl:value-of select="concat('#', .)"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When applied on the provided source XML document:
<GSK_Canonical_MESGX2>
<header SEGMENT="1">
<orderNumber>002001454979</orderNumber>
<batchNumber>0000617944</batchNumber>
<BOM SEGMENT="1">
<operationNumber>0030</operationNumber>
<phaseIndicator>0011</phaseIndicator>
</BOM>
<BOM SEGMENT="1">
<operationNumber>0040</operationNumber>
<phaseIndicator>0012</phaseIndicator>
</BOM>
<recipe SEGMENT="1">
<phase>0011</phase>
<parentOperation>0030</parentOperation>
<workcenter>MANUOHD1</workcenter>
</recipe>
<recipe SEGMENT="1">
<phase>0012</phase>
<parentOperation>0040</parentOperation>
<workcenter>COSTOHD1</workcenter>
</recipe>
</header>
</GSK_Canonical_MESGX2>
produces exactly the wanted, correct result:
HEADER#002001454979#0000617944
MANUOHD1
COSTOHD1

Resources