How Run two xslt transformations for same node - xslt

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>

Related

XSLT 1.0: Transforming flat structure to nested with Muenschian grouping: avoid creation of spurious copies

I am XSLT beginner, learning by example and by working on projects. Currently, I am working on creating grouped, nested structure from flat.
Consider this sample xml input:
<root>
<a>First text</a>
<b>Text</b>
<c>More text in c tag</c>
<d>There is even d tag</d>
<a>Another "a" test.</a>
<b>ěščřžýáíéúů</b>
<b>More b tags</b>
<c>One followed by c tag</c>
<a>Last a tag</a>
<b>This time only with b tag, but this goes on and on</b>
</root>
And this XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" method="xml" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="utf-8"/>
<xsl:key name="groupA" match="b|c|d" use="generate-id(preceding-sibling::a[1])" />
<xsl:key name="groupB" match="c|d" use="generate-id(preceding-sibling::b[1])"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<wrapperTest>
<xsl:apply-templates/>
</wrapperTest>
</xsl:template>
<xsl:template match="root">
<xsl:apply-templates select="#*|a"/>
<xsl:apply-templates select="#*|b"/>
</xsl:template>
<xsl:template match="a">
<xsl:copy>
<xsl:apply-templates select="key('groupA', generate-id())" />
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:copy>
<xsl:apply-templates select="key('groupB', generate-id())" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The expected output is:
<wrapperTest>
<a>First text
<b>Text
<c>More text in c tag</c>
<d>There is even d tag</d>
</b>
</a>
<a>Another "a" test.
<b>ěščřžýáíéúů</b>
<b>More b tags
<c>One followed by c tag</c>
</b>
</a>
<a>Last a tag
<b>This time only with b tag, but this goes on and on</b>
</a>
</wrapperTest>
In the transformation I created are excessive copies created and I have no idea why. I guess that the isuue I am hiting upon is basic in its nature, but I cant figure it out.
The only limit for solution is, that preferably it should be in XSLT 1.0 (since the project is incorporated in python script with lxml). In the edge case, when this couldnt be achived with XSLT 1.0, I can accomodate for recent saxon version which removes any limitations ...
I have already looked at answers here, here and others, but most of them use either XSLT 2.0 or are very complicated for a beginner to knive through.
Final note: Ideally, proposed solution should be extensible in it nature, because the final form of my project should be also grouped by tag <c>, like so:
<wrapperTest>
<a>First text
<b>Text
<c>More text in c tag
<d>There is even d tag</d>
</c>
</b>
</a>
<a>Another "a" test.
<b>ěščřžýáíéúů</b>
<b>More b tags
<c>One followed by c tag</c>
</b>
</a>
<a>Last a tag
<b>This time only with b tag, but this goes on and on</b>
</a>
</wrapperTest>
Which I will happily do as learning excersize.
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="*"/>
<xsl:key name="b" match="b" use="generate-id(preceding-sibling::a[1])" />
<xsl:key name="cd" match="c|d" use="generate-id(preceding-sibling::b[1])"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root">
<wrapperTest>
<xsl:apply-templates select="a"/>
</wrapperTest>
</xsl:template>
<xsl:template match="a">
<xsl:copy>
<xsl:apply-templates/>
<xsl:apply-templates select="key('b', generate-id())" />
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:copy>
<xsl:apply-templates/>
<xsl:apply-templates select="key('cd', generate-id())" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The recursive XSLT 2/3 for-each-group group-starting-with for all levels comes down to
<xsl:function name="mf:wrap" as="node()*">
<xsl:param name="input" as="node()*"/>
<xsl:for-each-group select="$input" group-starting-with="node()[node-name() = node-name($input[1])]">
<xsl:copy>
<xsl:sequence select="node(), mf:wrap(tail(current-group()))"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="root">
<xsl:copy>
<xsl:sequence select="mf:wrap(*)"/>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/gVhEaj8

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>

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?

How to Add to an Element, Creating it First If Needed

I need to add elements to an element, creating it first if it doesn't already exist.
My desired final result, after adding ABC and DEF, is:
<?xml version="1.0" encoding="utf-8"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Q/>
<B>
<string>ABC</string>
<string>DEF</string>
</B>
<A>
I thought that the following would accomplish this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Identity transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- Insert a B element if it doesn't exist. -->
<xsl:template match="A[not(B)]">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<B/>
</xsl:copy>
</xsl:template>
<xsl:template match="B">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<string>ABC</string>
<string>DEF</string>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If I start with the following, in which <B> already exists, it works fine, returning the result above:
<?xml version="1.0" encoding="utf-8"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org
<Q/>
<B/>
</A>
However, if I don't have a <B>, as in below:
<?xml version="1.0" encoding="utf-8"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org
<Q/>
</A>
then it creates the <B> as below, but doesn't insert ABC and DEF:
<?xml version="1.0" encoding="utf-8"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org
<Q/>
<B/>
</A>
So, what am I missing? Thanks in advance.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Identity transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- Insert a B element with string elements if it doesn't exist. -->
<xsl:template match="A[not(B)]">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<B>
<xsl:call-template name="add-strings"/>
</B>
</xsl:copy>
</xsl:template>
<!-- Add string elements to existing B if missing -->
<xsl:template match="B[not(string)]">
<xsl:copy>
<xsl:call-template name="add-strings"/>
</xsl:copy>
</xsl:template>
<!-- Add string elements to caller -->
<xsl:template name="add-strings">
<string>ABC</string>
<string>DEF</string>
</xsl:template>
</xsl:stylesheet>
You will have to add the sub tags of B too when B does not exist, as follows:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- Identity transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- Insert a B element if it doesn't exist. -->
<xsl:template match="A[not(B)]">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<B>
<string>ABC</string>
<string>DEF</string>
</B>
</xsl:copy>
</xsl:template>
<xsl:template match="B">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<string>ABC</string>
<string>DEF</string>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied to
<?xml version="1.0" encoding="UTF-8"?>
<root>
<A>
<Q/>
</A>
<A>
<Q/>
<B/>
</A>
</root>
this gives
<?xml version="1.0" encoding="UTF-8"?>
<root>
<A>
<Q/>
<B>
<string>ABC</string>
<string>DEF</string>
</B>
</A>
<A>
<Q/>
<B>
<string>ABC</string>
<string>DEF</string>
</B>
</A>
</root>
Empo's answer was very close, but if <B> already contained <string> elements, the new <string>s weren't added. I made two minor changes, which solved that:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Identity transform. -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- Insert a B element with string elements if it doesn't exist. -->
<xsl:template match="A[not(B)]">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<B>
<xsl:call-template name="add-strings"/>
</B>
</xsl:copy>
</xsl:template>
<!-- Add string elements to existing B element. -->
<xsl:template match="B"> <!-- Whether there are <string>s or not. -->
<xsl:copy>
<xsl:apply-templates select="#* | node()"/> <!-- Keep existing <string>s. -->
<xsl:call-template name="add-strings"/>
</xsl:copy>
</xsl:template>
<!-- Add string elements to caller. -->
<xsl:template name="add-strings">
<string>ABC</string>
<string>DEF</string>
</xsl:template>
</xsl:stylesheet>

How to remove XML elements dynamically

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.