Converting mutiple xml elements into single complex xml element.
I would like to group child elements into one complex xml using parent value.
In the example given , source xml contains two xml records for AUDIT_ID=1.
Based on the AUDIT_ID value can we combine two into single as given in the target xml using xslt?
Source xml:
<Header>
<Audit_records>
<AUDIT_ID>1</AUDIT_ID>
<ESI_ID>100</ESI_ID>
<ESI_NAME>AB</ESI_NAME>
</Audit_records>
<Audit_records>
<AUDIT_ID>1</AUDIT_ID>
<ESI_ID>101</ESI_ID>
<ESI_NAME>BC</ESI_NAME>
</Audit_records>
<Audit_records>
<AUDIT_ID>2</AUDIT_ID>
<ESI_ID>103</ESI_ID>
<ESI_NAME>TH</ESI_NAME>
</Audit_records>
<Audit_records>
<AUDIT_ID>2</AUDIT_ID>
<ESI_ID>104</ESI_ID>
<ESI_NAME>UI</ESI_NAME>
</Audit_records>
</Header>
Target Xml:
<Header>
<Audit_records>
<AUDIT_ID>1</AUDIT_ID>
<ESI>
<ESI_ID>100</ESI_ID>
<ESI_NAME>AB</ESI_NAME>
</ESI>
<ESI>
<ESI_ID>101</ESI_ID>
<ESI_NAME>BC</ESI_NAME>
</ESI>
</Audit_records>
<Audit_records>
<AUDIT_ID>2</AUDIT_ID>
<ESI>
<ESI_ID>103</ESI_ID>
<ESI_NAME>TH</ESI_NAME>
</ESI>
<ESI>
<ESI_ID>104</ESI_ID>
<ESI_NAME>UI</ESI_NAME>
</ESI>
</Audit_records>
</Header>
xslt used:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0"
xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
xmlns:ns0="http://xmlns.oracle.com/pcbpel/adapter/db/getrecord"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://xmlns.oracle.com/ATAS_BPEL">
<!--<xsl:key name="myaudit" match="/ns0:getrecordOutputCollection/ns0:getrecordOutput" use="/ns0:getrecordOutputCollection/ns0:getrecordOutput/ns0:AUDIT_ID"/> -->
<xsl:key match="Audit_records" name="myaudit" use="AUDIT_ID"/>
<xsl:template match="/">
<ns1:Header>
<xsl:for-each select="/ns0:Header/ns0:Audit_records[generate-id() = generate-id(key('myaudit',AUDIT_ID)[1])]">
<xsl:sort select="AUDIT_ID" order="ascending" data-type="number" />
<ns1:CARF>
<ns1:Header>
<ns1:AUDIT_ID>
<xsl:value-of select="ns0:AUDIT_ID"/>
</ns1:AUDIT_ID>
<ns1:ESI >
<ns1:ESI_ID>
<xsl:value-of select="key('myaudit',AUDIT_ID)/ns0:ESI_ID"/>
</ns1:ESI_ID>
<ns1:ESI_NAME>
<xsl:value-of select="key('myaudit',AUDIT_ID)/ns0:ESI_NAME"/>
</ns1:ESI_NAME>
</ns1:ESI>
</ns1:Header>
</ns1:CARF>
</xsl:for-each>
</ns1:AUDITRECORDS>
</xsl:template>
</xsl:stylesheet>
Your XSLT is using namespaces, but your sample input/desired output does not.
Here's something that should get you started, but you'll have to modify it for whatever namespaces are in your actual data...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="records" match="Audit_records" use="AUDIT_ID"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Header">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each select="Audit_records[generate-id()=generate-id(key('records',AUDIT_ID)[1])]">
<xsl:copy>
<xsl:apply-templates select="#*|AUDIT_ID"/>
<xsl:for-each select="key('records',AUDIT_ID)">
<ESI>
<xsl:apply-templates select="*[not(self::AUDIT_ID)]"/>
</ESI>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Fiddle: http://xsltfiddle.liberty-development.net/bEzknsz/1
Related
I need to duplicate a node and its child elements, if it occurs only once in the xml. Otherwise, the xml shouldn't be modified. For ex, in the below xml, if <dataList> occurs only once then duplicate it one more time. If not, don't change the xml at all. Only XSLT 1.0 please.
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<API>
<Token/>
<root>
<dataList>
<addressOne>1</addressOne>
<addressTwo/>
<bkdn/>
</dataList>
</root>
</API>
Expected output xml
<?xml version="1.0" encoding="UTF-8"?>
<API>
<Token/>
<root>
<dataList>
<addressOne>1</addressOne>
<addressTwo/>
<bkdn/>
</dataList>
<dataList>
<addressOne>1</addressOne>
<addressTwo/>
<bkdn/>
</dataList>
</root>
</API>
As per my understanding here i want to solve it:
<?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="1.0">
<xsl:output indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:choose>
<!-- If you are looking for the dataList occurance then use count -->
<xsl:when test="count(dataList) = 1">
<!-- If you are looking for the dataList/addressOne value = 1 occurance then use below -->
<!-- <xsl:when test="dataList/addressOne=1"> -->
<xsl:apply-templates select="dataList"/>
<xsl:apply-templates select="dataList"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="dataList"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I tried all the codes I've seen in the internet with relevant requirement as I have. However, in my case, I also need to populate the namespace within the inner parent group. My XSLT didn't work as expected. Can anyone help me with this? Thank you.
XSLT CODE:
<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:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Section">
<Section xmlns="www.hdgd.co">
<xsl:apply-templates select="#*|node()"/>
</Section>
</xsl:template>
INPUT:
<Record xmlns="www.hdgd.co">
<Data>
<Section>
<ID>1234DFD57</ID>
</Section>
</Data>
EXPECTED OUTPUT:
<Record>
<Data>
<Section xmlns="www.hdgd.co">
<ID>1234DFD57</ID>
</Section>
</Data>
GENERATED OUTPUT:
<Record xmlns="www.hdgd.co">
<Data>
<Section>
<ID>1234DFD57</ID>
</Section>
</Data>
You seem to be unaware of namespaces inheritance. The default namespace declaration at the Record root element is applied to all elements of the input document. Therefore, in order to achieve the requested result, you must take all elements out of their namespace, while leaving the Section element and its descendants unprocessed:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="www.hdgd.co">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="Section">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Added:
If your input has attributes that need copying, then change the first template to:
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
That sounds as if you want to remove the namespace from Record and Data:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xpath-default-namespace="www.hdgd.co">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Record | Data">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:transform>
http://xsltransform.net/bEzjRJM gives
<?xml version="1.0" encoding="UTF-8"?><Record>
<Data>
<Section xmlns="www.hdgd.co">
<ID>1234DFD57</ID>
</Section>
</Data>
</Record>
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.
Can someone guide me on how to write a XSLT1.0 to create the output as below?
<csvImportSchema>
<payload>
<test>**COPY VALUE**</test>
<test2>2</test2>
<test3>3</test3>
<ean>1111111111</ean>
<productId/>
</payload>
</csvImportSchema>
to
<csvImportSchema>
<payload>
<test>COPY VALUE</test>
<test2>2</test2>
<test3>3</test3>
<ean>1111111111</ean>
<productId/>
**<copied>COPY VALUE</copied>**
</payload>
<csvImportSchema>
The following XSLT extracts the COPY VALUE string between ** and copies it to the end of the <payload> tag. Then the strings before and after the COPY VALUE are used as prefixes and postfixes of the <copied> tag.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="payload">
<xsl:variable name="copyval" select="substring-before(substring-after(test/text(),'**'),'**')" />
<xsl:variable name="valBefore" select="substring-before(test/text(),$copyval)" />
<xsl:variable name="valAfter" select="substring-after(test/text(),$copyval)" />
<xsl:copy>
<test><xsl:value-of select="$copyval" /></test>
<xsl:apply-templates select="node()[not(self::test)]|#*" />
<xsl:value-of select="$valBefore" /><copied><xsl:value-of select="$copyval" /></copied><xsl:value-of select="$valAfter" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I wanted to extract leaf nodes and have them sorted.
My XSL gives unexpected results. How can I solve this?
Input
<root>
<b>
<b33 zzz="2" fff="3"></b33>
<b11></b11>
<b22></b22>
</b>
<a>
<a27></a27>
<a65 fff="0" eee="2" zzz="10"></a65>
<a11></a11>
</a>
</root>
Xsl
<?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" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:call-template name="leafnodes"/>
</root>
</xsl:template>
<xsl:template match="*[not(*)]|#*" name="leafnodes">
<xsl:copy>
<xsl:apply-templates select="node()">
<xsl:sort select="name()"/>
</xsl:apply-templates>
<xsl:apply-templates select="#*">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output (I would expected it to be sorted, it is not)
<root>
<b33 fff="3" zzz="2" />
<b11 />
<b22 />
<a27 />
<a65 eee="2" fff="0" zzz="10" />
<a11 />
</root>
I would expect the nodes in the order a11, a27, a65, b11, b22, b33.
If I leave out the '[not(*)]', the xsl takes all nodes and sorts them properly.
How can this be solved?
To output all element which have no child sorted by name and the attributes also sorted by name. Try 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" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="//*[not(*)]">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</root>
</xsl:template>
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates select="#*" >
<xsl:sort select="name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Which will generate following output:
<root>
<a11/>
<a27/>
<a65 eee="2" fff="0" zzz="10"/>
<b11/>
<b22/>
<b33 fff="3" zzz="2"/>
</root>