How to remove XML elements dynamically - xslt

I am basically looking for an XSLT (say a callable template), which will take as input an xml AND an element to be deleted in the XML and give me back the final XML after deleting that particular element in the XML.
Example:
<Request>
<Activity1>XYZ</Activity1>
<Activity2>ABC</Activity2>
</Request>
Now i need an xslt for which i must give the above xml as input and the element to be deleted (Say <Activity1>) as input. The XSLT must return the final xml after deleting the element passed to it.

You can use a modified copy-template:
<xsl:stylesheet ...>
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:variable name="removeNode">Activity1</xsl:variable>
<xsl:template match="node()">
<xsl:if test="not(name()=$removeNode)">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template match="#*">
<xsl:copy>
<xsl:apply-templates select="#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
How to pass the parameter to yout template depends on your used XSLT-processor.
Edit
Another possibility is to ignore the node when needed:
<xsl:template match="/">
<xsl:apply-templates select="*/*[not(self::element-to-ignore)]"
mode="renderResult"/>
</xsl:template>
<xsl:template match="#*|node()" mode="renderResult">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="renderResult"/>
</xsl:copy>
</xsl:template>

This is a generic transformation that accepts a global (externally specified) parameter with the name of the element to be deleted:
<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="pDeleteName" select="'c'"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test="not(name() = $pDeleteName)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on any XML document (for example the following):
<a>
<b>
<c/>
<d>
<e>
<c>
<f/>
</c>
<g/>
</e>
</d>
</b>
</a>
the correct result is produced -- the source XML document in which any element having name the same as the string in the pDeleteName parameter -- is deleted:
<a>
<b>
<d>
<e>
<g/>
</e>
</d>
</b>
</a>
As can be clearly seen, any occurence of the element <c> has been deleted.

Related

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>

How Run two xslt transformations for same node

My xml and xslt file looks like follow. problem is when i apply my transformation file only 2nd one happens 1st one skips. How could i run both on first run. Please help Thanks.
//BEFORE TRANSFORMATION
<A>
<B>
<Name>ThisOne</Name>
<Target>abc</Target>
</B>
</A>
My XSLT FIle
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<!--Transformation 1 to replace Target Text-->
<xsl:template match="A/B/Target/text()">
<xsl:text>xyz</xsl:text>
</xsl:template>
<!--Transformation 2 to Add a new node after Target-->
<xsl:template match="A/B/Target">
<xsl:copy-of select="."/>
<JOJO></JOJO>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
What i would like to see after transformation is following
<A>
<B>
<Name>ThisOne</Name>
<Target>xyz</Target>
<JOJO/>
</B>
</A>
Change
<xsl:template match="A/B/Target">
<xsl:copy-of select="."/>
<JOJO></JOJO>
</xsl:template>
to
<xsl:template match="A/B/Target">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
<JOJO></JOJO>
</xsl:template>
You could just use one template to rewrite the B node. Replace those two templates with this:
<xsl:template match ="A/B">
<B>
<Name><xsl:value-of select="Name"/></Name>
<Target>xyz</Target>
<JOJO/>
</B>
</xsl:template>

How to parse nested tags using XSLT in sequence?

I have below scenario for my XML.
<content>
<para>text-1 <emphasis type="bold">text-2</emphasis> text-3</para>
</content>
I want to parse it like below
<content>
<p>text-1 <b>text-2</b> text-3</p>
</content>
I have created my XSLT as below
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="ISO-8859-1" indent="no"/>
<xsl:template name="para">
<p>
<xsl:value-of select="text()" disable-output-escaping="yes"/>
<xsl:for-each select="child::*">
<xsl:if test="name()='emphasis'">
<xsl:call-template name="emphasis"/>
</xsl:if>
</xsl:for-each>
</p>
</xsl:template>
<xsl:template name="emphasis">
<xsl:if test="attribute::type = 'bold'">
<b>
<xsl:value-of select="text()" disable-output-escaping="yes"/>
</b>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<content>
<xsl:for-each select="content/child::*">
<xsl:if test="name()='para'">
<xsl:call-template name="para"/>
</xsl:if>
</xsl:for-each>
</content>
</xsl:template>
</xsl:stylesheet>
XSLT provided above is generating output like below
<content>
<p>text-1 text-3<b>text-2 </b></p>
</content>
Please guide me with your suggestions, how can I get my desire output?
To do this, you just need to extend the standard Identity Transform with special cases for matching your para and emphasis elements. For example, for para elements you would the following to replace para with p and then continue matching all the child nodes
<xsl:template match="para">
<p>
<xsl:apply-templates select="#*|node()"/>
</p>
</xsl:template>
So, given the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<!-- This is the Identity Transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Replace para with p -->
<xsl:template match="para">
<p>
<xsl:apply-templates select="#*|node()"/>
</p>
</xsl:template>
<!-- Replace emphasis with b -->
<xsl:template match="emphasis[#type='bold']">
<b>
<xsl:apply-templates select="node()"/>
</b>
</xsl:template>
</xsl:stylesheet>
When applied to the following input XML
<content>
<para>text-1 <emphasis type="bold">text-2</emphasis> text-3</para>
</content>
The following is output
<content>
<p>text-1 <b>text-2</b> text-3</p>
</content>
You should be able to see how easy it is to extend to other cases should you input XML have more tags to transform.
do it like this ;)
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="content">
<content><xsl:apply-templates select="para" /></content>
</xsl:template>
<xsl:template match="emphasis [#type='bold']">
<b><xsl:value-of select="." /></b>
</xsl:template>
</xsl:stylesheet>
when you do it like this the default template will catch text-1 and text-3

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.