Home > Blockchain >  xslt nested for each
xslt nested for each

Time:07-20

I have the following xml

    <?xml version="1.0" encoding="UTF-8"?>
<root>
    <employee>
        <name>a</name>
        <company>1</company>
    </employee>
    <employee>
        <name>b</name>
        <company>2</company>
    </employee>
    <employee>
        <name>c</name>
        <company>1</company>
    </employee>
    <employee>
        <name>d</name>
        <campany>2</campany>
    </employee>
    <employee>
        <name>e</name>
        <company>2</company>
    </employee>
    <employee>
        <name>f</name>
        <company>1</company>
    </employee>    
</root>

I would like to have sth like

<root>
 <company>
  <id>1</id>
  <employee>a</employee>
  <employee>c</employee>
  <employee>f</employee>
 </company>
 <company>
  <id>2</id>
  <employee>b</employee>
  <employee>d</employee>
  <employee>e</employee>
 </company>
<root>

I tried using loops

<?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"
    xmlns:mdti="urn:com.workday/multiDocumentTransform/Input" xmlns:bc="urn:com.workday/bc"
    exclude-result-prefixes="xs" version="2.0">

    <xsl:output indent="yes"/>
    <xsl:template match="/">

        <root>

            <xsl:variable name="companies" select="distinct-values(root/employee/company)"/>

            <xsl:for-each select="$companies">

                <company>
                    <companyID> <xsl:value-of select="."/></companyID>
                    <employees>
                        <xsl:for-each select="root/employee[company=.]">
                            <employee><xsl:value-of select="."/></employee>
                        </xsl:for-each>
                    </employees>
                </company>
            </xsl:for-each>


        </root>
    </xsl:template>

</xsl:stylesheet>

But it gives me the following error in the second for each:

The required item type of the context item for the child axis is node(), but the supplied expression {.} has item type xs:anyAtomicType

Is it possible to use nested loops here? I know I can get what I want with grouping.

Thanks

Przemek

CodePudding user response:

I know I can get what I want with grouping.

Not sure why you would not want to do exactly that. To do it with distinct-values() instead, you'd need something like:

XSLT 2.0

<xsl:stylesheet version="2.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">
    <xsl:variable name="root" select="." />
    <root>
        <xsl:for-each select="distinct-values(employee/company)">
            <company>
                <companyID>
                    <xsl:value-of select="."/>
                </companyID>
                <employees>
                    <xsl:for-each select="$root/employee[company=current()]">
                        <employee>
                            <xsl:value-of select="name"/>
                        </employee>
                    </xsl:for-each>
                </employees>
            </company>
        </xsl:for-each>
    </root>
</xsl:template>

</xsl:stylesheet>

This is because distinct-values() creates a sequence that is separate from the input XML.

Note also that using a predicate to select the employees of each company is inefficient. Using a key would be much better.

  • Related