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

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">

Related

xslt, Apply Child node counter to all attribute in child nodes

For XML Transformation, i have to transform XML in such a way in adds counter value to all child node attribute Below is my xml sample
<hs>
<hscode>
<hsdetail>
<Name>Shirt</Name>
<ItemPrice>30</ItemPrice>
</hsdetail>
<hsdetail>
<Name>Shirt</Name>
<ItemPrice>30</ItemPrice>
</hsdetail>
</hscode>
</hs>
Using Xslt i want to apply counter on each child nodes of , id there are multiple hsdetails, each attribute in this node will use counter, and so on, Theconverted xml looks like below
<hs>
<hscode>
<hsdetail>
<Name1>Shirt</Name1>
<ItemPrice1>30</ItemPrice1>
</hsdetail>
<hsdetail>
<Name2>Shirt</Name2>
<ItemPrice2>30</ItemPrice2>
</hsdetail>
</hscode>
</hs>
I am using xsl but does not seems to be working when applying transformation
Any help on this? The xsl is as follows:
<xsl:stylesheet xmlns:xsl="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="hsdetail/*">
<xsl:element
name="{name()}{count(preceding-sibling::*[name() = name(current())]) + 1}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template> </xsl:stylesheet>
How about:
XSLT 1.0
<xsl:stylesheet version="1.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="hsdetail">
<xsl:variable name="i" select="position()" />
<xsl:copy>
<xsl:for-each select="*">
<xsl:element name="{name()}{$i}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

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>

Change namespace of child element using xsl

How do I replace the namespace of child elements in an xml?
For example I have this source file:
<ns:Parent xmlns:ns="http://test.com">
<ns:Name>John</ns:Name>
<ns:Country>Japan</ns:Country>
<ns:Contact>9999999</ns:Contact>
</ns:Parent>
My output should be like this:
<ns:Parent xmlns:ns="http://test.com">
<ns1:Name xmlns:ns1="http://development.com">John</ns1:Name>
<ns:Country>Japan</ns:Country>
<ns:Contact>9999999</ns:Contact>
</ns:Parent>
So basically All the other fields aside from Name were not affected.
First start with the identity template, to handle copying all the nodes you don't want to change
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Then, just add a template to match ns:Name, where you create a new node in your required namespace instead (where ns1 is defined on the xsl:stylesheet element)
<xsl:template match="ns:Name">
<ns1:Name>
<xsl:apply-templates select="#*|node()"/>
</ns1:Name>
</xsl:template>
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:ns="http://test.com"
xmlns:ns1="http://development.com">
<xsl:output method="xml" indent="yes" />
<xsl:template match="ns:Name">
<ns1:Name>
<xsl:apply-templates select="#*|node()"/>
</ns1:Name>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

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>

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.