XSLT copy by attribute - xslt

I have a problem. Cant get to copy only node with specific attribute in XSLT. My XML;
<root>
<mouse code="red"> .. </mouse>
<mouse code="blue"> .. </mouse>
<mouse code="green"> .. </mouse>
</root>
I am trying to copy only node with red attribute to get XML like this:
<root>
<mouse code="red"> .. </mouse>
</root>
Can this be done with simple XSLT transformation?
DeLuka

You can do simply:
<xsl:template match="/root">
<xsl:copy>
<xsl:copy-of select="mouse[#code='red']"/>
</xsl:copy>
</xsl:template>

This can be done simply with xsl:templates matching the node you want and using copy/copy-of.
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="mouse[#code='red']">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
Of course, if there are other elements within mouse[#code='red'] then you'll need to handle them with another template.

Related

Create a root element in XSLT

I have an input document, where I only want to extract the elements with the prefix ppp. All elements with the prefix ppp are on the same level.
Input:
<root>
<ppp:element>aaa</ppp:element>
<ppp:element>ccc</ppp:element>
<lala:element>PPP</lala:element>
<rrr:element>dsfsdbfsdf</rrr:element>
</root>
In my XSLT I copy all the elements with the prefix ppp to the output file.
The problem is that I don't have a root element in the output file.
So I need to create a root element. And inside the root element, I should copy all element with the prefix ppp.
My XSLT:
<xsl:template match="node()">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="ppp:*">
<xsl:copy>
<xsl:apply-templates select="ppp:*/node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Desired output:
<root>
<ppp:element>aaa</ppp:element>
<ppp:element>ccc</ppp:element>
</root>
Given a well-formed input, all your stylesheet would have to do is:
<xsl:template match="/root">
<xsl:copy>
<xsl:copy-of select="ppp:*"/>
</xsl:copy>
</xsl:template>
A more generic option to discard all elements, no matter where they are in the XML tree, if they have the ppp prefix (regardless of the namespace URI – this is bad practice! But it's strictly what you're asking for), while preserving the root of the input:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ppp="http://example.org">
<xsl:output method="xml" />
<xsl:template match="/|#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[substring-before(name(), ':') != 'ppp' and not(. = /)]" />
</xsl:stylesheet>
Here is the XSLT transform demo: http://xsltransform.net/6r5Gh3y/2.
Thnx.
<xsl:template match="/">
<ppp:name>
<xsl:apply-templates select="node()"></xsl:apply-templates>
</ppp:name>
</xsl:template>
<xsl:template match="ppp:*">
<xsl:copy>
<xsl:apply-templates select="ppp:*/node()"/>
</xsl:copy>
</xsl:template>
The above works for me :)

XSLT - Copy grandchildren reordered

I'm quite new to XSLT and I was trying to copy an existent XML file I already have but with the elements reordered but I got stuck when trying to reorder grandchildren.
Let's say I have this input:
<grandParent>
<parent>
<c>789</c>
<b>
<b2>123</b2>
<b1>456</b1>
</b>
<a>123</a>
</parent>
....
</grandParent>
What I want to do is get the same XML file but changing the order of the tags to be a,b,c with b = b1, b2 in that order.
So I started with the XSLT file:
<xsl:template match="node()|#*"> <- This should copy everything as it is
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="a"/>
<xsl:copy-of select="b"/>
<xsl:copy-of select="c"/>
</xsl:copy>
</xsl:template>
But "xsl:copy-of select="b"" copies the elements as they are specified (b2, b1).
I tried using another xsl:template for "grandParent/parent/b" but wouldn't help.
Maybe I'm not doing things the correct way... Any tips?
Thanks!
SOLUTION - Thanks to Nils
Your solution works just fine Nils, I just customized it a bit more to fit in my current scenario where "b" is optional and the names of the tags might not be correlative.
The final code is like this:
<xsl:template match="node()|#*"> <- This should copy everything as it is
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="a"/>
<xslt:if test="b">
<b>
<xsl:copy-of select="b1"/>
<xsl:copy-of select="b2"/>
</b>
</xslt:if>
<xsl:copy-of select="b"/>
<xsl:copy-of select="c"/>
</xsl:copy>
</xsl:template>
I tried using another xsl:template for "grandParent/parent/b" but wouldn't help.
Since you have an identity template you should use <xsl:apply-templates> instead of <xsl:copy-of>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="a"/>
<xsl:apply-templates select="b"/>
<xsl:apply-templates select="c"/>
</xsl:copy>
</xsl:template>
Now you can add a similar template for the b elements
<xsl:template match="parent/b">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="b1"/>
<xsl:apply-templates select="b2"/>
</xsl:copy>
</xsl:template>
This will nicely handle the case where b doesn't exist - if the select="b" doesn't find any elements, then no templates will fire.
In fact, if the sort order is the same in both cases (alphabetically by element name) then you can combine the two templates into one which uses <xsl:sort>, giving a complete transformation of
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent | parent/b">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="*">
<xsl:sort select="name()" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
(for the example XML you've given you don't actually need the #* bits because the XML doesn't include any attributes, but it won't do any harm to leave it there in case there are any in the real XML or you add any in future).
Using xsl:sort.
The following code is off the top of my head and might not work; the thought behind it should be clear though.
<xsl:template match="node()|#*"> <- This should copy everything as it is
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="a"/>
<b>
<xsl:for-each select="b/*">
<xsl:sort select="text()" />
<xsl:copy-of select="." />
</xsl:for-each>
</b>
<xsl:copy-of select="c"/>
</xsl:copy>
</xsl:template>
Here is a most generic solution -- using xsl:sort and templates -- no xsl:copy-of and no hardcoding of specific 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:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*/*">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<grandParent>
<parent>
<c>789</c>
<b>
<b2>123</b2>
<b1>456</b1>
</b>
<a>123</a>
</parent>
....
</grandParent>
the wanted, correct result is produced:
<grandParent>
<parent>
<a>123</a>
<b>
<b1>456</b1>
<b2>123</b2>
</b>
<c>789</c>
</parent>
....
</grandParent>
Now, let's change all the names in the XML document -- note that none of the other answers works with this:
<someGrandParent>
<someParent>
<z>789</z>
<y>
<y2>123</y2>
<y1>456</y1>
</y>
<x>123</x>
</someParent>
....
</someGrandParent>
We apply the same transformation and it again produces the correct result:
<someGrandParent>
<someParent>
<x>123</x>
<y>
<y1>456</y1>
<y2>123</y2>
</y>
<z>789</z>
</someParent>
....
</someGrandParent>

Getting 3rd level Parent node from child node XSLT when adding attribute

XML
<root>
<Algemeen>
<foto>
<foe>
<fee>
<img src="www.blah.com/sample.jif"></img>
</fee>
</foe>
</foto>
</Algemeen>
</root>
XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<result>
<xsl:apply-templates select="/root/Algemeen/foto/foe/fee/img"/>
</result>
</xsl:template>
<!--specific template match for this img -->
<xsl:template match="/root/Algemeen/foto/foe/fee/img">
<xsl:copy>
<xsl:attribute name="width">100</xsl:attribute>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!--Identity template copies content forward -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I'm adding an attribute to "img" tag via template, how can i get the whole "foto" node? is this "#*|node()" refers to 2nd level parent node "foe"?
viewed links:
xslt how to add attributes to copy-of
Can E4X Get Attribute of a Parent Node Based on Attribute of a Child
At Any Level?
is this "#*|node()" refers to 2nd level parent node "foe"?
Nope! This refers to child nodes and attributes ..
How to copy <img> adding an attribute, along with its grand-parent <foto>??
In your code you are matching the root node by saying <xsl:template match="/"> and you are renaming it as <result>. Under that you are saying <xsl:apply-templates select="/root/Algemeen/foto/foe/fee/img"/> .. So that will skip the hierarchy and do what <xsl:template match="/root/Algemeen/foto/foe/fee/img"> says ..
Your <xsl:template match="/root/Algemeen/foto/foe/fee/img"> template looks perfect!! All you need is below correction!
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<result>
<xsl:apply-templates select="/root/Algemeen/foto"/>
</result>
</xsl:template>
<xsl:template match="foto">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--specific template match for this img -->
<xsl:template match="/root/Algemeen/foto/foe/fee/img">
<xsl:copy>
<xsl:attribute name="width">100</xsl:attribute>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!--Identity template copies content forward -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you want to access the parent or ancestor try this:
<xsl:for-each select="ancestor::foto">

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 the parent tag name and Delete an attribute from XML file?

I have an XML file from which I need to delete an attribute with name "Id" (It must be deleted wherever it appears) and also I need to rename the parent tag, while keeping its attributes and child elements unaltered .. Can you please help me modifying the code. At a time, am able to achieve only one of the two requirements .. I mean I can delete that attribute completely from the document or I can change the parent tag ..
Here is my code to which removes attribute "Id":
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id[parent::*]">
</xsl:template>
Please help me changing the parent tag name from "Root" to "Batch".
None of the offered solutions really solves the problem: they simply rename an element named "Root" (or even just the top element), without verifying that this element has an "Id" attribute.
wwerner is closest to a correct solution, but renames the parent of the parent.
Here is a solution that has the following properties:
It is correct.
It is short.
It is generalized (the replacement name is contained in a variable).
Here is the code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vRep" select="'Batch'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id"/>
<xsl:template match="*[#Id]">
<xsl:element name="{$vRep}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()|text()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id" />
<xsl:template match="Root">
<Batch>
<xsl:copy-of select="#*|*|text()" />
</Batch>
</xsl:template>
This should do the job:
<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()|text()" />
</xsl:copy>
</xsl:template>
<xsl:template match="node()[node()/#Id]">
<batch>
<xsl:apply-templates select='#*|*|text()' />
</batch>
</xsl:template>
<xsl:template match="#Id">
</xsl:template>
</xsl:stylesheet>
I tested with the following XML input:
<root anotherAttribute="1">
<a Id="1"/>
<a Id="2"/>
<a Id="3" anotherAttribute="1">
<b Id="4"/>
<b Id="5"/>
</a>
I would try:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id">
</xsl:template>
<xsl:template match="/Root">
<Batch>
<xsl:apply-templates select="#*|node()"/>
</Batch>
</xsl:template>
The first block copies all that is not specified, as you use.
The second replaces #id with nothing wherever is occurs.
The third renames /Root to /Batch.