XSLT - Traverse through root node - xslt

I have the XML structure as below
I am deep down in the inner elements of env:Envelope and want to jump to something in Report_Entry.
I tried multiple paths like *, .., . and more but was not able to get to the expected node.
The <xsl:for-each select="/*"> is taking me to the root but <xsl:for-each select="/*/wd:Report_Entry"> does NOT work
<root>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<Child1>
<Child2>
<Child3>
</Chiild3>
</Child2>
</Child1>
</env:Envelope>
<wd:Report_Entry xmlns:wd="urn:com.workday.report/CR_INT001_Worker_Benefit_Group>
<Child4>
<Child5>
</Child5>
</Child4>
</wd:Report_Entry>
</root>
Here are the XSLT declarations
<xsl:stylesheet version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wd="urn:com.workday/bsvc"
xmlns:out="http://www.workday.com/integration/output"
xmlns:func="http://www.func.com"
xmlns:xtt="urn:com.workday/xtt"
xmlns:env="urn:com.workday/bsvc" wd:version="v35.0"
exclude-result-prefixes="xs wd out xsl xtt func">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/root">
<Root>
<-- I am in Chiild 3 and want to retrieve the value from Child 5-->
</Root>
</xsl:template>
</xsl:stylesheet>
Any help appreciated

Related

How to use Key in XSLT Oxygen

I am trying to get the output based on two separate nodes in XML using key concept of XSLT
I have below XML
<?xml version="1.0" encoding="UTF-8"?>
<mdti:Input xmlns:mdti="urn:com.workday/multiDocumentTransform/Input">
<mdti:Files xmlns:mdti="urn:com.workday/multiDocumentTransform/Input">
<mdti:EventFiles>
<mdti:File mdti:filename="first.xml" mdti:contentType="text/xml">
<wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
<wd:Report_Entry>
<wd:key>1234</wd:key>
<wd:comp>ABC</wd:comp>
<wd:asof>2021-03-24T04:59:32.179-07:00</wd:asof>
<wd:emplid>33333333</wd:emplid>
<wd:worker_type>EMP</wd:worker_type>
<wd:emp_type>Regular</wd:emp_type>
<wd:orig_hire_dt>2021-11-27</wd:orig_hire_dt>
<wd:rehire_dt>2019-04-01</wd:rehire_dt>
<wd:home_host_class>M</wd:home_host_class>
<wd:service_dt>2014-11-27</wd:service_dt>
</wd:Report_Entry>
</wd:Report_Data>
</mdti:File>
<mdti:File mdti:filename="second.xml" mdti:contentType="text/xml">
<wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
<wd:Report_Entry>
<wd:key>1234</wd:key>
<wd:supervisor_lname>xyz</wd:supervisor_lname>
<wd:hr_status>A</wd:hr_status>
<wd:hr_status_descr>Active</wd:hr_status_descr>
<wd:empl_status>A</wd:empl_status>
<wd:empl_status_descr>Active</wd:empl_status_descr>
<wd:ben_status>A</wd:ben_status>
<wd:home_address_change_dt>2019-07-30</wd:home_address_change_dt>
<wd:location>444</wd:location>
<wd:location_descr>Ind</wd:location_descr>
</wd:Report_Entry>
</wd:Report_Data>
</mdti:File>
</mdti:EventFiles>
</mdti:Files>
</mdti:Input>
**I am using below XSLT for my data. Please let me know if I am missing something and way to do it. The only identifier in each node is the mdti:filename **
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mdti="urn:com.workday/multiDocumentTransform/Input"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wd="urn:com.workday/bsvc"
exclude-result-prefixes="#all">
<xsl:key name="share" match="mdti:Input/mdti:Files/mdti:EventFiles/mdti:File/wd:Report_Data/wd:Report_Entry" use="mdti:Input/mdti:Files/mdti:EventFiles/mdti:File/wd:Report_Data/wd:Report_Entry/wd:key"/>
<xsl:template match="/">
<data>
<key>1234</key>
<xsl:copy-of select="key('share', wd:key)/wd:hr_status"/>
<emp_type>Regular</emp_type>
<supervisor_lname>xyz</supervisor_lname>
<hr_status>A</hr_status>
<location>444</location>
</data>
</xsl:template>
</xsl:stylesheet>
You have several mistakes.
First, <xsl:template match="/"> puts you in the context of the root node; from this context, the expression wd:key selects nothing - so your instruction <xsl:copy-of select="key('share', wd:key)/wd:hr_status"/> does nothing.
Next, if you want your key to match nodes in the second file, you should restrict it to match only nodes in the second file. Also, the use attribute must be relative to the matched node.
Furthermore, if you want the result to be in no-namespace, you cannot copy nodes from the input - at least not in XSLT 1.0.
There is more, but these are the ones that stand out immediately.
Consider the following example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mdti="urn:com.workday/multiDocumentTransform/Input"
xmlns:wd="urn:com.workday/bsvc"
exclude-result-prefixes="mdti wd">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="entry2" match="mdti:File[#mdti:filename='second.xml']/wd:Report_Data/wd:Report_Entry" use="wd:key"/>
<xsl:template match="/mdti:Input">
<root>
<xsl:for-each select="mdti:Files/mdti:EventFiles/mdti:File[#mdti:filename='first.xml']/wd:Report_Data/wd:Report_Entry">
<data>
<!-- data from file1 -->
<key>
<xsl:value-of select="wd:key"/>
</key>
<emplid>
<xsl:value-of select="wd:emplid"/>
</emplid>
<!-- data from file2 -->
<xsl:variable name="entry2" select="key('entry2', wd:key)" />
<supervisor_lname>
<xsl:value-of select="$entry2/wd:supervisor_lname"/>
</supervisor_lname>
<hr_status>
<xsl:value-of select="$entry2/wd:hr_status"/>
</hr_status>
</data>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, this will produce:
Result
<?xml version="1.0" encoding="utf-8"?>
<root>
<data>
<key>1234</key>
<emplid>33333333</emplid>
<supervisor_lname>xyz</supervisor_lname>
<hr_status>A</hr_status>
</data>
</root>
You can add fields from both branches as required.

Merge data from two node sets using XSLT [duplicate]

I am trying to get the output based on two separate nodes in XML using key concept of XSLT
I have below XML
<?xml version="1.0" encoding="UTF-8"?>
<mdti:Input xmlns:mdti="urn:com.workday/multiDocumentTransform/Input">
<mdti:Files xmlns:mdti="urn:com.workday/multiDocumentTransform/Input">
<mdti:EventFiles>
<mdti:File mdti:filename="first.xml" mdti:contentType="text/xml">
<wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
<wd:Report_Entry>
<wd:key>1234</wd:key>
<wd:comp>ABC</wd:comp>
<wd:asof>2021-03-24T04:59:32.179-07:00</wd:asof>
<wd:emplid>33333333</wd:emplid>
<wd:worker_type>EMP</wd:worker_type>
<wd:emp_type>Regular</wd:emp_type>
<wd:orig_hire_dt>2021-11-27</wd:orig_hire_dt>
<wd:rehire_dt>2019-04-01</wd:rehire_dt>
<wd:home_host_class>M</wd:home_host_class>
<wd:service_dt>2014-11-27</wd:service_dt>
</wd:Report_Entry>
</wd:Report_Data>
</mdti:File>
<mdti:File mdti:filename="second.xml" mdti:contentType="text/xml">
<wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
<wd:Report_Entry>
<wd:key>1234</wd:key>
<wd:supervisor_lname>xyz</wd:supervisor_lname>
<wd:hr_status>A</wd:hr_status>
<wd:hr_status_descr>Active</wd:hr_status_descr>
<wd:empl_status>A</wd:empl_status>
<wd:empl_status_descr>Active</wd:empl_status_descr>
<wd:ben_status>A</wd:ben_status>
<wd:home_address_change_dt>2019-07-30</wd:home_address_change_dt>
<wd:location>444</wd:location>
<wd:location_descr>Ind</wd:location_descr>
</wd:Report_Entry>
</wd:Report_Data>
</mdti:File>
</mdti:EventFiles>
</mdti:Files>
</mdti:Input>
**I am using below XSLT for my data. Please let me know if I am missing something and way to do it. The only identifier in each node is the mdti:filename **
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mdti="urn:com.workday/multiDocumentTransform/Input"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wd="urn:com.workday/bsvc"
exclude-result-prefixes="#all">
<xsl:key name="share" match="mdti:Input/mdti:Files/mdti:EventFiles/mdti:File/wd:Report_Data/wd:Report_Entry" use="mdti:Input/mdti:Files/mdti:EventFiles/mdti:File/wd:Report_Data/wd:Report_Entry/wd:key"/>
<xsl:template match="/">
<data>
<key>1234</key>
<xsl:copy-of select="key('share', wd:key)/wd:hr_status"/>
<emp_type>Regular</emp_type>
<supervisor_lname>xyz</supervisor_lname>
<hr_status>A</hr_status>
<location>444</location>
</data>
</xsl:template>
</xsl:stylesheet>
You have several mistakes.
First, <xsl:template match="/"> puts you in the context of the root node; from this context, the expression wd:key selects nothing - so your instruction <xsl:copy-of select="key('share', wd:key)/wd:hr_status"/> does nothing.
Next, if you want your key to match nodes in the second file, you should restrict it to match only nodes in the second file. Also, the use attribute must be relative to the matched node.
Furthermore, if you want the result to be in no-namespace, you cannot copy nodes from the input - at least not in XSLT 1.0.
There is more, but these are the ones that stand out immediately.
Consider the following example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mdti="urn:com.workday/multiDocumentTransform/Input"
xmlns:wd="urn:com.workday/bsvc"
exclude-result-prefixes="mdti wd">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="entry2" match="mdti:File[#mdti:filename='second.xml']/wd:Report_Data/wd:Report_Entry" use="wd:key"/>
<xsl:template match="/mdti:Input">
<root>
<xsl:for-each select="mdti:Files/mdti:EventFiles/mdti:File[#mdti:filename='first.xml']/wd:Report_Data/wd:Report_Entry">
<data>
<!-- data from file1 -->
<key>
<xsl:value-of select="wd:key"/>
</key>
<emplid>
<xsl:value-of select="wd:emplid"/>
</emplid>
<!-- data from file2 -->
<xsl:variable name="entry2" select="key('entry2', wd:key)" />
<supervisor_lname>
<xsl:value-of select="$entry2/wd:supervisor_lname"/>
</supervisor_lname>
<hr_status>
<xsl:value-of select="$entry2/wd:hr_status"/>
</hr_status>
</data>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, this will produce:
Result
<?xml version="1.0" encoding="utf-8"?>
<root>
<data>
<key>1234</key>
<emplid>33333333</emplid>
<supervisor_lname>xyz</supervisor_lname>
<hr_status>A</hr_status>
</data>
</root>
You can add fields from both branches as required.

Create xml tree from xPath expression using xslt

I have a sample source xml like:
<?xml version="1.0" encoding="utf-16"?>
<shiporder orderid="orderid1">
<item>
item1
</item>
<item>
item2
</item>
</shiporder>
The assumptions for the transformation of my source xml is that I have a list of xPaths in this case /shiporder[1]/item[1] and /shiporder[1]/item[2] and the information that I have to transform the content of those nodes into Customer[1]/adress[1] and Customer[1]/adress[2]. In detail the result should look like:
<?xml version="1.0" encoding="utf-16"?>
<customer>
<adress>
item1
</adress>
<adress>
item2
</adress>
</customer>
Now I'm looking for a template which applies for each path in my source list ["/shiporder[1]/item"] and transform the inner text of that nodes into nodes with respect to ["Customer[1]/adress"].
First of all I thought to create templates like:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:func="http://www.functx.com">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/shiporder[1]">
<Customer>
<xsl:apply-templates select="item"/>
</Customer>
</xsl:template>
<xsl:template match="/shiporder[1]/item">
<adress>
<xsl:value-of select="text()"/>
</adress>
</xsl:template>
</xsl:stylesheet>
But later I was thinking about a function which combines the implicit call of the templates in a recursive way. I hope it is clear from here on other wise I could add some pseudo code what kind of function I have in mind.

Extract node value by calculating absolute position

My source XML looks:
<test>
<text1>Test</text1>
<text2>Test</text2>
<text2>Test</text2>
<section>
<text1>Test<bold>content</bold></text1>
<text1>Test</text1>
<text2>Test</text2>
<text2>Test</text2>
</section>
</test>
Want to extract the value of 6th node, based on the absolute number of the element (overall count). The absolute number of the element has been identified using <xsl:number level="any" from="/" count="*"/>.
The XPath expression /descendant::*[6] should give you the element you need.
<xsl:template match="/">
<xsl:copy-of select="/descendant::*[6]" />
</xsl:template>
outputs
<text1>Test<bold>content</bold></text1>
Note that this is an example of the difference between descendant:: and // - //*[6] would give you all elements that are the sixth child element of their respective parent, rather than simply the sixth element in the document in depth-first order.
This xslt
<?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" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="allElements" select="//element()" />
<xsl:template match="/">
<output>
<xsl:value-of select="$allElements[6]" />
</output>
</xsl:template>
</xsl:stylesheet>
will result in
<?xml version="1.0" encoding="UTF-8"?>
<output>Testcontent</output>

How to select the value from an attribute that has a colon in xslt?

I am working with xslt to handle the results that are returned from a web service. I first need to determine which web service the results are for. I know that the tag platformCore:record has the attribute "xsi:type="listRel:Contact or "xsi:type="listEmp:Employee". I am trying to select the value that the attribute is storing, but the colon seems to be causing some issues when I attempt to select the value.
Here is what I tried, but fails to work.
<?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" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:variable name="Type"><xsl:value-of select="//*[local-name()='searchResponse']//*[local-name()='searchResult']//*[local-name()='recordList']//*[local-name()='record']#xsi:type"/></xsl:variable>
<root>
<test><xsl:value-of select="$Type"/></test>
</root>
</xsl:template>
</xsl:stylesheet>
Here is a simple sample
<?xml version="1.0" encoding="UTF-8"?>
<searchResponse:searchResponse xmlns="urn:messages_2012_2.platform.webservices.itsthesuite.com"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:searchResponse="urn:messages_2012_2.platform.webservices.itsthesuite.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<platformCore:searchResult xmlns:platformCore="urn:core_2012_2.platform.webservices.itsthesuite.com">
<platformCore:status isSuccess="true"/>
<platformCore:totalRecords>1</platformCore:totalRecords>
<platformCore:recordList>
<platformCore:record internalId="154098" xsi:type="listRel:Contact" xmlns:listRel="urn:relationships_2012_2.lists.webservices.itsthesuite.com">
<listRel:entityId>John Smith</listRel:entityId>
<listRel:firstName>John</listRel:firstName>
<listRel:lastName>Smith</listRel:lastName>
<listRel:phone>(777) 777-7777</listRel:phone>
<listRel:email>john.smith#yormoms.com</listRel:email>
</platformCore:record>
</platformCore:recordList>
</platformCore:searchResult>
</searchResponse:searchResponse>
I need the solution to work for this sample as well.
Employee Sample
<?xml version="1.0" encoding="UTF-8"?>
<searchResponse xmlns="urn:messages_2012_2.platform.webservices.netsuite.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:searchResponse="urn:messages_2012_2.platform.webservices.netsuite.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<platformCore:searchResult xmlns:platformCore="urn:core_2012_2.platform.webservices.netsuite.com" >
<platformCore:status isSuccess="true"/>
<platformCore:totalRecords>1</platformCore:totalRecords>
<platformCore:recordList>
<platformCore:record internalId="158778" xsi:type="listEmp:Employee" xmlns:listEmp="urn:employees_2012_2.lists.webservices.netsuite.com">
<listEmp:entityId>331sfds Dipo Chaponda</listEmp:entityId>
<listEmp:salutation>Mr.</listEmp:salutation>
<listEmp:firstName>Dipo</listEmp:firstName>
<listEmp:lastName>Chaponda</listEmp:lastName>
<listEmp:email>dchapond#youmm.com</listEmp:email>
</platformCore:record>
</platformCore:recordList>
</platformCore:searchResult>
</searchResponse>
You can select an attribute using local name similarly to what you are already doing, but by prefacing the * with an #:
#*[local-name() = 'type']
However, littering your XPaths with local-name() = and double slashes is not a good practice. You should use namespaces properly, and use precise paths when they are known, although it seems that is not an option for the elements in your case because they are using different namespaces in the two examples. This should work:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="sr pc xsi"
>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:variable name="Type">
<xsl:value-of select="*[local-name() = 'searchResponse']/
*[local-name() = 'searchResult']/
*[local-name() = 'recordList']/
*[local-name() = 'record']/
#xsi:type"/>
</xsl:variable>
<root>
<test>
<xsl:value-of select="$Type"/>
</test>
</root>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces the expected result:
<root>
<test>listRel:Contact</test>
</root>