Basically, what happens when you have two namespace declarations for the
same namespace URI is (a) your request to exclude a result prefix is
actually a request to exclude all namespace nodes for that namespace
URI; (b) the serializer (according to the XSLT 1.0 spec) is allowed to
add extra namespace nodes, and is required to add them where the
namespace URI is actually used. One would like the serializer to add the
minimum set of necessary namespace nodes at this stage, but Saxon is
using the rule that if the namespace URI is used in the result, then it
adds back all the namespace nodes for that URI, regardless of prefix.
This is inelegant, but not non-conformant. The serializer is actually
allowed to output any namespace declarations it chooses.
Michael Kay
-----Original Message-----
From: owner-xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
[mailto:owner-xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com] On Behalf Of
Lars Huttar
Sent: 27 October 2003 17:51
To: XSL-List (E-mail)
Subject: [xsl] Saxon fails to exclude result prefixes?
Hi all,
Maybe I'm missing something, but it sure seems like Saxon
isn't doing what it's supposed to in this case.
I'm using Saxon652. (But I have just now upgraded to 6.5.3
and found the same behavior.)
Can anybody confirm if this is a bug?
The situation:
I have a stylesheet that does an identity
transform for the most part, but discards some elements
and adds others. All elements in the input and the output
are in the namespace whose URI is "http://www.sil.org/namespace/gem".
In the input, the outermost element, <system-model>,
has a namespace pseudo-attribute on it:
<system-model xmlns="http://www.sil.org/namespace/gem">
and no prefixes or other namespace declarations are used.
I would like the output to be the same way.
The problem:
The nodes that are copied via <xsl:copy> appear the same in
the output as they do in the input, i.e. none of them have
namespace declarations except the outermost. However, the
nodes I create as literal result elements have a superfluous
namespace declaration, e.g.
<access xmlns:gem="http://www.sil.org/namespace/gem"
role="guest" update="no" read="yes"/>
This happens even though (and whether or not) I put
xsl:exclude-result-prefixes="gem"
on the literal result element <access>.
My reading of the spec leads me to believe that putting
xsl:exclude-result-prefixes="gem"
here, or
exclude-result-prefixes="gem"
on the <xsl:stylesheet> element, should prevent unnecessary
declarations of the "gem" prefix in the output. (I have tried both.)
When I try this stylesheet with this input in Xalan or MSXSL,
I get what I wanted: <access> with no "gem" prefix declaration.
Is this indeed a bug in Saxon?
I checked the list of changes 6.5.2 - 6.5.3, the
list of known limitations of 6.5.3, and the Saxon bug tracker
pages on Sourceforge, and didn't see this mentioned as a bug.
If this is not a bug and I'm just misunderstanding what
(xsl:)exclude-result-prefixes is supposed to do, I would
appreciate correction.
The files in question will be available at
http://www.huttar.net/lars-kathy/test/Ethnologue.xml: the
input document;
.../role-extractor.xsl: the stylesheet;
.../system-model.dtd: DTD for the input document;
.../msxslout.xml: the output when processed by msxml;
.../saxonout.xml: the output when processed by Saxon 653;
but at the moment I can't get the server to let me upload them.
So here's the stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<!-- role-extractor.xsl
Input: a system model conforming to system-model.dtd
Parameter: a role (e.g. 'guest' or 'admin')
Output: the system model, filtered to include only items
accessible to the given role.
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gem="http://www.sil.org/namespace/gem"
xmlns="http://www.sil.org/namespace/gem">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes"/>
<xsl:param name="role" select="'admin'" />
<!-- global variables -->
<xsl:variable name="role-node"
select="/*/gem:columnFour/gem:roles/*[(_at_)id = $role]
| /*/gem:columnFour/gem:roles/gem:admin[$role = 'admin']" />
<xsl:variable name="default-read">
<xsl:choose>
<xsl:when test="$role-node/@read = 'all'">yes</xsl:when>
<xsl:otherwise>no</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="default-update">
<xsl:choose>
<xsl:when test="$role-node/@update = 'all'">yes</xsl:when>
<xsl:otherwise>no</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Top-level template -->
<xsl:template match="/">
<xsl:if test="not($role-node)">
<xsl:message terminate="yes">Role "<xsl:value-of
select="$role" />" unknown.</xsl:message>
</xsl:if>
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<!-- Discard <attribute>s that the given role doesn't have
access to. Modify others. -->
<xsl:template match="gem:attribute">
<xsl:choose>
<!-- Discard <attribute>s that the given role
explicitly has no access to. -->
<xsl:when test="gem:access[(_at_)role = $role and @read = 'no']" />
<!-- Discard <attribute>s that the given role doesn't
have access to by default. -->
<xsl:when test="$default-read = 'no' and
not(gem:access[(_at_)role = $role and @read = 'yes'])" />
<!-- The given role has read access to this attribute,
so process the attribute. -->
<xsl:otherwise>
<xsl:copy>
<!-- Copy everything except <access> elements -->
<xsl:apply-templates
select="@*|node()[not(self::gem:access)]" />
<!-- create an <access> element only if needed
(i.e. if this attribute is read-only) -->
<xsl:if test="($default-update = 'no' and
not(gem:access[(_at_)role = $role and @update =
'yes'])) or
gem:access[(_at_)role = $role and
@update = 'no']">
<access xsl:exclude-result-prefixes="gem"
role="{$role}" update='no' read='yes' />
<!-- @read is redundant but is there to conform to DTD -->
</xsl:if>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="gem:roles">
<!-- Discard all of <roles>' child elements except those
that are required by the DTD. -->
<xsl:apply-templates select="gem:guest | gem:admin" />
</xsl:template>
<!-- Everything else is preserved as-is. -->
<!-- identity transform --> <!-- Problem: namespace needs
to be preserved too. -->
<xsl:template match="@*|node()" priority="-1">
<xsl:copy>
<!-- Debugging:
<xsl:message>Copying node <xsl:value-of select="name()"
/> with namespace-URI <xsl:value-of select="namespace-uri()"
/></xsl:message> -->
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And here's an excerpt from the input XML:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE system-model SYSTEM "system-model.dtd">
<system-model xmlns="http://www.sil.org/namespace/gem">
<name>Ethnologue</name>
...
<columnOne>
<object id="d.ECoun" independent="yes" stage="phase1">
<name>Ethnologue Country</name>
<definition>The
Ethnologue description of a country of the world.</definition>
<attribute size="35" type="phrase">
<name>Print_Name</name>
<definition>LNAM -
"Print:" format Country Name</definition>
</attribute>
...
</object>
</columnOne>
<columnFour>
<roles>
<guest id="guest" read="all" update="none"/>
<admin read="all" update="all"/>
</roles>
</columnFour>
</system-model>
Expected output: the <attribute> whose <name> is Print_Name
should have an <access> element that looks like this:
<access role="guest" update="no" read="yes"/>
but instead I get this with Saxon:
<access xmlns:gem="http://www.sil.org/namespace/gem"
role="guest" update="no" read="yes"/>
Thanks,
Lars
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list