Replacing a parent with its child in XSLT - xslt

I wish to use XSLT to make following change. Delete parent and move its children 1 level up.
Input is:
<data>
<parentnode>
<results>
<result>
</results>
</parentnode>
<parentnode>
<results>
<result>
<result>
</results>
</parentnode>
</data>
Expected Output is:
<data>
<parentnode>
<result>
</parentnode>
<parentnode>
<result>
<result>
</parentnode>
</data>
Instead of that, my output comes out to be:
<data>
<parentnode>
<result>
<result>
<result>
</parentnode>
<parentnode>
<result>
<result>
<result>
</parentnode>
</data>
I am using following xslt code:
<!-- Identity Template -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<!-- Move results up one node -->
<xsl:template match="results">
<xsl:copy-of select="*"/>
</xsl:template>

Related

Add attributes from to node to parent

After reading a lot about this question already, I still do not find final solution for my problem as I am an absolut beginner with xsl.
I want to add all attributes of child nodes to parent level.
This is what I have:
<rankings date="2021-03-15">
<ranking rank="1" rank_change="0" points="12008">
<player initials="" nationality="SRB" last_name="Djokovic" first_name="Novak" id="7" display_name="Novak Djokovic"/>
</ranking>
<ranking rank="2" rank_change="1" points="9940">
<player initials="" nationality="RUS" last_name="Medvedev" first_name="Daniil" id="35844" display_name="Daniil Medvedev"/>
</ranking>
<ranking rank="3" rank_change="-1" points="9670">
<player initials="" nationality="ESP" last_name="Nadal" first_name="Rafael" id="4" display_name="Rafael Nadal"/>
</ranking>
</rankings>
This is what I tried (miss identity tranform I think)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="rankings">
<data>
<xsl:apply-templates select="*"/>
</data>
</xsl:template>
<xsl:template match="ranking | player">
<row>
<xsl:apply-templates select="#* | node()"/>
</row>
</xsl:template>
<xsl:template match="ranking/#* | player/#*">
<xsl:element name="{name(.)}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
With following result:
<data>
<row>
<rank>1</rank>
<rank_change>0</rank_change>
<points>12008</points>
<row>
<initials/>
<nationality>SRB</nationality>
<last_name>Djokovic</last_name>
<first_name>Novak</first_name>
<id>7</id>
<display_name>Novak Djokovic</display_name>
</row>
</row>
</data>
This is my goal:
<data>
<row>
<rank>1</rank>
<rank_change>0</rank_change>
<points>12008</points>
<initials/>
<nationality>SRB</nationality>
<last_name>Djokovic</last_name>
<first_name>Novak</first_name>
<id>7</id>
<display_name>Novak Djokovic</display_name>
</row>
</data>
I hope one of you can help me with this.
Cheers,
Phil
try splitting ranking and player in its own template
<xsl:template match="ranking">
<row>
<xsl:apply-templates select="#* | node()"/>
</row>
</xsl:template>
<xsl:template match="player">
<xsl:apply-templates select="#* | node()"/>
</xsl:template>
Result:
<data>
<row>
<rank>1</rank>
<rank_change>0</rank_change>
<points>12008</points>
<initials/>
<nationality>SRB</nationality>
<last_name>Djokovic</last_name>
<first_name>Novak</first_name>
<id>7</id>
<display_name>Novak Djokovic</display_name>
</row>
<row>
<rank>2</rank>
<rank_change>1</rank_change>
<points>9940</points>
<initials/>
<nationality>RUS</nationality>
<last_name>Medvedev</last_name>
<first_name>Daniil</first_name>
<id>35844</id>
<display_name>Daniil Medvedev</display_name>
</row>
<row>
<rank>3</rank>
<rank_change>-1</rank_change>
<points>9670</points>
<initials/>
<nationality>ESP</nationality>
<last_name>Nadal</last_name>
<first_name>Rafael</first_name>
<id>4</id>
<display_name>Rafael Nadal</display_name>
</row>
</data>
If I am guessing correctly what your real goal is, you could do simply:
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:template match="rankings">
<data>
<xsl:for-each select="ranking">
<row>
<xsl:for-each select=".//#*">
<xsl:element name="{name(.)}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</row>
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 transformation

I'm trying to transform a XML file with XSLT 1.0 but I'm having troubles with this.
Input:
<task_order>
<Q>
<record id="1">
<column name="task_externalId">SPLIT4_0</column>
</record>
<record id="2">
<column name="task_externalId">SPLIT4_1</column>
</record>
</Q>
<task>
<id>SPLIT4</id>
<name>test</name>
</task>
</task_order>
Wanted result:
For each task_order element: When there is more than 1 record-element (SPLIT4 and SPLIT4_1) I need to duplicate the task element and change the orginal task-id with the id from record elements.
<task_order>
<Q>
<record id="1">
<column name="task_externalId">SPLIT4_0</column>
</record>
<record id="2">
<column name="task_externalId">SPLIT4_1</column>
</record>
</Q>
<task>
<id>SPLIT4_0</id>
<name>test</name>
</task>
<task>
<id>SPLIT4_1</id>
<name>test</name>
</task>
</task_order>
Any suggestions?
Thank you
First start off with the Identity Template which will handle copying across all existing nodes
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Next, to make looking up the columns slightly easier, consider using an xsl:key
<xsl:key name="column" match="column" use="substring-before(., '_')" />
Then, you have a template matching task where you can look up all matching column elements using the key, and create a new task element for each
<xsl:template match="task">
<xsl:variable name="task" select="." />
<xsl:for-each select="key('column', id)">
<!-- Create new task -->
</xsl:for-each>
</xsl:template>
Try this XSTL
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="column" match="column" use="substring-before(., '_')" />
<xsl:template match="task">
<xsl:variable name="task" select="." />
<xsl:for-each select="key('column', id)">
<task>
<id><xsl:value-of select="." /></id>
<xsl:apply-templates select="$task/*[not(self::id)]" />
</task>
</xsl:for-each>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

xslt: how to normalize numerical element

I would like to add an element to existing XML file with normalized value of existing element in the XML.
Any help would be extremely appreciated.
regards
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>no</a>
<b>8</b>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>no</a>
<b>6</b>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<c>32</c>
<d>34</d>
</Results>
</top>
In the sample input xml above, I would like to add "b_nom" elements to each of the results where the value is (b)/(minimum of 'b' grouped with a). The expected output is as below
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<b_nom>1.66</b_nom>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>no</a>
<b>8</b>
<b_nom>1.33</b_nom>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>no</a>
<b>6</b>
<b_nom>1</b_nom>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<b_nom>1</b_nom>
<c>32</c>
<d>34</d>
</Results>
</top>
I think you want
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="k1" match="Results" use="a"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:call-template name="identity"/>
<xsl:variable name="min">
<xsl:for-each select="key('k1', ../a)">
<xsl:sort select="b" data-type="number"/>
<xsl:if test="position() = 1">
<xsl:value-of select="b"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<b_nom>
<xsl:value-of select="format-number(. div $min, '0.##')"/>
</b_nom>
</xsl:template>
</xsl:stylesheet>
Here is a changed version of the stylesheet that takes a couple of values identifying a group into account:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:data="http:://example.com/data"
exclude-result-prefixes="data">
<data:data xmlns="">
<group>
<key>no</key>
<values>
<value>no</value>
<value>n</value>
<value>0</value>
</values>
</group>
<group>
<key>yes</key>
<values>
<value>yes</value>
<value>y</value>
<value>1</value>
</values>
</group>
</data:data>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="main-input" select="/"/>
<xsl:variable name="groups" select="document('')/xsl:stylesheet/data:data/group"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:call-template name="identity"/>
<xsl:variable name="this" select="."/>
<xsl:variable name="min">
<xsl:for-each select="$main-input//Results[a = $groups/values[value = $this/../a]/value]">
<xsl:sort select="b" data-type="number"/>
<xsl:if test="position() = 1">
<xsl:value-of select="b"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<b_nom>
<xsl:value-of select="format-number(. div $min, '0.##')"/>
</b_nom>
</xsl:template>
</xsl:stylesheet>
That transforms the input
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>n</a>
<b>8</b>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>0</a>
<b>6</b>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<c>32</c>
<d>34</d>
</Results>
</top>
into the output
<top>
<Results>
<a>no</a>
<b>10</b>
<b_nom>1.67</b_nom>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>n</a>
<b>8</b>
<b_nom>1.33</b_nom>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>0</a>
<b>6</b>
<b_nom>1</b_nom>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<b_nom>1</b_nom>
<c>32</c>
<d>34</d>
</Results>
</top>

Get a collection of the attributes of a nodeset

I have a collection of nodes like this
<node id="1">
<languaje>c</languaje>
<os>linux</os>
</node>
<node id="2">
<languaje>c++</languaje>
<os>linux</os>
</node>
<node id="3">
<languaje>c#</languaje>
<os>window</os>
</node>
<node id="4">
<languaje>basic</languaje>
<os>mac</os>
</node>
And i want to create a new collection of all the properties id's like this
<root>
<token>1</token>
<token>2</token>
<token>3</token>
<token>4</token>
</root>
How can do that
If you can use XQuery you can do it like this:
<root>
{ ($document/node/<node>{string(#id)}</node>) }
</root>
which is imho the clearest solution.
Otherwise you could create a string (not a document) containing your desired result with XPath 2 by concatenating the tags and your ids :
concat("<root>", string-join(for $i in /base/node/#id return concat("<node>",$i,"</node>"), " ") , "</root>")
All you need is
<xsl:output indent="yes"/>
<xsl:template match="*[node]">
<root>
<xsl:apply-templates select="node"/>
</root>
</xsl:template>
<xsl:template match="node">
<token><xsl:value-of select="#id"/></token>
</xsl:template>
If you want to store the result in a variable you can create a result tree fragment with XSLT 1.0 with e.g.
<xsl:variable name="rtf1">
<xsl:apply-templates select="node()" mode="m1"/>
</xsl:variable>
<xsl:template match="*[node]" mode="m1">
<root>
<xsl:apply-templates select="node" mode="m1"/>
</root>
</xsl:template>
<xsl:template match="node" mode="m1">
<token><xsl:value-of select="#id"/></token>
</xsl:template>
Then you can do <xsl:copy-of select="$rtf1"/> to use the result tree fragment, or with 'exsl:node-set` you can process the created nodes with XPath and XSLT e.g.
<xsl:apply-templates select="exsl:node-set($rtf1)/root/token"/>
With XSLT 2.0 there are no longer result tree fragments so you can use the variable like any input without the need for an extension function.
If you wrap all the nodes under a tag, like <nodes> this works:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<root>
<xsl:apply-templates select="*" />
</root>
</xsl:template>
<!-- templates -->
<xsl:template match="node">
<token><xsl:value-of select="#id" /></token>
</xsl:template>
</xsl:stylesheet>
Tested on XsltCake
http://www.xsltcake.com/slices/E937yH

Insert element at specific point in output document during XSLT transformation

I am wondering if you can access the result-document during processing.
The reason I ask is that I am transforming an input document and would like to insert elements depending on some conditions but this would have to occur when I have traversed the tree and I am nearly at end of creating it.
The transformed xml looks something similar to this:
<xform>
<xforms>
<model>
<instance>
<data />
<data />
</instance>
</model>
<bind />
<bind />
<bind />
</xforms>
</xform>
I intend, during transformation (before the above xml is serialized), to access the <instance> tag and insert additional <data> elements.
Note
The input document is different from the above xml - the above xml is what the transformation should produce.
Similarly, I would want to access the <xform> element and insert additional <bind> nodes.
So the final document would look like this (assuming I added 2 data nodes and 2 bind nodes):
<xform>
<xforms>
<model>
<instance>
<data />
<data />
<data>new data node</data>
<data>second new data node</data>
</instance>
</model>
<bind />
<bind />
<bind />
<bind>new bind node</bind>
<bind>second new bind node</bind>
</xforms>
</xform>
Any help is appreciated.
No, you can't access a result-document, you can however create temporary trees in variables and then process them again, if needed with templates with a different mode. So instead of e.g.
<xsl:template match="/">
<xsl:result-document href="example.xml">
<xform>
<xforms>
<model>
<instance>
<data>
</data>
</instance>
</model>
<bind />
<bind />
<bind />
</xforms>
</xform>
</xsl:result-document>
</xsl:template>
you would create the first result in a variable and then process it further as in e.g.
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="temp1">
<xform>
<xforms>
<model>
<instance>
<data>
</data>
</instance>
</model>
<bind />
<bind />
<bind />
</xforms>
</xform>
</xsl:variable>
<xsl:result-document href="example.xml">
<xsl:apply-templates select="$temp1/*"/>
</xsl:result-document>
</xsl:template>
<xsl:template match="instance">
<xsl:copy>
<xsl:apply-templates/>
<data>...</data>
</xsl:copy>
</xsl:template>
That sample does not use modes but I often use them with variables and different processing steps to cleanly seperate the templates for each step from other steps.
Yes, the way to do this is with multi-pass processing:
<xsl:stylesheet version="2.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()|#*" mode="#default pass2">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:apply-templates select="$vPass1/node()" mode="pass2"/>
</xsl:template>
<xsl:template match="instance" mode="pass2">
<instance>
<xsl:apply-templates mode="pass2"/>
<data>2</data>
<data>3</data>
</instance>
</xsl:template>
<xsl:template match="model" mode="pass2">
<model>
<xsl:apply-templates mode="pass2"/>
<bind>1</bind>
<bind>2</bind>
<bind>3</bind>
</model>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<xform>
<xforms>
<model>
<instance>
<data>
</data>
</instance>
</model>
<bind />
<bind />
<bind />
</xforms>
</xform>
it transforms it to itself using the identity rule and the result of this first pass is captured in the variable $vPass1. Then the second pass processes the current results in $vPass1 and adds two new data children under the instance element and three bind children under the model element -- so the final result is:
<xform>
<xforms>
<model>
<instance>
<data/>
<data>2</data>
<data>3</data>
</instance>
<bind>1</bind>
<bind>2</bind>
<bind>3</bind>
</model>
<bind/>
<bind/>
<bind/>
</xforms>
</xform>