0

I have this XML:

<Root>
  <Employee>
    <Name>Dash</Name>
    <Age>23</Age>
  </Employee>
  <Employee>
    <Name>Gwen</Name>
    <Age>22</Age>
  </Employee>
</Root>

And I need to convert to below XML using XSLT:

<Root>
  <Employee>
    <Name>Dash,Gwen</Name>
    <Age>23,22</Age>
  </Employee>
</Root>

I am using the for-each loop to get the values of the sub-nodes of the <Employee> node.The problem I am facing is I cannot figure out how to store the concatenated values in another temperoary variable in XSLT.I found on many sites that we cannot update the variables in the XSLT, so is there any alternative solution for this?

9
  • 1
    Why is necessary to to store the concatenated values in a temporary variable? Commented Sep 15, 2014 at 4:35
  • you need to use a recursive template or try to come up with a solution that does not need the temporary variable. Commented Sep 15, 2014 at 4:38
  • 1
    @Pawel I know what recursive templates do. Why would you need to use one here? Commented Sep 15, 2014 at 5:31
  • 2
    Can you use XSLT 2.0, or are you limited to XSLT 1.0? Commented Sep 15, 2014 at 7:42
  • 1
    In that case, the answer provided by Chong Lip Phang will work for you (It works in XSLT 2.0 as well as XSLT 3.0). Commented Sep 15, 2014 at 8:31

3 Answers 3

2

Will this do?

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="3.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="Root">
      <Root>
         <Employee>
            <Name>
              <xsl:value-of select="Employee/Name" separator="," />
            </Name>
            <Age>
              <xsl:value-of select="Employee/Age" separator="," />
            </Age>
         </Employee>
      </Root>
   </xsl:template>
</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

6 Comments

Note to anyone trying this: it requires an XSLT 2.0 or 3.0 processor. With a 1.0 processor, the separator attribute will be ignored, and only the first Employee/Name or Employee/Age will be output.
@Chong Lip Phang:- When I use your code, It gives an error separator attribute is not declared.
@Dash Are you sure you are using an XSLT 2.0 processor? Which one? And what is the exact error message you're getting?
No its not XSLT 2.0 processor. I am sorry for that. And I guess the error is due to this reason only!!
@Dash In such case, Joel's answer will probably work for you, albeit a little inefficiently due to the repeated code. Unless you have some special requirement (you never answered my question about why it's necessary to store the concatenated values in a temporary variable).
|
1

This is what I meant by "use a common template for both and avoid the code repetition":

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="/Root">
    <Root>
        <Employee>
            <Name>
                <xsl:apply-templates select="Employee/Name"/>
            </Name>
            <Age>
                <xsl:apply-templates select="Employee/Age"/>
            </Age>
        </Employee>
    </Root>
</xsl:template>

<xsl:template match="Name|Age">
    <xsl:value-of select="."/>
    <xsl:if test="position()!=last()">
        <xsl:text>,</xsl:text>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

While I am here, let me also point out that this replaces a solid XML structure with a very problematic one. I am not sure what's the purpose of this transformation, but the result is an XML that could be very difficult to consume by downstream applications.

1 Comment

Nice optimization @michael.hor257k. Thank you!
0

please try the following stylesheet:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes"/>

    <xsl:template match="Root">
        <Root>
            <Employee>
                <Name>
                    <xsl:for-each select="Employee/Name">
                        <xsl:if test="position() &gt; 1">
                            <xsl:text>,</xsl:text>
                        </xsl:if>
                        <xsl:value-of select="."/>
                    </xsl:for-each>
                </Name>
                <Age>
                    <xsl:for-each select="Employee/Age">
                        <xsl:if test="position() &gt; 1">
                            <xsl:text>,</xsl:text>
                        </xsl:if>
                        <xsl:value-of select="."/>
                    </xsl:for-each>
                </Age>
            </Employee>
        </Root>
    </xsl:template>
</xsl:stylesheet>

or as an alternative,

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:strip-space elements="*"/>

    <xsl:output indent="yes"/>

    <xsl:template match="/">
        <Root>
            <Employee>
                <xsl:apply-templates select="Root/Employee[1]"/>
            </Employee>
        </Root>
    </xsl:template>

    <xsl:template match="Employee[1]/Name|Employee[1]/Age">
        <xsl:variable name="curr_name" select="name()"/>
        <xsl:copy>
            <xsl:value-of select="."/>
            <xsl:for-each select="following::*[name()=$curr_name]">
                <xsl:text>,</xsl:text>
                <xsl:value-of select="."/>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>


</xsl:stylesheet>

3 Comments

Couldn't you use a common template for both and avoid the code repetition?
@michael.hor257k I have created an alternative stylesheet. Is this what you are referring to?
No, not quite - see my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.