Namespace prefix normalizer

This is an (E)XSLT 1.0 stylesheet. An XSLT 2.0 version is on my TODO list. now available!

It has been asserted that a "best practice" for XML Namespaces is to define all namespace prefix bindings at the document (root) element context. A program that forces all documents to follow this practice, i.e. a "namespace normalizer", might be a useful thing to have around:

This stylesheet uses the exsl:node-set() extension function. This works fine with Saxon and should work with any other conformant XSLT processor that also implements the node-set() function in the EXSLT "common" module. To make it work with Xalan or other processors that support their own node-set() function, simply change the namespace URI of the function (and possibly its local name, e.g. Xalan's is "nodeset()"). Note: I don't remember if I've tested this on Xalan (with the appropriate changes).

The stylesheet itself is heavily commented throughout, providing a walk-through of the algorithm employed. For additional context, see Jonathan Borden's prose description of the same basic algorithm.

With respect to the XPath data model, this transformation does not simply produce an alternative serialization, i.e. it is indeed a transformation. In the XPath data model, prefixes are significant and exactly where namespace bindings are in scope, is significant. Those and only those two things are precisely what this transformation (possibly) changes. In other words, if your data model is not quite as low-level as XPath's, this transformation indeed provides you an alternate serialization that perhaps is more human-readable, and, in any case, follows the "best practice" described above.

Conversely, it must be used with care on XSLT stylesheets, XSD schemas, and any other grammar that uses in-scope namespace bindings to expand QNames in attribute values or element content. It will not necessarily break them, but it could. In general, use of this stylesheet on any source document that uses QNames in attribute values is discouraged.

Below is an example of what this transformation does. Note that prefixes can be removed ("abc") and created ("d3e10").

BEFORE:

<foo xmlns="default1">
  <abc:bar xmlns="default2andfoo" xmlns:abc="default1">
    <bat xmlns:foo="default2andfoo" foo:bar="value">
      <bang xmlns="default3">
        <abc:hi xmlns:xyz="unused-uri"/>
      </bang>
    </bat>
  </abc:bar>
</foo>

AFTER (serialized by Saxon):

<foo xmlns="default1" xmlns:foo="default2andfoo" xmlns:d3e10="default3" xmlns:xyz="unused-uri">
  <bar>
    <foo:bat foo:bar="value">
      <d3e10:bang>
        <hi/>
      </d3e10:bang>
    </foo:bat>
  </bar>
</foo>

Contact: evan@lenzconsulting.com; +1 (206) 898-1654
Copyright © 2023 — Lenz Consulting Group, Inc.