5

i am trying to filter on a specific field and concat on another field:

Input:

<?xml version="1.0" encoding="UTF-8"?>
<payloads>
    <payload>
        <firstname>michael</firstname>
        <secondname>brown</secondname>
        <number>1</number>
    </payload>
    <payload>
        <firstname>michael</firstname>
        <secondname>brown</secondname>
        <number>2</number>
    </payload>
    <payload>
        <firstname>michael</firstname>
        <secondname>brown</secondname>
        <number>3</number>
    </payload>
</payloads>

Output:

<?xml version="1.0" encoding="UTF-8"?>
<payloads>
    <payload>
        <firstname>michael</firstname>
        <secondname>brown</secondname>
        <number>1,2,3</number>
    </payload>
</payloads>

I know that i need to loop through each payload tag, but at the moment i am unable to get it too output correctly. At the moment i have this:

<?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="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="payloads">
        <xsl:copy>
            <xsl:for-each select="payload">
                <payload>
                    <xsl:value-of select="firstname"/>
                    <xsl:value-of select="secondname"/>
                    <xsl:value-of select="number"/>
                </payload>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
1
  • So actually, you want to group by firstname, secondname and then "aggregate" (concat) all of their numbers? Commented Mar 19, 2012 at 10:20

1 Answer 1

3

Use this template:

<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="k" match="payload" use="concat(firstname, '|', secondname)"/>

    <xsl:template match="payload[generate-id() = 
                  generate-id(key('k', concat(firstname, '|', secondname)))]">
        <xsl:copy>
            <xsl:copy-of select="firstname"/>
            <xsl:copy-of select="secondname"/>
            <number>
                <xsl:for-each select="key('k', concat(firstname, '|', secondname))">
                    <xsl:value-of select="number"/>

                    <xsl:if test="position() != last()">
                        <xsl:text>,</xsl:text>
                    </xsl:if>
                </xsl:for-each>
            </number>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="payload"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

When applied to provided input XML, it outputs wanted correct result:

<payloads>
  <payload>
    <firstname>michael</firstname>
    <secondname>brown</secondname>
    <number>1,2,3</number>
  </payload>
</payloads>
Sign up to request clarification or add additional context in comments.

4 Comments

That works perfectly. thank you. If i wanted to use the same but just match on say firstname, could i use this? <xsl:key name="k" match="payload" use="firstname"/>?? And <xsl:apply-templates select="payload[generate-id() = generate-id(key('k', firstname))]"/> & this <xsl:for-each select="key('k', firstname)">??
@liveek, Welcome. Yes, you can.
Thats great. I was wondering im wanting to get much more experiance in XSLT as im new to this. Is there anything you would advice to read, or training to do to become more sufficient?
@liveek, XSLT/XPath spec from W3C (e.g.: w3.org/TR/xslt), some high-rating XSLT book, and practice, practice, practice...

Your Answer

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