Remove namespaces for all except for one node - xslt

Given the following source xml:
<?xml version="1.0" encoding="UTF-8"?>
<Test xmlns="http://someorg.org">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>Some text</p>
<p>Some text</p>
</div>
</text>
</Test>
I would like to have the same output as the above source xml (the source xml contains many other xml nodes but for this section, I want to output it as it is, with no changes.) I have the following xslt (see below) which strips the elements of their namespaces as desired. Unfortunately, it also strips the div elements of their name spaces but I want to retain them. The closest I got to achieving my aim is the following xslt but it outputs the div element twice because of the apply-templates but I only want the div element once with its namespace.
This is my xslt:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://someorg.org"
xmlns="http://someorg.org"
exclude-result-prefixes="f xsl">
<xsl:template match="*">
<xsl:element name="{local-name(.)}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match = "f:text/f:status">
<status value ="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<xsl:apply-templates/>
</div>
</xsl:template>
</xsl:stylesheet>

Instead of trying to remove namespaces for all elements (as handled by your <xsl:template match="*"> template, you can only target elements in the "http://someorg.org" namespace. Simply change the template match to this
<xsl:template match="f:*">
For the elements in the "http://www.w3.org/1999/xhtml" namespace, you could use the identity template to pick up everything else
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="http://someorg.org">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="f:*">
<xsl:element name="{local-name(.)}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

add a template
<xsl:template match="*[local-name()='div'
and namespace-uri() = 'http://www.w3.org/1999/xhtml']">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
to perform a copy instead of creating a namespace-stripped element.

Related

XSLT Tags attributes are getting lost

Input XML
<?xml version="1.0" encoding="UTF-8"?>
<web-inf metadata-complete="true">
<A>
<A1>DGDDG</A1>
<A1>TYTY</A1>
</A>
</web-inf>
When i am applying my transforms then the O/P XML is just dumping the <web-inf> tag without the metadata-complete="true" i.e as below
<?xml version="1.0" encoding="UTF-8"?>
<web-inf>
<A>
<A1>DGDDG</A1>
<A1>TYTY</A1>
</A>
</web-inf>
My XSLT Transform file has below in the beginning.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="web-inf[not(A/A1='hello')]">
<xsl:copy>
<xsl:call-template name="XXX"/>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Not sure what going wrong here.
Any suggestions?
<xsl:copy> copies only the current node, but not any attributes or child nodes. You are already catering for the child nodes with <xsl:apply-templates /> (which is equivalent to <xsl:apply-templates select="node()" />), but you also need to handle selecting attributes separately.
<xsl:template match="web-inf[not(A/A1='hello')]">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:call-template name="XXX"/>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>

XSLT- Creating Parent XML element

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>

xsl match template by attribute except one

I've a global match on an attribut in my stylesheet but I want to exclude the f - element. How can I do that?
Example XML:
<a>
<b formatter="std">...</b>
<c formatter="abc">...</c>
<d formatter="xxx">
<e formatter="uuu">...</e>
<f formatter="iii">
<g formatter="ooo">...</g>
<h formatter="uuu">...</h>
</f>
</d>
</a>
Current solution:
<xsl:template match="//*[#formatter]">
...
</xsl:template>
I've tried something like this, but that didn't worked.
<xsl:template match="f//*[#formatter]">
...
</xsl:template>
<xsl:template match="//f*[#formatter]">
...
</xsl:template>
Either //f[#formatter] or f[#formatter] would have worked (the // is not necessary). When this XSLT is run on your example input:
<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="*[#formatter]">
<xsl:element name="transformed-{local-name()}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="f[#formatter]">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
The result is:
<a>
<transformed-b formatter="std">...</transformed-b>
<transformed-c formatter="abc">...</transformed-c>
<transformed-d formatter="xxx">
<transformed-e formatter="uuu">...</transformed-e>
<transformed-g formatter="ooo">...</transformed-g>
<transformed-h formatter="uuu">...</transformed-h>
</transformed-d>
</a>
As you can see, the f is excluded. Does this answer your issue, or have I misunderstood what you want to do?

XSLT: When element exists add it and change value otherwise add element with value

My problem is that in some xml files an element exists and in another it does not.
When the element exists, it's value should be changed. If it does not exists, it should be added.
Here is an example for better understanding:
<root>
<group>
<element1>SomeValue1</element1>
<element2>SomeValue2</element2>
</group>
</root>
Let's say I always want element1, element2 and element3 with the values Changed1, Changed2, Changed3.
It should end up like this:
<root>
<group>
<element1>Changed1</element1>
<element2>Changed2</element2>
<element3>Changed3</element3>
</group>
</root>
What can I do to make it happen?
Thanking you in anticipation
Dennis
I'm not sure if it's the most elegant solution in the world, but I think this would probably do what you're after:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- If the element exists, do what you want to do -->
<xsl:template match="element1">
<xsl:copy>Changed1</xsl:copy>
</xsl:template>
<xsl:template match="element2">
<xsl:copy>Changed2</xsl:copy>
</xsl:template>
<xsl:template match="element3">
<xsl:copy>Changed3</xsl:copy>
</xsl:template>
<!-- If the element doesn't exist, add it -->
<xsl:template match="group">
<xsl:copy>
<xsl:apply-templates/>
<xsl:if test="not(element1)">
<element1>Changed1</element1>
</xsl:if>
<xsl:if test="not(element2)">
<element2>Changed2</element2>
</xsl:if>
<xsl:if test="not(element3)">
<element3>Changed3</element3>
</xsl:if>
</xsl:copy>
</xsl:template>
<!-- Identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Anything which isn't explicitly matched should just copy across untouched.
Of course, if the values are constant (that is, element1, element2 and element3 will always have the same values regardless of whether they're new or updated) then you can have something simpler:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- If the element exists, remove it -->
<xsl:template match="element1 | element2 | element3"/>
<!-- Now put in your preferred elements -->
<xsl:template match="group">
<xsl:copy>
<xsl:apply-templates/>
<element1>Changed1</element1>
<element2>Changed2</element2>
<element3>Changed3</element3>
</xsl:copy>
</xsl:template>
<!-- Identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Which essentially removes the original "element" nodes and puts yours in in their place.
Here is a more generic solution. The names of the elements can be specified separately from the code:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" exclude-result-prefixes="my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:newValues>
<element1>Changed1</element1>
<element2>Changed2</element2>
<element3>Changed3</element3>
</my:newValues>
<xsl:variable name="vElements" select="document('')/*/my:newValues/*"/>
<xsl:template match="*" name="identity" mode="copy">
<xsl:element name="{name()}">
<xsl:copy-of select="namespace::*[not(name()='my' or name()='xsl')]"/>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:if test="not($vElements[name()=name(current())])">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="group">
<xsl:copy>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="$vElements" mode="copy"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<root>
<group>
<element1>SomeValue1</element1>
<element2>SomeValue2</element2>
</group>
</root>
the wanted, correct result is produced:
<root>
<group>
<element1>Changed1</element1>
<element2>Changed2</element2>
<element3>Changed3</element3>
</group>
</root>
Note: The seemingly complicated processing that discards the "xsl" anf "my" namespaces is unnecessary when the to-be-modified elements are in their separate document. This code intentionally puts the to-be-modified elements in the same document as the xslt stylesheet for demonstration purposes. In practice, just an <xsl:copy-of select="$vModifiedDoc/*"> will be used.
Your example might be oversimplified. It looks like you can just generate the 3 "changed" values for every element you encounter... so I'm guessing it's a bit more complicated than that in real life.
In any case, if the changed value is not a replacement but actually a modification of the original value, you can probably use a for-each on groups and then generate the 3 elements, where the value of each element is based on an xsl-if element that checks whether the original value exists and uses it if so.
Just for fun, an XSLT 2.0 solution:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vAdd" as="element()*">
<element1>Changed1</element1>
<element2>Changed2</element2>
<element3>Changed3</element3>
</xsl:variable>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="group/*[name()=$vAdd/name()]"/>
<xsl:template match="group/*[last()]" priority="1">
<xsl:next-match/>
<xsl:apply-templates select="$vAdd"/>
</xsl:template>
</xsl:stylesheet>
Output:
<root>
<group>
<element1>Changed1</element1>
<element2>Changed2</element2>
<element3>Changed3</element3>
</group>
</root>

XSLT: How to change an attribute value during <xsl:copy>?

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.