2

I am attempting to transform an XML document with an included stylesheet in multiple passes, however whenever I attempt to include exsl:node-set to make the variable I have placed the transformed XML into usable Firefox fails parsing with the notice Error loading stylesheet: An unknown error has occurred ().
I have not found any other technique to do multiple transformation passes in XSLT 1.0, and I am led to believe Firefox does not support XSLT 2.0 and should support exsl:node-set.

My code is the following:

<?xml version="1.0"  encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="#stylesheet"?>
<doc>
    <xsl:stylesheet id="stylesheet" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="xsl:stylesheet" mode="passone"/>
        <xsl:template match="@*|node()" mode="passone">
            <xsl:copy>
                <xsl:copy-of select="ancestor::node()[local-name()='inherit']/@*"/> <!-- take default from parent -->
                <xsl:copy-of select="@*"/> <!-- overwrite if applicable -->
                <xsl:apply-templates mode="passone"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="@*|node()" mode="passtwo">
            <xsl:copy>
                <xsl:copy-of select="ancestor::node()[local-name()='inherit']/@*"/> <!-- take default from parent -->
                <xsl:copy-of select="@*"/> <!-- overwrite if applicable -->
                <xsl:apply-templates mode="passtwo"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="@*|node()" mode="passthree">
            <xsl:copy>
                <xsl:copy-of select="ancestor::node()[local-name()='inherit']/@*"/> <!-- take default from parent -->
                <xsl:copy-of select="@*"/> <!-- overwrite if applicable -->
                <xsl:apply-templates mode="passthree"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="*[local-name()='inherit']" mode="passthree">
            <xsl:apply-templates mode="passthree"/>
        </xsl:template>
        <xsl:template match="*[local-name()='template'][@define]" mode="passtwo"/>
        <xsl:template match="*[local-name()='template'][@insert]" mode="passtwo">
            <xsl:copy-of select="//*[local-name()='template'][@define=current()/@insert]/*"/>
            <xsl:apply-templates mode="passtwo"/>
        </xsl:template>
        <xsl:template match="/">
            <xsl:variable name="resultone">
                <xsl:apply-templates mode="passone" select="."/>
            </xsl:variable>
            <xsl:variable name="resulttwo">
                <xsl:apply-templates mode="passtwo" select="exsl:node-set($resultone)"/>
            </xsl:variable>
            <xsl:apply-templates mode="passthree" select="exsl:node-set($resulttwo)"/>
        </xsl:template>
    </xsl:stylesheet>


    <svg version="1.1" viewBox="0 0 26 14" xmlns="http://www.w3.org/2000/svg">
        <template define="row">
            <rect/>
            <rect x="4"/>
            <rect x="8"/>
            <rect x="12"/>
            <rect x="16"/>
            <rect x="20"/>
            <rect x="24"/>
        </template>
        <inherit width="2" height="2">
            <template insert="row"/>
            <inherit y="4">
                <template insert="row"/>
            </inherit>
            <inherit y="8">
                <template insert="row"/>
            </inherit>
            <inherit y="12">
                <template insert="row"/>
            </inherit>
        </inherit>
    </svg>
</doc>

And the expected result is:

<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 26 14" xmlns="http://www.w3.org/2000/svg">
    <rect width="2" height="2"/>
    <rect x="4" width="2" height="2"/>
    <rect x="8" width="2" height="2"/>
    <rect x="12" width="2" height="2"/>
    <rect x="16" width="2" height="2"/>
    <rect x="20" width="2" height="2"/>
    <rect x="24" width="2" height="2"/>
    <rect x="24" y="4" width="2" height="2"/>
    <rect x="20" y="4" width="2" height="2"/>
    <rect x="16" y="4" width="2" height="2"/>
    <rect x="12" y="4" width="2" height="2"/>
    <rect x="8" y="4" width="2" height="2"/>
    <rect x="4" y="4" width="2" height="2"/>
    <rect y="4" width="2" height="2"/>
    <rect y="8" width="2" height="2"/>
    <rect x="4" y="8" width="2" height="2"/>
    <rect x="8" y="8" width="2" height="2"/>
    <rect x="12" y="8" width="2" height="2"/>
    <rect x="16" y="8" width="2" height="2"/>
    <rect x="20" y="8" width="2" height="2"/>
    <rect x="24" y="8" width="2" height="2"/>
    <rect x="24" y="12" width="2" height="2"/>
    <rect x="20" y="12" width="2" height="2"/>
    <rect x="16" y="12" width="2" height="2"/>
    <rect x="12" y="12" width="2" height="2"/>
    <rect x="8" y="12" width="2" height="2"/>
    <rect x="4" y="12" width="2" height="2"/>
    <rect y="12" width="2" height="2"/>
</svg>
hyperfekt
  • 169
  • 2
  • 9
  • 1
    Firefox does support `exsl:node-set` but of course you have to declare the namespace to be able to use it. A simple example is at https://xsltfiddle.liberty-development.net/pPgCcoE/1, I couldn't make sense of your code with an embedded stylesheet. Does your code produce the right result when run outside of the browser with a standalone XSLT 1 processor like xsltproc or Xalan or Saxon 6? – Martin Honnen Mar 25 '18 at 06:59
  • Thank you, I was completely unaware of that (I cobbled this monstrosity together with google-fu but without knowing anything about XSLT or XML for that matter, and nowhere was this mentioned). That allowed me to debug the transformation and it turns out that inheriting/copying of pass one/two applied pass two/three templates respectively instead of pass one/two ones. I fixed that and now it works! :D Thank you so much. Do you want to write the namespace declaration requirement in an answer or should I do that? – hyperfekt Mar 25 '18 at 20:54

1 Answers1

2

exsl:node-set describes the function node-set in the namespace with the prefix exsl. Like any other, this prefix and namespace is not available by default, but has to be declared.
Specifically node-set exists in the Common module of EXSLT, whose namespace is http://exslt.org/common.
This means you will have to add xmlns:exsl="http://exslt.org/common" to the xsl:stylesheet element, which will make this namespace available under the exsl prefix and enable you to use the desired function as exsl:node-set.

Apart from that, using modes to apply only specific templates, piping the result into a variable and using it as input to apply-templates as demonstrated in the given source is the correct way.

Thanks to Martin Honnen for the pointer.

hyperfekt
  • 169
  • 2
  • 9