简体   繁体   中英

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!

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.

What that means, is that the <xsl:key> is not (matched and) compiled when the XSLT engine first reaches the key definition element, rather when the XSLT engine meets the first XPath key() function. Only at this time can the key be created, because only then is the context known.

...a kind of lazy execution...

MOREOVER one key can be applied to multiple documents, thus if the XML document to be transformed contained the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<fmxmlsnippet type="LayoutObjectList">
    <Layout enclosingRectTop ="46.0000000" enclosingRectLeft ="46.0000000" enclosingRectBottom ="171.0000000" enclosingRectRight ="366.0000000">
        <line-height font-size="14">18 in XML file</line-height>
        …

Then the following XSLT (added to the original XSLT above):

    <xsl:comment>
        <xsl:for-each select="document('')">
            <!-- apply the key to the XSLT document -->
            <xsl:value-of select="key('lookup.font-metrics.line-height','14')"/>
        </xsl:for-each>
    </xsl:comment>
    <xsl:comment>
            <!-- apply the key to the XML document -->
            <xsl:value-of select="key('lookup.font-metrics.line-height','14')"/>
    </xsl:comment>

would create the following output:

<!--16-->
<!--18 in XML file-->

Thus the key function is not a reference to a previously built static index, but - in combination with the for-each-document('') trick - exactly what I was looking for: a lookup function that can be used on XML nodes, wherever they may be!

Nice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM