I have an XML:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:p1="http://www.w3.org/2001/XMLSchema-instance">
<Body>
<reinstateAccountRequest xmlns="http://abc.xyx/">
<serviceRequestContext>
<a>t</a>
<b>t</b>
</serviceRequestContext>
<reinstateAccountInput>
<a>t</a>
<b>t</b>
</reinstateAccountInput>
</reinstateAccountRequest>
</Body>
</Envelope>
I want to add empty xmlns to serviceRequestContext and reinstateAccountInput node
Result XML should look like below:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:p1="http://www.w3.org/2001/XMLSchema-instance">
<Body>
<reinstateAccountRequest xmlns="http://abc.xyx/">
<serviceRequestContext xmlns="">
<a>t</a>
<b>t</b>
</serviceRequestContext>
<reinstateAccountInput xmlns="">
<a>t</a>
<b>t</b>
</reinstateAccountInput>
</reinstateAccountRequest>
</Body>
</Envelope>
How to write an XSLT for this
You can start off by building upon the XSLT identity template, to copy across any existing nodes
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
With this in place, you only then have to write templates where you want to make changes to the nodes. In your case, you want to change the child elements of the reinstateAccountRequest element, and the change you need to make is to create new elements with the same name, but no namespace.
<xsl:template match="abc:reinstateAccountRequest//*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
Where "abc" is a namespace prefix that would be defined to have the same namespace URI as in your input XML.
Here is the full XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://abc.xyx/">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:template match="abc:reinstateAccountRequest//*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Related
I've fairly recently started learning XSLT after getting to grips with XML and XPath; I'm trying to complete a practice exercise; I think I'm nearly there but I've ran into a problem. So I have the following XML document:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>
<b></b>
<b></b>
<b></b>
<b></b>
</a>
</root>
And I'd like to surround the elements with a pair of parent elements (to output the following):
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>
<b-group>
<b></b>
<b></b>
<b></b>
<b></b>
<b-group>
</a>
Here is my XSLT:
<?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="2.0">
<xsl:template match="root">
<xsl:element name="root">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="a">
<xsl:element name="a">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="b">
<xsl:element name="b-group">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="b">
<xsl:element name="b">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Despite making several other attempts, I'm having difficulty creating the pair of elements that surround the elements; could anyone point me in the right direction of how to do this please?
You need to do this in the template matching a, for example:
<xsl:template match="a">
<a>
<b-group>
<xsl:apply-templates select="b"/>
</b-group>
</a>
</xsl:template>
Additional notes:
Use a literal result element to create an element whose name is known, instead of xsl:element.
Most of your templates do the same thing: create an element with the same name as the one being matched, and apply templates to its children. Thus they could be consolidated into one. A template like this is known as the identity transform template and it is commonly used when most of the document needs to preserved as is, with only a few modifications. This would reduce your entire stylesheet to just:
<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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a">
<a>
<b-group>
<xsl:apply-templates select="b"/>
</b-group>
</a>
</xsl:template>
</xsl:stylesheet>
I have the following XML:
<myreport>
<media>
<assets>
<asset>
<type>image</type>
<H>224 mm</H>
<W>154 mm</W>
</asset>
<asset>
<type>video</type>
<H>480 px</H>
<W>600 px</W>
</asset>
</assets>
</myreport>
I need to restructure as follows:
<myreport>
<media>
<assets>
<image>
<H>224 mm</H>
<W>154 mm</W>
</image>
<video>
<H>480 px</H>
<W>600 px</W>
<video>
</assets>
</media>
</myreport>
How do I match type with height (H) width (W) to come out with the desire transformation. I used xsl:value-of select="node" for normal restructuring.
Start with the identity transformation, which copies nodes as they appear in the input XML:
<?xml version="1.0" encoding="UTF-8"?>
<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:stylesheet>
Add a special case for asset elements:
<xsl:template match="asset">
<xsl:element name="{type}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
Note that name={type} will name an outputed element per the value of the child type element.
Suppress type elements:
<xsl:template match="type"/>
Clarify output format:
<xsl:output method="xml" indent="yes"/>
Altogether:
<?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="asset">
<xsl:element name="{type}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="type"/>
</xsl:stylesheet>
This can be accomplished fairly easily with a modified identity transform. A template to match asset elements and instead of copying the asset element, use the value of it's type element as the name of the element to create, then apply-templates to the rest of it's children. An (empty) template that will suppress the type elements and any whitespace-only text() nodes.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--instead of an asset element, create an element named after it's type-->
<xsl:template match="asset[type]">
<xsl:element name="{type}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<!--suppress the type element and whitespace-only text nodes -->
<xsl:template match="asset/type | text()[not(normalize-space())]"/>
</xsl:stylesheet>
I am new to XSLT and i am trying to accomplish the follow case
I have an xml in the following format
<A>
<B>..</B>
<C>..</C>
..
<Z>..</Z>
</A>
I am trying to add a new node soon after so that the final xml will get transformed to
<A>
<aa>
<B>..</B>
<C>..</C>
..
<X>...</X>
</aa>
</A>
In order to achieve this i wrote the following xslt code
<?xml version="1.0" encoding="UTF-8"?>
<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="B">
<aa>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</aa>
</xsl:template>
By using this i get the following output
<A>
<aa>
<B>..</B>
</aa>
<C>..</C>
..
<X>..</X>
</A>
I am not sure what kind of changes i need to make to the xslt to achieve the desired output
If you want to wrap all the children of A in a single aa then you need to do that in a template matching A, not B.
<?xml version="1.0" encoding="UTF-8"?>
<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="A">
<xsl:copy><!-- copy the A -->
<xsl:apply-templates select="#*" /><!-- attributes, if any -->
<aa><!-- insert the extra aa -->
<xsl:apply-templates /><!-- process children -->
</aa>
</xsl:copy>
</xsl:template>
<!-- this may be a typo in the question, but for reference, here's how
to rename Z to X. If you don't need to do this, just leave this template
out and let the identity template at the top handle it. -->
<xsl:template match="Z">
<X>
<xsl:apply-templates select="#*|node()"/>
</X>
</xsl:template>
</xsl:stylesheet>
I have a source XML
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body>
<PublishANZINCIDENTESB xmlns="http://www.ibm.com/maximo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creationDateTime="2013-02-25T23:25:35+00:00" transLanguage="EN" baseLanguage="EN" messageID="1361834735434709840" maximoVersion="7 1 20110105-1024 V7118-37" event="1"><ANZINCIDENTESBSet><INCIDENT action="Replace"></INCIDENT></ANZINCIDENTESBSet></PublishANZINCIDENTESB></soapenv:Body></soapenv:Envelope>
I would like to change to target XML by adding namespace and header to the tag. The output XML is
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s600="http://LIB_ISM_WPS/S600_PublishANZINCIDENT_Service_INT" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Header/><soapenv:Body>
<s600:PublishANZINCIDENTESBOperation><PublishANZINCIDENTESB xmlns="http://www.ibm.com/maximo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creationDateTime="2013-02-25T23:25:35+00:00" transLanguage="EN" baseLanguage="EN" messageID="1361834735434709840" maximoVersion="7 1 20110105-1024 V7118-37" event="1"><ANZINCIDENTESBSet><INCIDENT action="Replace"></INCIDENT></ANZINCIDENTESBSet></PublishANZINCIDENTESB></s600:PublishANZINCIDENTESBOperation></soapenv:Body></soapenv:Envelope>
XSL I am using:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s600="http://LIB_ISM_WPS/S600_PublishANZINCIDENT_Service_INT" >
<s600:PublishANZINCIDENTESBOperation>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</s600:PublishANZINCIDENTESBOperation>
<xsl:template match="*">
<xsl:element name="s600:{name()}" namespace="http://LIB_ISM_WPS/S600_PublishANZINCIDENT_Service_INT">
<xsl:copy-of select="namespace::*"/>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Can anyone please help me as soon as possible as it is getting my head around?
From your input and output examples it looks like you simply want to wrap an s600:PublishANZINCIDENTESBOperation element around the content of the soapenv:Body but otherwise leave the content unchanged. You can achieve this with
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s600="http://LIB_ISM_WPS/S600_PublishANZINCIDENT_Service_INT"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="soapenv:Body">
<xsl:copy>
<s600:PublishANZINCIDENTESBOperation>
<xsl:apply-templates select="#*|node()" />
</s600:PublishANZINCIDENTESBOperation>
</xsl:copy>
</xsl:template>
<!-- If the namespace declaration absolutely _must_ be on the Envelope
you could uncomment this extra template -->
<!--
<xsl:template match="soapenv:Envelope">
<soapenv:Envelope>
<xsl:apply-templates select="#*|node()" />
</soapenv:Envelope>
</xsl:template>
-->
</xsl:stylesheet>
This will produce the output you're looking for, except that the xmlns:s600 declaration will be on the s600:PublishANZINCIDENTESBOperation element rather than on the soapenv:Envelope, but this shouldn't matter to downstream components provided they are using a proper XML parser to process the data rather than treating it as text. Uncomment the third template if it is really important to have the declaration on the envelope instead.
I have an XML document, and I want to change the values for one of the attributes.
First I copied everything from input to output using:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
And now I want to change the value of the attribute "type" in any element named "property".
This problem has a classical solution: Using and overriding the identity template is one of the most fundamental and powerful XSLT design patterns:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNewType" select="'myNewType'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="property/#type">
<xsl:attribute name="type">
<xsl:value-of select="$pNewType"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
When applied on this XML document:
<t>
<property>value1</property>
<property type="old">value2</property>
</t>
the wanted result is produced:
<t>
<property>value1</property>
<property type="myNewType">value2</property>
</t>
Tested on a simple example, works fine:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#type[parent::property]">
<xsl:attribute name="type">
<xsl:value-of select="'your value here'"/>
</xsl:attribute>
</xsl:template>
Edited to include Tomalak's suggestion.
The top two answers will not work if there is a xmlns definition in the root element:
<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<property type="old"/>
</html>
All of the solutions will not work for the above xml.
The possible solution is like:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()[local-name()='property']/#*[local-name()='type']">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}">
some new value here
</xsl:attribute>
</xsl:template>
<xsl:template match="#*|node()|comment()|processing-instruction()|text()">
<xsl:copy>
<xsl:apply-templates select="#*|node()|comment()|processing-instruction()|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You need a template that will match your target attribute, and nothing else.
<xsl:template match='XPath/#myAttr'>
<xsl:attribute name='myAttr'>This is the value</xsl:attribute>
</xsl:template>
This is in addition to the "copy all" you already have (and is actually always present by default in XSLT). Having a more specific match it will be used in preference.
I had a similar case where I wanted to delete one attribute from a simple node, and couldn't figure out what axis would let me read the attribute name. In the end, all I had to do was use
#*[name(.)!='AttributeNameToDelete']
I also came across same issue and i solved it as follows:
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- copy property element while only changing its type attribute -->
<xsl:template match="property">
<xsl:copy>
<xsl:attribute name="type">
<xsl:value-of select="'your value here'"/>
</xsl:attribute>
<xsl:apply-templates select="#*[not(local-name()='type')]|node()"/>
</xsl:copy>
</xsl:template>
For the following XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<property type="foo"/>
<node id="1"/>
<property type="bar">
<sub-property/>
</property>
</root>
I was able to get it to work with the following XSLT:
<?xml version="1.0" encoding="utf-8"?>
<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="//property">
<xsl:copy>
<xsl:attribute name="type">
<xsl:value-of select="#type"/>
<xsl:text>-added</xsl:text>
</xsl:attribute>
<xsl:copy-of select="child::*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If your source XML document has its own namespace, you need to declare the namespace in your stylesheet, assign it a prefix, and use that prefix when referring to the elements of the source XML - for example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />
<!-- identity transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- exception-->
<xsl:template match="xhtml:property/#type">
<xsl:attribute name="type">
<xsl:text>some new value</xsl:text>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Or, if you prefer:
...
<!-- exception-->
<xsl:template match="#type[parent::xhtml:property]">
<xsl:attribute name="type">
<xsl:text>some new value</xsl:text>
</xsl:attribute>
</xsl:template>
...
ADDENDUM:
In the highly unlikely case where the XML namespace is not known beforehand, you could do:
<?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" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />
<!-- identity transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- exception -->
<xsl:template match="*[local-name()='property']/#type">
<xsl:attribute name="type">
<xsl:text>some new value</xsl:text>
</xsl:attribute>
</xsl:template>
Of course, it's very difficult to imagine a scenario where you would know in advance that the source XML document contains an element named "property", with an attribute named "type" that needs replacing - but still not know the namespace of the document. I have added this mainly to show how your own solution could be streamlined.