2

While answering this question, it occurred to me that I know how to use the XSLT 3.0 (XPath 3.0) serialize() function, but that I do not know how to avoid serialization of namespaces that are in scope. Here is a minimal example:

XML Input

<?xml version="1.0" encoding="UTF-8" ?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
    <cichlid id="1">
        <name>Zeus</name>
        <color>gold</color>
        <teeth>molariform</teeth>
        <breeding-type>lekking</breeding-type>
    </cichlid>
</ci:cichlids>

XSLT 3.0 Stylesheet

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization"
    xmlns:ci="http://www.cichlids.com">

    <xsl:output method="xml" encoding="UTF-8" indent="yes" />

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/ci:cichlids/cichlid">
        <xsl:variable name="serial-params">
            <output:serialization-parameters>
                <output:omit-xml-declaration value="yes"/>
            </output:serialization-parameters>
        </xsl:variable>
        <xsl:value-of select="serialize(., $serial-params/*)"/>
    </xsl:template>

</xsl:stylesheet>

Actual Output

<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
    &lt;cichlid xmlns:ci="http://www.cichlids.com" id="1"&gt;
        &lt;name&gt;Zeus&lt;/name&gt;
        &lt;color&gt;gold&lt;/color&gt;
        &lt;teeth&gt;molariform&lt;/teeth&gt;
        &lt;breeding-type&gt;lekking&lt;/breeding-type&gt;
    &lt;/cichlid&gt;
</ci:cichlids>

The serialization process included the namespace declaration that is in scope for the cichlid element, although it is not used on this element. I would like to remove this declaration and make the output look like

Expected Output

<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
    &lt;cichlid id="1"&gt;
        &lt;name&gt;Zeus&lt;/name&gt;
        &lt;color&gt;gold&lt;/color&gt;
        &lt;teeth&gt;molariform&lt;/teeth&gt;
        &lt;breeding-type&gt;lekking&lt;/breeding-type&gt;
    &lt;/cichlid&gt;
</ci:cichlids>

I know how to modify the cichlid element, removing the namespaces in scope, and serialize this modified element instead. But this seems a rather cumbersome solution. My question is:

What is a canonical way to serialize an XML element using the serialize() function without also serializing unused namespace declarations that are in scope?


Testing with Saxon-EE 9.6.0.7 from within Oxygen.

Community
  • 1
  • 1
Mathias Müller
  • 22,203
  • 13
  • 58
  • 75

2 Answers2

3

Serialization will always give you a faithful representation of the data model that you are serializing. If you want to modify the data model, that's called transformation. Run a transformation to remove the unwanted namespaces, then serialize the result.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Thanks for your answer. If I were serializing the result of a "normal" XSLT transformation, I could use `exclude-result-prefixes` to remove unused namespaces and would not have to modify the data model directly. Is there a means comparable to `exclude-result-prefixes` when using `serialize()`? – Mathias Müller Feb 24 '16 at 10:47
  • @MathiasMüller, no, if the input is a posted, and your XSLT would do e.g. ``, then using `exclude-result-prefixes` would not help to have the in scope namespaces copied from the input to the output being serialized. You would need to make sure you use ``. The same would be necessary if you want to use the `serialize` function, you would need to transform the input first to get rid of the in scope namespaces. `exclude-result-prefixes` only helps with namespaces in XSLT. – Martin Honnen Feb 24 '16 at 11:36
  • @MartinHonnen Thanks, I understood it now. – Mathias Müller Feb 24 '16 at 12:52
1

Michael Kay already gave the correct answer and I have accepted it. This is just to flesh out his comments. By

Run a transformation to remove the unwanted namespaces, then serialize the result.

he means applying a transformation like the following before calling serialize():

XSLT Stylesheet

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization"
    xmlns:ci="http://www.cichlids.com">

    <xsl:output method="xml" encoding="UTF-8" indent="yes" />

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:variable name="cichlid-without-namespace">
        <xsl:copy-of copy-namespaces="no" select="/ci:cichlids/cichlid"/>
    </xsl:variable>

    <xsl:template match="/ci:cichlids/cichlid">
        <xsl:variable name="serial-params">
            <output:serialization-parameters>
                <output:omit-xml-declaration value="yes"/>
            </output:serialization-parameters>
        </xsl:variable>
        <xsl:value-of select="serialize($cichlid-without-namespace, $serial-params/*)"/>
    </xsl:template>

</xsl:stylesheet>

XML Output

<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
    &lt;cichlid id="1"&gt;
        &lt;name&gt;Zeus&lt;/name&gt;
        &lt;color&gt;gold&lt;/color&gt;
        &lt;teeth&gt;molariform&lt;/teeth&gt;
        &lt;breeding-type&gt;lekking&lt;/breeding-type&gt;
    &lt;/cichlid&gt;
</ci:cichlids>
Mathias Müller
  • 22,203
  • 13
  • 58
  • 75