Search code examples
xmlxsltxslt-2.0saxondocbook

XSLT multiple output files not working with namespace


Im trying to generate multiple html output files from one merged xml file with xslt 2.0

it works as expected when i have no namespace in the section

when i use the docbook namespace there are no files generated.

Does someone know why its not working with a namespace?

my xsl file:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="2.0">

    <xsl:output method="text"/>
    <xsl:output method="html" indent="yes" name="html"/>

    <xsl:template match="/">
        <xsl:for-each select="book/chapter/section">
            <xsl:variable name="filename" select="concat('www/out/',@xml:id,'.html')" />
            <xsl:value-of select="$filename" />  <!-- Creating  -->
            <xsl:result-document href="{$filename}" format="html">
                <html><body>
                    <xsl:value-of select="title"/>
                </body></html>
            </xsl:result-document>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

my xml file:

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <chapter>
        <section xmlns="http://docbook.org/ns/docbook"  xml:id="lorem_1" >
            <title>Lorem ipsum 1?</title>
        </section>
        <section xmlns="http://docbook.org/ns/docbook" xml:id="lorem_2" >
            <title>Lorem ipsum 2?</title>
        </section>
    </chapter>
</book>

without

xmlns="http://docbook.org/ns/docbook"

it generates me 2 html files

im using Saxon 9.1.0.8J from Saxonica

build.sh

export CLASSPATH=$CLASSPATH:/usr/share/java/saxonb.jar

java net.sf.saxon.Transform -ext:on  -s:www/merged.xml -xsl:www/transform.xsl -T -xi

Solution

  • It is not working with namespaces because you have not accounted for the namespace in your XSLT. You XSLT is looking for an element called section in no namespace, and so will not find the element in the XML which is in a namespace.

    If, indeed, only the section and title elements are in a namespace, and not book and chapter too, you can handle the namespace by declaring it with a prefix in the XSLT like so:

    xmlns:db="http://docbook.org/ns/docbook"
    

    And then using that prefix wherever you need to select an element in that namespace:

    <xsl:for-each select="book/chapter/db:section">
    

    Try this XSLT

    <xsl:stylesheet
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:db="http://docbook.org/ns/docbook"
            version="2.0">
    
        <xsl:output method="text"/>
        <xsl:output method="html" indent="yes" name="html"/>
    
        <xsl:template match="/">
            <xsl:for-each select="book/chapter/db:section">
                <xsl:variable name="filename" select="concat('www/out/',@xml:id,'.html')" />
                <xsl:value-of select="$filename" />  <!-- Creating  -->
                <xsl:result-document href="{$filename}" format="html">
                    <html><body>
                        <xsl:value-of select="db:title"/>
                    </body></html>
                </xsl:result-document>
            </xsl:for-each>
        </xsl:template>
    </xsl:stylesheet>
    

    Note, supposing your XML had all the elements in the default namespace, like so:

    <book xmlns="http://docbook.org/ns/docbook">
        <chapter>
            <section xml:id="lorem_1" >
                <title>Lorem ipsum 1?</title>
            </section>
            <section xml:id="lorem_2" >
                <title>Lorem ipsum 2?</title>
            </section>
        </chapter>
    </book>
    

    Then you could use xpath-default-namespace instead, to avoid having to use a prefix

    <xsl:stylesheet
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xpath-default-namespace="http://docbook.org/ns/docbook"
            version="2.0">
    
        <xsl:output method="text"/>
        <xsl:output method="html" indent="yes" name="html"/>
    
        <xsl:template match="/">
            <xsl:for-each select="book/chapter/section">
                <xsl:variable name="filename" select="concat('www/out/',@xml:id,'.html')" />
                <xsl:value-of select="$filename" />  <!-- Creating  -->
                <xsl:result-document href="{$filename}" format="html">
                    <html><body>
                        <xsl:value-of select="title"/>
                    </body></html>
                </xsl:result-document>
            </xsl:for-each>
        </xsl:template>
    </xsl:stylesheet>