Converting XML Schemas to Schematron (#15): Qname madness

By Rick Jelliffe
August 5, 2009

XML Schemas has at least three different methods for deciding how to map from a name token to find the appropriate qualified name: one is used for types (such as in base attributes) and pretty much uses the XML Namespaces mechanism, the others are for tracking down element and attribute names, which is where elementFormDefault and form come into play.

One of my stock answers to the question. why doesn't Schematron use XML Namespaces in the 'standard' way is to point out that there is no standard way, as far as interpreting Qnames in attribute values.

The XML Namespaces specification is limited to figuring out how to make a qualified name (a two-part name consisting of a local part and a URI, together nicknamed a 'qname') from the prefixed or unprefixed names found in markup as element names and attribute names. It does not standardize how to treat qnames when you find them in attribute values.

XSLT went one way, and does use the namespace infrastructure. This causes problems in real life, because the issue of default (unprefixed) namespaces comes into play.

I think data and markup should be separate worlds, and that the defaulting issue (how to treat qnames in attribute values which have no prefixes) is an unnecessary carbuncle and distraction for the developer. So Schematron has an element ns that sets the bindings between prefixes and URIs for interpreting XPaths. (This also insulates Schematron users from the difference in implementations as well: the namespace axis was patchily supported in early XSLT1 (MSXL, Mozilla XSLT) and not mandatory for XQuery2.)

And XSD has its variety of mechanisms.

I have recently being doing some more work on the XML Schema to Schematron converter, and one of the first issues to come up is more proper handling of namespaces. (And to take the opportunity to start refactoring the code: while the over all pipeline and top-level structure is pretty good, some of the individual templates are ...shall we say... overripe for refactoring: this is actually quite important at the stage, because unless the code is in small enough chunks, it is too much to expect interested hackers to add incremental improvements, which needs to be a goal of this kind of open source development.)

Here is the initial code for handling namespaces better: the idea is to convert unprefixed names that use some default namespace to prefixed names: then downstream code does not need to be concerned with namespace defaults.

An initial stab at the rules for prefixing element names is:


<!-- Takes a name and if it has no prefix then adds the appropriate one -->

<xsl:template name="qualify-element-name">
<xsl:param name="name"/>
<xsl:choose>
<!-- already prefixed: just keep it -->
<xsl:when test="contains($name, ':')">
<xsl:value-of select="$name"/>
</xsl:when>

<!-- no namespace therefore no prefix -->
<xsl:when test="ancestor::namespace/@uri=''
or not(ancestor::namespace) or not(ancestor::namespace/@uri)">
<xsl:value-of select="$name"/>
</xsl:when>

<!-- Explicit qualification -->
<xsl:when test="@form='qualified'">
<xsl:value-of select="concat(ancestor::namespace/@prefix, ':', $name)"/>
</xsl:when>

<xsl:when test="ancestor::schema/@elementFormDefault='qualified'">
<xsl:value-of select="concat(ancestor::namespace/@prefix, ':', $name)"/>
</xsl:when>


<xsl:when test="@form='unqualified'">
<xsl:value-of select="$name"/>
</xsl:when>

<xsl:when test="ancestor::schema/@elementFormDefault='unqualified'">
<xsl:value-of select="$name"/>
</xsl:when>

<!-- DEFAULT -->
<xsl:otherwise>
<xsl:value-of select="concat(ancestor::namespace/@prefix, ':', $name)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Actually, I couldn't really understand one part of the spec: I sent in a comment to the W3C XML Schema comments list about what I thought the problem was. It is just as well Oracle is involved in the XML Schema specification, because you need a bloody oracle for that document.

An initial stab at the rules for prefixing type names is:

<!-- This template takes a type name and returns the prefixed form, if available.
This is to cope with, in particular, the case where the XSD namespace is the default
namespaces, and therefore base types are written without prefixes.
Thanks to David Carlisle for a code hint. -->
<xsl:template name="qualify-type-name">
<xsl:param name="name"/>

<xsl:choose>
<!-- already prefixed: just keep it -->
<xsl:when test="contains($name, ':')">
<xsl:value-of select="$name"/>
</xsl:when>

<!-- no prefix and no default namespace -->
<xsl:when test="namespace-uri-for-prefix('',.) = ''">
<xsl:value-of select="$name"/>
</xsl:when>

<!-- no prefix therefore use default namespace-->
<!-- default namespace isxs: -->
<xsl:when test="namespace-uri-for-prefix('',.)='http://www.w3.org/2001/XMLSchema'">
<xsl:value-of select="concat('xs:', $name)"/>
</xsl:when>

<!-- default namespace is used and it is not the xsd -->
<!-- TODO: do something clever here, like track down the appropriate prefix -->
<xsl:otherwise>
<xsl:value-of select="$name"/>
v/xsl:otherwise>
</xsl:choose>

</xsl:template>


A new beta version of the converter, 0.5, with these improvement will be up at schematron.com this week.


You might also be interested in:

News Topics

Recommended for You

Got a Question?