Reputation: 5
I'm new to XSLT and I'm trying to transform an input xml into an other by concatenating the following element values into attributes. Is there a way I can do this with XSLT?
Input xml:
<requests>
<request>
<name>BLA1</name>
<age>42</age>
</request>
<request>
<name>BLA2</name>
<age>24</age>
</request>
</requests>
Result xml:
<bodyParams>
<param name='name' value='BLA1,BLA2' />
<param name='age' value='42,24' />
</bodyParams>
Thank you for the help in advance!
Upvotes: 0
Views: 2307
Reputation: 41
Here some other method...
<xsl:template match="requests">
<xsl:variable name="name">
<xsl:value-of select="for $x in . return if ($x//name) then $x//name else ''" separator=","/>
</xsl:variable>
<xsl:variable name="age">
<xsl:value-of select="for $x in . return if ($x//age) then $x//age else ''" separator=","/>
</xsl:variable>
<bodyparams>
<param name="name" value="{$name}"/>
<param name="age" value="{$age}"/>
</bodyparams>
</xsl:template>
or
<xsl:template match="requests">
<xsl:variable name="name">
<xsl:value-of select="for $x in . return if ($x/request/*[1]) then $x/request/*[1] else ''" separator=","/>
</xsl:variable>
<xsl:variable name="age">
<xsl:value-of select="for $x in . return if ($x/request/*[2]) then $x/request/*[2] else ''" separator=","/>
</xsl:variable>
<bodyparams>
<param name="name" value="{$name}"/>
<param name="age" value="{$age}"/>
</bodyparams>
</xsl:template>
Upvotes: 2
Reputation: 116959
Assuming all request
elements have the same child nodes, in the same order, this is how I would solve this in 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="/requests">
<bodyParams>
<xsl:for-each select="request[1]/*">
<xsl:variable name="i" select="position()" />
<param name="{name()}">
<xsl:attribute name="value">
<xsl:for-each select="../../request/*[$i]">
<xsl:value-of select="." />
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</param>
</xsl:for-each>
</bodyParams>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 167446
Using XSLT 2.0 or 3.0 you could use
<xsl:template match="requests">
<bodyParams>
<xsl:for-each-group select="request/*" group-by="node-name(.)">
<param name="{current-grouping-key()}" value="{string-join(current-group(), ',')}"/>
</xsl:for-each-group>
</bodyParams>
</xsl:template>
Upvotes: 2
Reputation: 29022
The following is an XSLT-1.0 solution.
The param
's names are taken from the children's name()
of the first request
element. The xsl:if
checks if it's the last element, if not, it emits the delimiter (delimiter in XSLT-1.0).
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/requests">
<bodyParams>
<xsl:for-each select="request[1]/*"> <!-- iterate over children's names of the first request element -->
<xsl:variable name="curPos" select="name()" /> <!-- remember its name -->
<xsl:element name="param"> <!-- construct <param name="..." value="..." /> element -->
<xsl:attribute name="name"><xsl:value-of select="name()" /></xsl:attribute>
<xsl:attribute name="value"><xsl:for-each select="//request/*[name()=$curPos]">
<xsl:value-of select="normalize-space(.)" /><xsl:if test="position() != last()"><xsl:text>,</xsl:text></xsl:if>
</xsl:for-each></xsl:attribute>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="@*|node()" />
</bodyParams>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
Upvotes: 1