Fragosus
Fragosus

Reputation: 5

Concatenate multiple xml element values into a single element attribute

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

Answers (4)

Uday Dubey
Uday Dubey

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

michael.hor257k
michael.hor257k

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

Martin Honnen
Martin Honnen

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

zx485
zx485

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

Related Questions