Trying to move immediate child elements from root elements and then copying them under a new root element. Able to do it one child element but not sure how to apply or with in select statement.
Current XSLT for recursive child element:
<xsl:template match="Element1">
<Information>
<xsl:apply-templates select="*[name()!='Element1']"/>
</Information>
<xsl:apply-templates select="Element1"/>
</xsl:template>
<xsl:template match="Element1/Element1">
<Metadata>
<xsl:apply-templates/>
</Metadata>
</xsl:template>
I want to apply same for another child element and tried
<xsl:template match="Element1">
<Information>
<xsl:apply-templates select="*[name()!='Element1'] | *[name()!='Element2']"/>
</Information>
<xsl:apply-templates select="Element1"/>
</xsl:template>
<xsl:template match="Element1/Element1">
<Metadata>
<xsl:apply-templates/>
</Metadata>
</xsl:template>
<xsl:template match="Element1/Element2">
<Metadata2>
<xsl:apply-templates/>
</Metadata2>
</xsl:template>
But it didn't work. Please help.
You might want to try placing these templates into a group and use an empty template to remove those that you do not want. Add a mode to the apply-templates
<xsl:apply-templates select="*" mode="metadata"/>
to activate the group and then define templates in the group
<xsl:template match="*" mode="metadata"/>
<xsl:template match="Element1" mode="metadata">
...
<xsl:template>
<xsl:template match="Element2" mode="metadata">
...
<xsl:template>
for the metadata blocks.
Using a mode, your template would look something like this:
<xsl:template match="Element1">
<Information>
<!-- use templates in the information group -->
<xsl:apply-templates select="*" mode="metadata"/>
</Information>
<xsl:apply-templates select="Element1"/>
</xsl:template>
<!-- This template removes unspecified elements -->
<xsl:template match="*" mode="metadata"/>
<xsl:template match="Element1" mode="metadata">
<Metadata>
<xsl:apply-templates/>
</Metadata>
</xsl:template>
<xsl:template match="Element2" mode="metadata">
<Metadata2>
<xsl:apply-templates/>
</Metadata2>
</xsl:template>
You probably have some other templates defining the content of <Metadata/> and <Metadata2/>. Those should probably be in the mode as well.
<xsl:apply-templates select="*[name()!='Element1'] | *[name()!='Element2']"/>
... is always true for Element1 and Element2 - you can't chain two != expressions together like that because the expression will evaluate to true for everything.
You can simply match the Element1/Element1 and Element1/Element2 paths with templates that output nothing - then you don't have to have complex logic in the apply-templates expression inside .
Then call a named template (rather than using a match) from your root to output the Element1/Element1 where you want it to go.
Related
I have a number of Java classes that I am using in conjunction with JAXB in order to generate XML. The java classes have minimal changes from year to year but the output XML needs to have very specific yearly changes to it and it's proving a little elusive. I've tried updating the attributes using DOM but nodes further along the tree are maintaining the previous state. I've tried using reflection to update the annotations directly before marshalling but it doesn't seem to be having the desired effect. I've also tried replacing the XMLRootElement object (and XMLType, XMLElement) with local classes but nothing seems to be working properly as some information always seems to be retained somewhere even when it seems that I have changed the member/attribute/etc.
I am not going to duplicate all the java objects on a yearly basis just so that I can change the namespaces to match the requirements.
Right now I'm at the point where I think XSLT might be the last option but I have little to no knowledge of it. Is there a simple way to update 5-8 namespace URI's that are located on the root element? I don't want to change the prefixes (they are already set using a prefix mapper), I just want to change the namespace from "com.help.me.2014" to "com.help.me.2015".
Thanks
Andy
Resolution:
First off I greatly appreciate the effort and responses. I didn't actually try any of them as I came up with a different solution prior to getting back to see them.
Anyone coming along in the future can try the items listed below (as an XSLT solution) or you can try what I describe below.
I am generating XML in two different styles/formats, one with and one without SOAP wrappers. Due to my difficulty accessing the actual namespaces within the DOM/SOAP objects and my inability to alter the annotations at runtime I ended up capturing the output stream and manipulating the resulting string.
SOAP:
ByteArrayOutputStream stream = new ByteArrayOutputStream();
soapMessage.writeTo(stream);
String file = new String(stream.toByteArray);
... manipulate file (now a string), replace values, etc. -> actually passed to dependency injected converters, then send on to client via response.write
JAXB Marshalling is very similar to the SOAP, both send the resulting String onto converters which manipulate it as a StringBuilder then send it on.
Thanks again for the suggestions. Hopefully it helps someone in the future although the requirement is a little out there.
Andy
Changing namespaces every year is almost certainly the wrong thing to do, but the following XSLT stylesheet will change namespaces
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:old="oldspace"
version="1.0">
<xsl:template match="old:*">
<xsl:element name="{local-name(.)}" namespace="newspace">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#old:*">
<xsl:attribute name="{local-name()}" namespace="newspace">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="*">
<xsl:copy select=".">
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="processing-instruction()|comment()">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
This style sheet creates a copy of every element changing the namespace from oldspace to newspace when appropriate. Other than the namespace change, the original document is preserved. Similar templates can be made for every namespace that needs to be changed (note there are two templates that are namespace specific).
Note that prefixes WILL be altered. These are not really content as such, so it is nearly impossible to preserve them in a case like this. The only way I can think of to preserve those would involve writing a separate template for each element in the original, directly creating the new elements instead of using the xsl:element element.
For example, the given xml
<os:myroot xmlns:os="oldspace">
<?keep-this?>
<os:testing abc='3' def='9'>
<!-- This is a child -->
<os:item>1234</os:item>
</os:testing>
<!-- this element is in the default namespace -->
<testing2>
<abc>112233</abc>
</testing2>
</os:myroot>
is transformed to
<myroot xmlns="newspace">
<?keep-this?>
<testing>
<!-- This is a child -->
<item>1234</item>
</testing>
<!-- this element is in the default namespace -->
<testing2 xmlns="">
<abc>112233</abc>
</testing2>
</myroot>
where all elements that were in the oldspace namespace are now in the newspace namespace.
Here's an option that allows you to pass the old and new namespace URIs in as xsl:params.
XML Input (Borrowed from Matthew's answer; thanks!)
<os:myroot xmlns:os="com.help.me.2014">
<?keep-this?>
<os:testing abc='3' def='9'>
<!-- This is a child -->
<os:item>1234</os:item>
</os:testing>
<!-- this element is in the default namespace -->
<testing2>
<abc>112233</abc>
</testing2>
</os:myroot>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="oldns" select="'com.help.me.2014'"/>
<xsl:param name="newns" select="'com.help.me.2015'"/>
<xsl:template match="#*|node()" name="ident">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" priority="1">
<xsl:choose>
<xsl:when test="namespace-uri()=$oldns">
<xsl:element name="{name()}" namespace="{$newns}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
XML Output
<os:myroot xmlns:os="com.help.me.2015"><?keep-this?>
<os:testing abc="3" def="9"><!-- This is a child -->
<os:item>1234</os:item>
</os:testing><!-- this element is in the default namespace -->
<testing2>
<abc>112233</abc>
</testing2>
</os:myroot>
Here's an XSLT 2.0 option that produces the same output...
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="oldns" select="'com.help.me.2014'"/>
<xsl:param name="newns" select="'com.help.me.2015'"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" priority="1">
<xsl:element name="{name()}" namespace="{
if (namespace-uri()=$oldns) then $newns else namespace-uri()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Here's another 2.0 example that handles multiple namespace uris. The old and new uris are passed in as a string with commas as delimiters.
The order of the uris are important. The first old uri corresponds to the first new uri. The second old uri corresponds to the second new uri. Etc.
XML Input (updated to have more than one namespace uri)
<os:myroot xmlns:os="com.help.me.2014">
<?keep-this?>
<os:testing abc='3' def='9'>
<!-- This is a child -->
<os:item>1234</os:item>
</os:testing>
<!-- this element is in the default namespace -->
<testing2>
<abc>112233</abc>
</testing2>
<os2:testing xmlns:os2="com.help.me.again.2014">
<os2:item>ABCD</os2:item>
</os2:testing>
</os:myroot>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="oldns" select="'com.help.me.2014,com.help.me.again.2014'"/>
<xsl:param name="newns" select="'com.help.me.2015,com.help.me.again.2015'"/>
<xsl:variable name="oldns-seq" select="tokenize($oldns,',')"/>
<xsl:variable name="newns-seq" select="tokenize($newns,',')"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" priority="1">
<xsl:variable name="nsIdx" select="index-of($oldns-seq,namespace-uri())"/>
<xsl:element name="{name()}" namespace="{
if (namespace-uri()=$oldns-seq) then $newns-seq[$nsIdx] else namespace-uri()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
XML Output
<os:myroot xmlns:os="com.help.me.2015"><?keep-this?>
<os:testing abc="3" def="9"><!-- This is a child -->
<os:item>1234</os:item>
</os:testing>
<!-- this element is in the default namespace -->
<testing2>
<abc>112233</abc>
</testing2>
<os2:testing xmlns:os2="com.help.me.again.2015">
<os2:item>ABCD</os2:item>
</os2:testing>
</os:myroot>
My requirement is as follows
If any complex element contains only text then corresponding element attributes translate into element to parent level.
If any complex element contains children then corresponding element attributes translate into element to same element level.
This translation want to achieve using xslt logic.
Input XML
<root>
<food name="desert">butter scotch</food>
<special type="nonveg">
<name>chicken</name>
</special>
</root>
Output XML
<root>
<food>butter scotch</food>
<name>desert</name>
<special>
<type>nonveg</type>
<name>chicken</name>
</special>
</root>
Start off with the Identity Transform....
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
For the rule "If any complex element contains only text then corresponding element attributes translate into element to parent level", you could use the following template (I am ignoring comments and processing-instuctions here, and just checking the element has no child element)
<xsl:template match="*[not(*)][#*]">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:apply-templates select="#*" mode="toelement"/>
</xsl:template>
The template with mode "toelement" will turn the attribute to an element. (It will be re-used by the other rule).
<xsl:template match="#*" mode="toelement">
<xsl:element name="{local-name()}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
For the rule "If any complex element contains children then corresponding element attributes translate into element to same element level." then you could actually match on the attribute directly:
<xsl:template match="*[*]/#*">
<xsl:apply-templates select="." mode="toelement"/>
</xsl:template>
Try this XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*[*]/#*">
<xsl:apply-templates select="." mode="toelement"/>
</xsl:template>
<xsl:template match="*[not(*)][#*]">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:apply-templates select="#*" mode="toelement"/>
</xsl:template>
<xsl:template match="#*" mode="toelement">
<xsl:element name="{local-name()}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Receiving a recursive xml a legacy application like this
<ResponseXml>
<AccountData>
<AccountInformation>
<AccountNumber>123465</AccountNumber>
<BankCode>456</BankCode>
<OwnerInformation>
<FirstName>Himanshu</FirstName>
<LastName>Yadav</LastName>
</OwnerInformation>
<AccountInformation>
<AccountNumber>78910</AccountNumber>
<BankCode>123</BankCode>
<OwnerInformation>
<FirstName>My</FirstName>
<LastName>Wife</LastName>
</OwnerInformation>
</AccountInformation>
</AccountInformation>
</AccountData>
</ResponseXml>
It has to be formatted into:
<BillingInformation>
<AccountNumber>123465</AccountNumber>
<BankCode>456</BankCode>
</BillingInformation>
<ClientInfo>
<FirstName>Himanshu</FirstName>
<LastName>Yadav</LastName>
</ClientInfo>
<BillingInformation2>
<AccountNumber>78910</AccountNumber>
<BankCode>123</BankCode>
</BillingInformation2>
<ClientInfo>
<FirstName>My</FirstName>
<LastName>Wife</LastName>
</ClientInfo>
Being new to XSLT transformation I am getting into multiple issues:
Excluding Child Elements while copying parent values.
And then copying excluded child elements under new root elements.
Tried so far.
Partial solution for the recursive part. It does not excludes root elements <ResponseXml><AccountData>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="AccountInformation">
<BillingInformation>
<xsl:apply-templates select="*[name()!='AccountInformation']"/>
</BillingInformation>
<xsl:apply-templates select="AccountInformation"/>
</xsl:template>
<xsl:template match="AccountInformation/AccountInformation">
<BillingInformation2>
<xsl:apply-templates/>
</BillingInformation2>
</xsl:template>
Since you are using the identity template then you have to override that template for any element that you want to act on. In this case if all you need is to remove the ResponseXml and AccountData elements then you just create an empty template for them both.
<xsl:template match="ResponseXml | AccountData">
<xsl:apply-templates/>
</xsl:template>
With that above line added to your XSL then it will not output those two elements.
A question about Martin's answer:
Martin Honnen's answer works great, but not with the root element. Let's say I have "cars" as a root element:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="foo">
<cars>
<car1 />
<car2 />
</cars>
</xsl:template>
</xsl:stylesheet>
And I want to obtain:
<xsl:template match="foo">
<cars>
<car1 />
<car2 />
<TANK />
</cars>
</xsl:template>
For this, I'd use:
<xsl:template match="cars" >
<xsl:copy>
<xsl:apply-templates/>
<TANK />
</xsl:copy>
</xsl:template>
Which outputs the exact input, without changing anything. I can try:
<xsl:template match="/" >
<xsl:copy>
<xsl:apply-templates/>
<TANK />
</xsl:copy>
</xsl:template>
But it will place the TANK node outside the stylesheet, like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="order">
<cars>
<car1/>
<car2/>
</cars>
</xsl:template>
</xsl:stylesheet><TANK/>
How to get the TANK element inside cars?
Original question:
I have a XSL that I use to transform an XML:
XML_format1 -> XSL1 -> XML_format2
I need to transform this first XSL file (using a second XSL) to obtain a third XSL file, which will output an XML with a third format. In short:
XSL1 -> XSL2 -> XSL3
XML_format1 -> XSL3 -> XML_format3
Using the following stylesheet, I am able to copy the first XSL's contents and also skip certain nodes:
<xsl:stylesheet 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="//skipThisNode"/>
</xsl:stylesheet>
My problem: in addition to this, I also need to change some nodes' structure (add something), from this:
<car>
<color>green</color>
<fuel>petrol</fuel>
</car>
To this:
<car>
<color>green</color>
<fuel>petrol</fuel>
<topSpeed>99</topSpeed>
</car>
LE: I could create a template to match the specific nodes that I need to add children to, like so:
<xsl:template match="car">
<color>
<!-- existing value-of -->
</color>
<fuel>
<!-- existing value-of -->
</fuel>
<topSpeed>
<!-- * new value-of * -->
</topSpeed>
</xsl:template>
But this seems like going over the top. Is there a simpler way of achieving what I want?
I would rather use
<xsl:param name="newSpeed" select="99"/>
<xsl:template match="car">
<xsl:copy>
<xsl:apply-templates/>
<topSpeed>
<xsl:value-of select="$newSpeed"/>
</topSpeed>
</xsl:copy>
</xsl:template>
that keeps the processing chain up, allowing you to add templates to transform or delete child and descendants of car elements when needed.
[edit]
I am not sure I understand your latest requirement as the input seems to be a complete stylesheet but then as the wanted output you have only shown a single template. So assuming you have the input as
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="order">
<cars>
<car1 />
<car2 />
</cars>
</xsl:template>
</xsl:stylesheet>
and you want to both transform the xsl:template's match attribute as well as the literal result elements in the template body I would use
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xsl:template[#match = 'order']/#match">
<xsl:attribute name="{name()}">
<xsl:text>foo</xsl:text>
</xsl:attribute>
</xsl:template>
<xsl:template match="xsl:template[#match = 'order']/cars">
<xsl:copy>
<xsl:apply-templates/>
<TANK/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
that way I get (tested with Saxon 6.5.5) the result
<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="foo">
<cars>
<car1/>
<car2/>
<TANK/></cars>
</xsl:template>
</xsl:stylesheet>
which is hopefully what you want (lacking proper indentation perhaps).
I'm working on an XSLT that is giving me a little headache, and was looking for some tips. I'm working on converting an XML where some of the tags have namespaces prefixes, and others do not. I am working to convert all of the tags to one common namespace prefix.
Example of XML:
<yes:Books>
<no:Book>
<Title>Yes</Title>
<maybe:Version>1</maybe:Version>
</no:Book>
</yes:Books>
What I'm trying to get:
<yes:Books>
<yes:Book>
<yes:Title>Yes</yes:Title>
<yes:Version>1</yes:Version>
</yes:Book>
</yes:Books>
The XML input is the aggregate of several webservices, that are returning various namespaces. I have no issue aggregating it together appropriately, it's creating one common prefix namespace that I am having an issue with.
Worst case, I could regex them away, but I'm sure that isn't recomended.
Thanks.
This transformation allows the wanted final prefix and its namespace to be specified as external/global parameters. It shows how to process in the same way attribute names:
<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:param name="pPrefix" select="'yes'"/>
<xsl:param name="pNamespace" select="'yes'"/>
<xsl:template match="*">
<xsl:element name="{$pPrefix}:{local-name()}" namespace="{$pNamespace}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{$pPrefix}:{local-name()}" namespace="{$pNamespace}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
when applied on the following document (the provided one with one added attribute to make the problem more challenging):
<yes:Books xmlns:yes="yes">
<no:Book xmlns:no="no">
<Title no:Major="true">Yes</Title>
<maybe:Version xmlns:maybe="maybe">1</maybe:Version>
</no:Book>
</yes:Books>
produces the wanted, correct result:
<yes:Books xmlns:yes="yes">
<yes:Book>
<yes:Title yes:Major="true">Yes</yes:Title>
<yes:Version>1</yes:Version>
</yes:Book>
</yes:Books>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:custom="c">
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="custom:{local-name()}" namespace-uri="c">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>