Search code examples
xmlxsltstylesheet

Unable to list relative elements XSLT 2.0


I am trying to learn XSLT 2.0. In the example below, I am trying to list languages and books available in each language. Can you please help me understand how the context works?

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="Library.xsl"?>
<Library>
    <Books>
        <Book>
            <Language code="English">English</Language>
            <code>1</code>
            <Title>History</Title>
        </Book>
        <Book>
            <Language code="Spanish">Spanish</Language>
            <code>2</code>
            <Title>Math</Title>
        </Book>
    </Books>
    <Languages>
        <Language code="English">English</Language>
        <Language code="Spanish">Spanish</Language>
    </Languages>
</Library>

// Stylesheet

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <html>
            <body>
                <table>
                    <xsl:for-each select="/Library/Languages/Language">
                        <tr>
                            <td style="border:1px solid red;">
                                <xsl:value-of select="@code" /><br /><br />
                                <xsl:variable name="key" select="@code" />
                            </td>
                        </tr>
                        <xsl:for-each select="/Library/Books/Book">
                            <xsl:if test="Language[@code=string($key)]">
                            <tr>
                                <td style="border:1px solid red;">
                                    <xsl:value-of select="@code" />
                                </td>
                            </tr>
                            </xsl:if>
                        </xsl:for-each>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

// Desired output:

Language: English
Title: English Book

Language: Spanish
Title: Spanish Book

Solution

  • To get your desired output, you can change your XSLT-1.0 file to the following (XSLT-2.0 does not seem to be necessary). One crucial aspect was moving the <xsl:variable name="key" select="@code" /> out of the tdelement up two layers, so that it can be accessed in the xsl:for-each.

    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="/">
            <html>
                <body>
                    <table>
                        <xsl:for-each select="/Library/Languages/Language">
                            <xsl:variable name="key" select="@code" />
                            <tr>
                                <td style="border:1px solid red;">
                                    <xsl:value-of select="concat('Language: ', @code)" /><br /><br />
                                </td>
                            </tr>
                            <xsl:for-each select="/Library/Books/Book[Language[@code=$key]]">
                                <tr>
                                    <td style="border:1px solid red;">
                                        <xsl:value-of select="concat('Title: ', $key,' Book')" />
                                    </td>
                                </tr>
                            </xsl:for-each>
                        </xsl:for-each>
                    </table>
                </body>
            </html>
        </xsl:template>
    </xsl:stylesheet>
    

    The output is:

    <html>
      <body>
        <table>
            <tr>
              <td style="border:1px solid red;">Language: English</td>
            </tr>
            <tr>
              <td style="border:1px solid red;">Title: English Book</td>
            </tr>
            <tr>
              <td style="border:1px solid red;">Language: Spanish</td>
            </tr>
            <tr>
              <td style="border:1px solid red;">Title: Spanish Book</td>
            </tr>
        </table>
      </body>
    </html>