Search code examples
xsltkeydocumentlookupxpath-1.0

In XSLT 1.0 how to create a lookup function on XML data embedded in the XSLT using key and document('')?


My specific need is to lookup line-heights given a particular font-size, but I am seeking to learn a general technique of creating lookups/a specific mapping.

I think it is possible to embed XML in the XSLT document itself (so it can work standalone) and build a key on it using the document('') function to reference the current XSLT, something like this:

<xsl:variable name="data.font-metrics.line-height">
    <line-height font-size="11">12</line-height>
    <line-height font-size="12">14</line-height>
    <line-height font-size="13">15</line-height>
    <line-height font-size="14">16</line-height>
    <line-height font-size="15">17</line-height>
    <line-height font-size="16">18</line-height>
    <line-height font-size="17">20</line-height>
    <line-height font-size="18">21</line-height>
</xsl:variable>
<xsl:key name="lookup.font-metrics.line-height" match="document('')//xsl:variable[@name='data.font-metrics.line-height'])/line-height" use="@font-size"/>

After that, I should be able to lookup a line height using the key function:

<xsl:value-of select="key('lookup.font-metrics.line-height',$font-size)"/>

...however I am getting the following error message:

XPath error : Invalid expression
//document('')//xsl:variable[@name='data.font-metrics.line-height'])/line-height/text()
           ^

I think several problems are coming together here:

  • use of the document function
  • use of the key function
  • what is the best method of embedding XML? in a variable?

There may also be a completely different solution to the problem.

I would be very grateful of your help!


Solution

  • In XSLT 1.0, the key() function works only in the context of the current document (in XSLT 2.0 it has a third argument, allowing you to select the context). In order to use the key on nodes in another document, you must first switch the context to that document - for example:

    XSLT 1.0

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:variable name="data.font-metrics.line-height">
        <line-height font-size="11">12</line-height>
        <line-height font-size="12">14</line-height>
        <line-height font-size="13">15</line-height>
        <line-height font-size="14">16</line-height>
        <line-height font-size="15">17</line-height>
        <line-height font-size="16">18</line-height>
        <line-height font-size="17">20</line-height>
        <line-height font-size="18">21</line-height>
    </xsl:variable>
    
    <xsl:key name="lookup.font-metrics.line-height" match="line-height" use="@font-size"/>
    
    <xsl:template match="/">
        <xsl:param name="font-size" select="14"/>
        <output>
            <!-- other nodes -->
            <!-- switch context to the stylesheet itself in order to use the key -->
            <xsl:for-each select="document('')">
                <lookup>
                    <xsl:value-of select="key('lookup.font-metrics.line-height', $font-size)"/>
                </lookup>
            </xsl:for-each>
            <!-- more nodes -->
        </output>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Result

    <?xml version="1.0" encoding="UTF-8"?>
    <output>
       <lookup>16</lookup>
    </output>
    

    Note that the xsl:key element is not playing a part in this switch and can be defined in much simpler terms.