0

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!

MrWatson
  • 476
  • 6
  • 11
  • It is quite not clear what you are trying to do, but I can tell you that document('')/xsl:variable isn't the right way of accessing a variable. Could you please add an input XML, expected output and the complete XSLT and tell us what you are trying to achieve. – Lingamurthy CS May 12 '15 at 09:12

2 Answers2

1

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.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
0

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.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
MrWatson
  • 476
  • 6
  • 11
  • "the `` is not compiled ...*" We don't know that. Only the people who wrote the XSLT engine could tell for sure - and only with regard to their own product. – michael.hor257k May 12 '15 at 20:13