Reputation: 21
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
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> </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> </xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
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