user2530053
user2530053

Reputation: 21

Grouping XPath queries based on node values?

I'm wondering if there is such as thing as being able to group XML data based on the text within elements.

I have some XML similar to the following, dumbed down for simplicity:

<person>
    <name>Bob</name>
    <food>Apples</food>
</person>
<person>
    <name>Billy</name>
    <food>Bananas</food>
</person>
<person>
    <name>Bob</name>
    <food>Oranges</food>
</person>

I want to display the above data in the following way:

Person's Name: Bob
Person's Food: Apples
               Oranges

Person's Name: Billy
Person's Food: Bananas

I've tried multiple approaches using all the different examples but I just can't hit right right use of XPath for this one. I just can't wrap my head around being able to display only the first instance of Bob but also be able to display all of Bob's food, which is spread across multiple instances of Bob. Any suggestions as to where I could start with this?

Upvotes: 1

Views: 2722

Answers (2)

hr_117
hr_117

Reputation: 9627

To do this with xslt-1.0 you should have a look for Muenchian Method e.g this

Therefore try this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes" method="text"/>

    <xsl:key name="kPerson" match="person" use="name"/>
    <xsl:template match="/*">
        <xsl:for-each select="person[count( . | key('kPerson', name)[1]) =1 ]">
            <xsl:variable name="pname" select="name"  />
            <xsl:text>Person's Name: </xsl:text>
            <xsl:value-of select="$pname"/>
            <xsl:text>&#10;</xsl:text>
            <xsl:for-each select="key('kPerson', $pname)" >
                <xsl:choose>
                    <xsl:when test="position()=1">Person's Food: </xsl:when>
                    <xsl:otherwise>
                        <xsl:text>               </xsl:text>
                    </xsl:otherwise>
                </xsl:choose>
                <xsl:value-of select="food"/>
                <xsl:text>&#10;</xsl:text>
            </xsl:for-each>

        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet> 

Upvotes: 1

BeniBela
BeniBela

Reputation: 16917

You can do something like that with XPath 2 (without xslt), using distinct-values to get all names, and then create strings with the food for each name.

for $v in distinct-values(/person/name) return concat($v, ": ", string-join(/person[name = $v]/food, ", "))

returns (with type infos)

sequence: (string: Bob: Apples, Oranges, string: Billy: Bananas)

Or more similar to your output:

for $v in distinct-values(/person/name) return concat(
  "Person's Name: ", $v, "
Person's Food: ", string-join(/person[name = $v]/food, "
               "))

returns

sequence: (string: Person's Name: Bob
Person's Food: Apples
               Oranges, string: Person's Name: Billy
Person's Food: Bananas)

Upvotes: 2

Related Questions