I want to convert one xml format to another xml format using XSLT .
Below is the input XML format that i need to transform to another XML format.
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
<RequestType>DocGen</RequestType>
<Version>10.6</Version>
<BankId>01</BankId>
<ChannelId>LOS</ChannelId>
</Header>
<Body>
<Data>
<CorpAppLimitDetailsBO>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
<DBApplicantMiscDetails>
<APPLICANTMISCID>400000</APPLICANTMISCID>
<APPLICANTID>400030</APPLICANTID>
<MISCTYPE>APPLIED</MISCTYPE>
</DBApplicantMiscDetails>
</CorpAppLimitDetailsBO>
</Data>
</Body>
</FIXML>
Below is the output format xml which i expect.
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
<RequestType>DocGen</RequestType>
<Version>10.6</Version>
<BankId>01</BankId>
<ChannelId>LOS</ChannelId>
</Header>
<Body>
<Data>
<LimitDetails>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
<APPLICANTMISCID>400000</APPLICANTMISCID>
<APPLICANTID>400030</APPLICANTID>
<MISCTYPE>APPLIED</MISCTYPE>
</Limit>
</Data>
</Body>
</FIXML>
I have tried with below code but dont know how to modify the code to inlcude DBApplicantMiscDetails details in output XML format.
<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="/">
<xsl:apply-templates select="FIXML"/>
</xsl:template>
<xsl:template match="FIXML">
<FIXML>
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Body"/>
</FIXML>
</xsl:template>
<xsl:template match="Header">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Body">
<Body>
<xsl:apply-templates select="Data"/>
</Body>
</xsl:template>
<xsl:template match="Data">
<Data>
<LimitDetails>
<xsl:apply-templates select="CorpAppLimitDetailsBO"/>
</LimitDetails>
</Data>
</xsl:template>
<xsl:template match="CorpAppLimitDetailsBO">
<Limit>
<xsl:copy-of select="child::*"/>
</Limit>
</xsl:template>
</xsl:stylesheet>
The way you could probably approach this is to build upon the XSLT identity transform, which on its own just copies the nodes in your XML
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
You then add templates to match your special cases where you want to make changes (as opposed to written templates for elements you just want to copy). For example, to rename the CorpAppLimitDetailsBO to Limit you would do this
<xsl:template match="CorpAppLimitDetailsBO">
<Limit>
<xsl:apply-templates />
</Limit>
</xsl:template>
Removing the DBApplicationMiscDetails then becomes a straight-forward task
<xsl:template match="DBApplicantMiscDetails">
<xsl:apply-templates />
</xsl:template>
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Data">
<Data>
<LimitDetails>
<xsl:apply-templates />
</LimitDetails>
</Data>
</xsl:template>
<xsl:template match="CorpAppLimitDetailsBO">
<Limit>
<xsl:apply-templates />
</Limit>
</xsl:template>
<xsl:template match="DBApplicantMiscDetails">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
<RequestType>DocGen</RequestType>
<Version>10.6</Version>
<BankId>01</BankId>
<ChannelId>LOS</ChannelId>
</Header>
<Body>
<Data>
<LimitDetails>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
<APPLICANTMISCID>400000</APPLICANTMISCID>
<APPLICANTID>400030</APPLICANTID>
<MISCTYPE>APPLIED</MISCTYPE>
</Limit>
</LimitDetails>
</Data>
</Body>
</FIXML>
Related
I'm using a for-each in my XSLT template.
This is my example input XML:
<products>
<data>
<label_1>some_label1</label_1>
<label_2>some_label2</label_2>
<values>
<a>a</a>
<b>b</b>
</values>
</data>
<data>
<label_1>some_label1</label_1>
<label_2>some_label2</label_2>
<values>
<c>c</c>
<d>d</d>
</values>
</data>
</products>
Now based on my template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http:/example.com/ns">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<data>
<xsl:variable name="values" select="values" />
<xsl:for-each select="$values">
<xsl:apply-templates select="#*|node()" />
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>
I get only <values></values> and that is ok for me.
That's my output:
<products>
<data>
<a>a</a>
<b>b</b>
</data>
<data>
<c>c</c>
<d>d</d>
</data>
</products>
What i need in my output is namespace like this:
<products>
<data>
<ns:a>a</ns:a>
<ns:b>b</ns:b>
</data>
<data>
<ns:c>c</ns:c>
<ns:d>d</ns:d>
</data>
</products>
So what i understand is "each element of values is applied by template". How can I add namespace ?
You can get output similar to what you show (albeit well-formed) by using:
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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:apply-templates select="values/*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="values/*">
<xsl:element name="ns:{local-name()}" namespace="http:/example.com/ns">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Or, if you prefer:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http:/example.com/ns">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/products">
<products>
<xsl:for-each select="data">
<xsl:copy>
<xsl:for-each select="values/*">
<xsl:element name="ns:{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</products>
</xsl:template>
</xsl:stylesheet>
Replace http:/example.com/ns with your own namespace URI.
Credits
This answer follows the technique used in this SO answer to a similar problem.
Solution
Add namespace information to all descendants of specific elements. Augment the stylesheet by a template matching this set of nodes:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http://my.ns.uri"
>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<data>
<xsl:variable name="values" select="values" />
<xsl:for-each select="$values">
<xsl:apply-templates select="#*|node()" />
</xsl:for-each>
</data>
</xsl:template>
<!--
Added template.
-->
<xsl:template match="data//*">
<xsl:element name="ns:{name()}" namespace="http://my.ns.uri">
<xsl:for-each select=".">
<xsl:apply-templates select="#*|node()" />
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Need to transform following XML snippet into DITA using XSLT. My requirements are:
1. All the tags comes before "orderedlist" should be wrapped under "context" node.
2. All the tags comes after "orderedlist" should be in "result".
3. All the "include" tags should be wrapped under their preceding sibling nodes.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<procedure>
<para>This is first para</para>
<para>This is second para</para>
<include>This is include</include>
<orderedlist>
<listitem>this is list item</listitem>
<include>This is include</include>
<listitem>this is list item <include>this is include</include></listitem>
</orderedlist>
<observation>this is observation</observation>
<para>this is result para <include>this is include</include></para>
<include>This is include</include>
</procedure>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<task>
<context>
<p>This is first para</p>
<p>This is second para <included type='tag'>This is include</included>
</p>
</context>
<ol>
<li>this is list item <included type='tag'>This is include</included>
</li>
<li>this is list item <included type='tag'>this is include</included></li>
</ol>
<result>
<observation>this is observation</observation>
<p>this is result para <included type='tag'>this is include</included><included type='tag'>this is include</included>
</p>
</result>
</task>
My XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="procedure">
<task>
<context>
<xsl:apply-templates select="*[parent::procedure][following-sibling::orderedlist]"/>
</context>
<xsl:apply-templates select="orderedlist"/>
<result>
<xsl:apply-templates select="*[parent::procedure][preceding-sibling::orderedlist]"/>
</result>
</task>
</xsl:template>
<xsl:template match="para">
<p>
<xsl:apply-templates/>
<include>
<xsl:apply-templates select="following-sibling::include"/>
</include>
</p>
</xsl:template>
<!-- rest of the template goes here -->
<xsl:template match="listitem">
<li>
<xsl:apply-templates/>
<include>
<xsl:apply-templates select="following-sibling::include"/>
</include>
</li>
</xsl:template>
</xsl:stylesheet>
Any pointer will be a great help. Thanks.
The first thing to note is you can simplify the following expression:
<xsl:apply-templates select="*[parent::procedure][following-sibling::orderedlist]"/>
You don't need the [parent::procedure] here, because you are already positioned on a procedure element, so so you know if you select any child element, it will have that as a parent!
<xsl:apply-templates select="*[following-sibling::orderedlist]"/>
However, you might need to add an clause to ensure you don't output the include elements at this point, as you will need special code to handle them being included later
<xsl:template match="include" />
To handle the include elements, it might be worth defining a key, so you can group them by the first most proceding non-include element, like so
<xsl:key name="include" match="include" use="generate-id(preceding-sibling::*[not(self::include)][1])"/>
Then, when matching an element such as para or listitem, you can then get the include elements to include, just like this:
<xsl:copy-of select="key('include', generate-id())"/>
Note I am not sure how you want to handle multipe include elements for a single element, but in my example, it will output them separately as opposing to merging them:
Here is the full XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="include" match="include" use="generate-id(preceding-sibling::*[not(self::include)][1])"/>
<xsl:template match="procedure">
<task>
<context>
<xsl:apply-templates select="*[following-sibling::orderedlist]"/>
</context>
<xsl:apply-templates select="orderedlist"/>
<result>
<xsl:apply-templates select="*[preceding-sibling::orderedlist]"/>
</result>
</task>
</xsl:template>
<xsl:template match="orderedlist">
<ol>
<xsl:apply-templates />
</ol>
</xsl:template>
<xsl:template match="para">
<p>
<xsl:apply-templates/>
<xsl:copy-of select="key('include', generate-id())" />
</p>
</xsl:template>
<xsl:template match="listitem">
<li>
<xsl:apply-templates/>
<xsl:copy-of select="key('include', generate-id())" />
</li>
</xsl:template>
<xsl:template match="include" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<task>
<context>
<p>This is first para</p>
<p>This is second para<include>This is include</include></p>
</context>
<ol>
<li>this is list item<include>This is include</include></li>
<li>this is list item</li>
</ol>
<result>
<observation>this is observation</observation>
<p>this is result para<include>This is include</include></p>
</result>
</task>
Give this a try:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()" name="Copy">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
<xsl:call-template name="Include" />
</xsl:copy>
</xsl:template>
<xsl:template match="procedure">
<task>
<context>
<xsl:apply-templates select="*[following-sibling::orderedlist]"/>
</context>
<xsl:apply-templates select="orderedlist"/>
<result>
<xsl:apply-templates select="*[preceding-sibling::orderedlist]"/>
</result>
</task>
</xsl:template>
<xsl:template match="para">
<p>
<xsl:apply-templates/>
<xsl:call-template name="Include" />
</p>
</xsl:template>
<!-- rest of the template goes here -->
<xsl:template match="listitem">
<li>
<xsl:apply-templates/>
<xsl:call-template name="Include" />
</li>
</xsl:template>
<xsl:template name="Include">
<xsl:apply-templates
select="following-sibling::include[
generate-id(preceding-sibling::*[not(self::include)][1]) =
generate-id(current())]"
mode="performIncludes"/>
</xsl:template>
<xsl:template match="include" />
<xsl:template match="include" mode="performIncludes">
<xsl:call-template name="Copy" />
</xsl:template>
</xsl:stylesheet>
Output when run on your sample input:
<task>
<context>
<p>This is first para</p>
<p>This is second para<include>This is include</include></p>
</context>
<orderedlist>
<li>this is list item<include>This is include</include></li>
<li>this is list item</li>
</orderedlist>
<result>
<observation>this is observation</observation>
<p>this is result para<include>This is include</include></p>
</result>
</task>
I need to pass the XML to third party system which can be understand by third party and parse it.
Below is my input xml which I created data by fetching from database.
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
<RequestType>DocGen</RequestType>
<Version>10.6</Version>
<BankId>01</BankId>
<ChannelId>LOS</ChannelId>
</Header>
<Body>
<Data>
**<CorpAppLimitDetailsBO>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
</CorpAppLimitDetailsBO>
<CorpAppLimitDetailsBO>
<ApprovedLimitHomeCCY>0.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY/>
<ApprovedLimit>500.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</CorpAppLimitDetailsBO>
<CorpAppLimitDetailsBO>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</CorpAppLimitDetailsBO>
<CorpAppProductDetailsBO>
<ProductCategory>3</ProductCategory>
</CorpAppProductDetailsBO>
<CorpAppProductDetailsBO>
<ProductCategory>1</ProductCategory>
</CorpAppProductDetailsBO>
<CorpAppProductDetailsBO>
<ProductCategory>2</ProductCategory>
</CorpAppProductDetailsBO>**
<TemplateDetails>
<Template>tempid001</Template>
</TemplateDetails>
<SelectedClauses>
<Clauses>
<Clause>clause1</Clause>
</Clauses>
<Clauses>
<Clause>clause2</Clause>
</Clauses>
<Clauses>
<Clause>clause3</Clause>
</Clauses>
</SelectedClauses>
<Distribution>
<Email>email1#domain.com,email2#domain.com,email3#domain.com</Email>
<Print>blrkec3030,blrkec3031</Print>
</Distribution>
</Data>
</Body>
</FIXML>
I want to convert this input XML to another XML format using XSLT.
Below is the format which I need,
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
<RequestType>DocGen</RequestType>
<Version>10.6</Version>
<BankId>01</BankId>
<ChannelId>LOS</ChannelId>
</Header>
<Body>
<Data>
**<LimitDetails>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
</Limit>
<Limit>
<ApprovedLimitHomeCCY>0.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY/>
<ApprovedLimit>500.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</Limit>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</Limit>
</LimitDetails>
<ProductDetails>
<Product>
<ProductCategory>3</ProductCategory>
</Product>
<Product>
<ProductCategory>1</ProductCategory>
</Product>
<Product>
<ProductCategory>2</ProductCategory>
</Product>
</ProductDetails>**
<TemplateDetails>
<Template>tempid001</Template>
</TemplateDetails>
<SelectedClauses>
<Clauses>
<Clause>clause1</Clause>
</Clauses>
<Clauses>
<Clause>clause2</Clause>
</Clauses>
<Clauses>
<Clause>clause3</Clause>
</Clauses>
</SelectedClauses>
<Distribution>
<Email>email1#domain.com,email2#domain.com,email3#domain.com</Email>
<Print>blrkec3030,blrkec3031</Print>
</Distribution>
</Data>
</Body>
</FIXML>
Please help me out as I have to complete the task in another 2 days of time.
I tried with below code and i m getting below outputs but rest of the tags for e.g. <FIXML> , <TemplateDetails> and etc is not coming as part of output xml.
xsl code below:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" /> <!-- This identity template copies the document -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<!--
This template will only match the 'CorpAppLimitDetailsBO'
nodes and modify them the way you want.
-->
<xsl:template match="/*">
<xsl:element name="LimitDetails">
<xsl:for-each select="//CorpAppLimitDetailsBO">
<xsl:element name="Limit">
<xsl:for-each select="*">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:element>
<xsl:element name="ProductDetails">
<xsl:for-each select="//CorpAppProductDetailsBO">
<xsl:element name="Product">
<xsl:for-each select="*">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
output.xml below:
<?xml version="1.0" encoding="UTF-8"?>
<LimitDetails>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
</Limit>
<Limit>
<ApprovedLimitHomeCCY>0.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY/>
<ApprovedLimit>500.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</Limit>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</Limit>
</LimitDetails>
<ProductDetails>
<Product>
<ProductCategory>3</ProductCategory>
</Product>
<Product>
<ProductCategory>1</ProductCategory>
</Product>
<Product>
<ProductCategory>2</ProductCategory>
</Product>
</ProductDetails>
Note: The child tag(for e.g.ApprovedLimitHomeCCY....) which i present under BO's(for e.g. CorpAppLimitDetailsBO) tag are dynamic.I shouldnt hard code in xsl.I m new to XSLT. pls help me out.
Thanks Shil and Sean for your solution. both are perfect for my requirements. but i have one more doubt now.i m adding one more child tag <DBApplicantMiscDetails> under
<CorpAppProductDetailsBO>
Input xml:
<CorpAppProductDetailsBO>
<ProductCategory>2</ProductCategory>
<DBApplicantMiscDetails>
<APPLICANTMISCID>400000</APPLICANTMISCID>
<APPLICANTID>400030</APPLICANTID>
<MISCTYPE>APPLIED</MISCTYPE>
</DBApplicantMiscDetails>
</CorpAppProductDetailsBO>
Below is the output format which i expect.
<ProductDetails>
<Product>
<ProductCategory>2</ProductCategory>
<APPLICANTMISCID>400000</APPLICANTMISCID>
<APPLICANTID>400030</APPLICANTID>
<MISCTYPE>APPLIED</MISCTYPE>
</Product>
</ProductDetails>
Thanks again.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:key name="kDetails" match="*
[starts-with(name(),'CorpApp') and
substring(name(), string-length(name()) - 8) = 'DetailsBO']"
use="substring-before(name(),'DetailsBO')" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[*[key('kDetails',substring-before(name(),'DetailsBO'))]]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="*[generate-id() =
generate-id(key('kDetails',
substring-before(name(),'DetailsBO'))[1])]" mode="group" />
<xsl:apply-templates select="*[not(
key('kDetails',substring-before(name(),'DetailsBO')))]
|comment()|processing-instruction()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="group">
<xsl:variable name="group-name" select="substring-after(substring-before(name(),'DetailsBO'),'CorpApp')" />
<xsl:element name="{$group-name}Details">
<xsl:for-each select="key('kDetails',substring-before(name(),'DetailsBO'))">
<xsl:element name="{$group-name}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
<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="/">
<xsl:apply-templates select="FIXML"/>
</xsl:template>
<xsl:template match="FIXML">
<FIXML>
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Body"/>
</FIXML>
</xsl:template>
<xsl:template match="Header">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Body">
<Body>
<xsl:apply-templates select="Data"/>
</Body>
</xsl:template>
<xsl:template match="Data">
<Data>
<LimitDetails>
<xsl:apply-templates select="CorpAppLimitDetailsBO"/>
</LimitDetails>
<ProductDetails>
<xsl:apply-templates select="CorpAppProductDetailsBO"/>
</ProductDetails>
<xsl:apply-templates select="TemplateDetails"/>
<xsl:apply-templates select="SelectedClauses"/>
<xsl:apply-templates select="Distribution"/>
</Data>
</xsl:template>
<xsl:template match="CorpAppLimitDetailsBO">
<Limit>
<xsl:copy-of select="child::*"/>
</Limit>
</xsl:template>
<xsl:template match="CorpAppProductDetailsBO">
<xsl:apply-templates select="ProductCategory"/>
</xsl:template>
<xsl:template match="ProductCategory">
<Product>
<xsl:copy-of select="."/>
</Product>
</xsl:template>
<xsl:template match="TemplateDetails">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="SelectedClauses">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Distribution">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:apply-templates select="FIXML"/>
</xsl:template>
<xsl:template match="FIXML">
<FIXML>
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Body"/>
</FIXML>
</xsl:template>
<xsl:template match="Header">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Body">
<Body>
<xsl:apply-templates select="Data"/>
</Body>
</xsl:template>
<xsl:template match="Data">
<Data>
<LimitDetails>
<xsl:apply-templates select="CorpAppLimitDetailsBO"/>
</LimitDetails>
<ProductDetails>
<xsl:apply-templates select="CorpAppProductDetailsBO"/>
</ProductDetails>
<xsl:apply-templates select="TemplateDetails"/>
<xsl:apply-templates select="SelectedClauses"/>
<xsl:apply-templates select="Distribution"/>
</Data>
</xsl:template>
<xsl:template match="CorpAppLimitDetailsBO">
<Limit>
<xsl:copy-of select="child::*"/>
</Limit>
</xsl:template>
<xsl:template match="CorpAppProductDetailsBO">
<Product>
<xsl:apply-templates select="ProductCategory"/>
<xsl:apply-templates select="DBApplicantMiscDetails"/>
</Product>
</xsl:template>
<xsl:template match="ProductCategory">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="DBApplicantMiscDetails">
<xsl:copy-of select="child::*"/>
</xsl:template>
<xsl:template match="TemplateDetails">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="SelectedClauses">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Distribution">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
I need to transform my xml to another xml based on parent and child relationship.
Below is my source xml
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
</Header>
<Body>
<Data>
<LimitDetails>
<LimitRefNo>L1</LimitRefNo>
<LimitClassification>ROOT</LimitClassification>
<ParentLimitRefNo></ParentLimitRefNo>
<ApprovedLimit>100.0</ApprovedLimit>
</LimitDetails>
<LimitDetails>
<LimitRefNo>L2</LimitRefNo>
<LimitClassification>ClASSIFICATION1</LimitClassification>
<ParentLimitRefNo>L1</ParentLimitRefNo>
<ApprovedLimit>200.0</ApprovedLimit>
</LimitDetails>
<LimitDetails>
<LimitRefNo>L3</LimitRefNo>
<LimitClassification>CLASSIFICATION2</LimitClassification>
<ParentLimitRefNo>L2</ParentLimitRefNo>
<ApprovedLimit>300.0</ApprovedLimit>
</LimitDetails>
<LimitDetails>
<LimitRefNo>L4</LimitRefNo>
<LimitClassification>CLASSIFICATION3</LimitClassification>
<ParentLimitRefNo>L3</ParentLimitRefNo>
<ApprovedLimit>400.0</ApprovedLimit>
</LimitDetails>
</Data>
</Body>
</FIXML>
Here,Child limits refers Parent limits based on ParentLimitRefNo. Parent limit is the one which has ParentLimitRefNo as empty.
Below is the xml which i need to produce based on source xml.
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
</Header>
<Body>
<Data>
<LimitDetails>
<Limit>
<LimitRefNo>L1</LimitRefNo>
<LimitClassification>ROOT</LimitClassification>
<ParentLimitRefNo></ParentLimitRefNo>
<ApprovedLimit>100.0</ApprovedLimit>
<SubLimit>
<LimitRefNo>L2</LimitRefNo>
<LimitClassification>ClASSIFICATION1</LimitClassification>
<ParentLimitRefNo>L1</ParentLimitRefNo>
<ApprovedLimit>200.0</ApprovedLimit>
<SubLimit>
<LimitRefNo>L3</LimitRefNo>
<LimitClassification>CLASSIFICATION2</LimitClassification>
<ParentLimitRefNo>L2</ParentLimitRefNo>
<ApprovedLimit>300.0</ApprovedLimit>
<SubLimit>
<LimitRefNo>L4</LimitRefNo>
<LimitClassification>CLASSIFICATION3</LimitClassification>
<ParentLimitRefNo>L3</ParentLimitRefNo>
<ApprovedLimit>400.0</ApprovedLimit>
</SubLimit>
</SubLimit>
</SubLimit>
</Limit>
</LimitDetails>
</Data>
</Body>
Thanks in advance.
This XSLT 2.0 transformation (easy to convert to XSLT 1.0):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kLD" match="LimitDetails" use="ParentLimitRefNo"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<Data>
<LimitDetails>
<xsl:apply-templates
select="LimitDetails[not(ParentLimitRefNo/node())]"/>
</LimitDetails>
</Data>
</xsl:template>
<xsl:template match="LimitDetails">
<xsl:variable name="vSuf" select=
"if(ParentLimitRefNo/text())
then 'Sub'
else ()
"/>
<xsl:element name="{$vSuf}Limit">
<xsl:apply-templates select="node()|key('kLD', LimitRefNo)"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
</Header>
<Body>
<Data>
<LimitDetails>
<LimitRefNo>L1</LimitRefNo>
<LimitClassification>ROOT</LimitClassification>
<ParentLimitRefNo></ParentLimitRefNo>
<ApprovedLimit>100.0</ApprovedLimit>
</LimitDetails>
<LimitDetails>
<LimitRefNo>L2</LimitRefNo>
<LimitClassification>ClASSIFICATION1</LimitClassification>
<ParentLimitRefNo>L1</ParentLimitRefNo>
<ApprovedLimit>200.0</ApprovedLimit>
</LimitDetails>
<LimitDetails>
<LimitRefNo>L3</LimitRefNo>
<LimitClassification>CLASSIFICATION2</LimitClassification>
<ParentLimitRefNo>L2</ParentLimitRefNo>
<ApprovedLimit>300.0</ApprovedLimit>
</LimitDetails>
<LimitDetails>
<LimitRefNo>L4</LimitRefNo>
<LimitClassification>CLASSIFICATION3</LimitClassification>
<ParentLimitRefNo>L3</ParentLimitRefNo>
<ApprovedLimit>400.0</ApprovedLimit>
</LimitDetails>
</Data>
</Body>
</FIXML>
produces the wanted, correct result:
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
</Header>
<Body>
<Data>
<LimitDetails>
<Limit>
<LimitRefNo>L1</LimitRefNo>
<LimitClassification>ROOT</LimitClassification>
<ParentLimitRefNo/>
<ApprovedLimit>100.0</ApprovedLimit>
<SubLimit>
<LimitRefNo>L2</LimitRefNo>
<LimitClassification>ClASSIFICATION1</LimitClassification>
<ParentLimitRefNo>L1</ParentLimitRefNo>
<ApprovedLimit>200.0</ApprovedLimit>
<SubLimit>
<LimitRefNo>L3</LimitRefNo>
<LimitClassification>CLASSIFICATION2</LimitClassification>
<ParentLimitRefNo>L2</ParentLimitRefNo>
<ApprovedLimit>300.0</ApprovedLimit>
<SubLimit>
<LimitRefNo>L4</LimitRefNo>
<LimitClassification>CLASSIFICATION3</LimitClassification>
<ParentLimitRefNo>L3</ParentLimitRefNo>
<ApprovedLimit>400.0</ApprovedLimit>
</SubLimit>
</SubLimit>
</SubLimit>
</Limit>
</LimitDetails>
</Data>
</Body>
</FIXML>
Explanation:
Using and modifying the identity rule.
Using a key to specify all logical children of a LimitDetails from its LimitRefNo.
II. XSLT 1.0 solution:
This is an almost mechanical translation of the above transformation into XSLT 1.0 -- only the definition of the variable $vSuf is different:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kLD" match="LimitDetails" use="ParentLimitRefNo"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<Data>
<LimitDetails>
<xsl:apply-templates
select="LimitDetails[not(ParentLimitRefNo/node())]"/>
</LimitDetails>
</Data>
</xsl:template>
<xsl:template match="LimitDetails">
<xsl:variable name="vSuf" select=
"concat('',
substring('Sub',1 div boolean(ParentLimitRefNo/text()))
)"/>
<xsl:element name="{$vSuf}Limit">
<xsl:apply-templates select="node()|key('kLD', LimitRefNo)"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When applied to the same XML document (above), the same correct result is produced.
The use of keys surely is more elegant (see Dimitre Novatchevs solution), but this one takes the wanted changes in the structure into account (e.g. rename <LimitDetails /> to <SubLimit /> and placing the <Limit /> tag.):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="Data">
<xsl:copy>
<LimitDetails>
<Limit>
<xsl:apply-templates select=".//LimitDetails[./ParentLimitRefNo='']" />
</Limit>
</LimitDetails>
</xsl:copy>
</xsl:template>
<xsl:template match="LimitDetails">
<xsl:variable name="LimitRefNo" select="./LimitRefNo" />
<xsl:apply-templates select="#*|node()" />
<xsl:if test="../LimitDetails[./ParentLimitRefNo = $LimitRefNo]">
<SubLimit>
<xsl:apply-templates select="../LimitDetails[./ParentLimitRefNo = $LimitRefNo]" />
</SubLimit>
</xsl:if>
</xsl:template>
</xsl:transform>
Or as a modification of Dimitre's solution using keys:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kLD" match="LimitDetails" use="ParentLimitRefNo"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<Data>
<LimitDetails>
<Limit>
<xsl:apply-templates select="LimitDetails[not(ParentLimitRefNo/node())]"/>
</Limit>
</LimitDetails>
</Data>
</xsl:template>
<xsl:template match="LimitDetails">
<xsl:apply-templates />
<xsl:if test="key('kLD', LimitRefNo)">
<SubLimit>
<xsl:apply-templates select="key('kLD', LimitRefNo)"/>
</SubLimit>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
How do I only add a namespace to the root element?
My XML:
<Envelope>
<from>
<contents />
</from>
</Envelope>
My desired output:
<Envelope xmlns:tns="Foo">
<from>
<contents />
</from>
</Envelope>
I can only get "xmlns='Foo'" using this, not "xmlns:tns=..":
<xsl:element name="{local-name()}" namespace="Foo" >
<xsl:copy-of select="attribute::*"/>
<xsl:apply-templates />
</xsl:element>
Here is a complete transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tns="Foo">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="{name()}">
<xsl:copy-of select=
"document('')/*/namespace::*[name()='tns']"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Envelope>
<from>
<contents />
</from>
</Envelope>
the wanted, correct result is produced:
<Envelope xmlns:tns="Foo">
<from>
<contents/>
</from>
</tns:Envelope>