Search code examples
xsltxpathxslt-2.0

XSLT retrieve values from multiple keys?


I have an XML file where I lookup multiple keys that are then used to lookup more elements. Simplified examples: Check the food someone likes and then add up the ingredients for each of them as "shopping list"

<?xml version="1.0" encoding="UTF-8"?>
<foodieparadise>
    <people>
    <person name="Frank">
        <food>Cake</food>
        <food>Ice Cream</food>
        <food>Schnitzel</food>
    </person>
        <person name="Joe">
            <food>Steak</food>
            <food>Soup</food>
            <food>Schnitzel</food>
            <food>Ice Cream</food>
        </person>
    </people>

    <ingredients>
        <food name="Ice Cream">
            <ingredient>Ice</ingredient>
            <ingredient>Cream</ingredient>
        </food>
        <food name="Cake">
            <ingredient>Egg</ingredient>
            <ingredient>Flour</ingredient>
            <ingredient>Butter</ingredient>
            <ingredient>Cream</ingredient>
        </food>
        <food name="Schnitzel">
            <ingredient>Pork</ingredient>
            <ingredient>Bread Crumbs</ingredient>
        </food>
        <food name="Steak">
            <ingredient>Beef</ingredient>
        </food>
        <food name="Soup">
            <ingredient>Tomato</ingredient>
            <ingredient>Onions</ingredient>
            <ingredient>Parsley</ingredient>
            <ingredient>Egg</ingredient>
        </food>
    </ingredients>
</foodieparadise>

I use this XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output method="text" encoding="UTF-8" />

    <xsl:template match="/">
        <xsl:apply-templates select="/foodieparadise/people/person" />
    </xsl:template>

    <xsl:template match="person">
        <xsl:value-of select="@name"/> likes <xsl:value-of select="count(food)"/> types of food
and needs to buy <xsl:value-of select="count(distinct-values(/foodieparadise/ingredients/food/ingredient))"/> different ingredients.

    </xsl:template>

</xsl:stylesheet>

The count(distinct-values(/foodieparadise/ingredients/food/ingredient)) is missing the query expression for the food each person eats. So I get the result:

Frank likes 3 types of food and needs to buy 11 different ingredients.        
Joe likes 4 types of food and needs to buy 11 different ingredients.

but what I need to achieve is:

Frank likes 3 types of food and needs to buy 7 different ingredients.        
Joe likes 4 types of food and needs to buy 8 different ingredients.

I tried count(distinct-values(/foodieparadise/ingredients/food/ingredient[text() = ./food])) but that didn't return anything.

How would my XPath and/or template need to look like?


Solution

  • You could also set up a key

    <xsl:key name="ref" match="ingredients/food" use="@name"/>
    

    and then use it

    <xsl:template match="person">
        <xsl:value-of select="@name"/> likes <xsl:value-of select="count(food)"/> types of food
        and needs to buy <xsl:value-of select="count(distinct-values(key('ref', food)/ingredient))"/> different ingredients.
    
    </xsl:template>