0

Is there any way, in XSLT, to control the choice of namespace prefixes in output elements?

In my particular case, I in effect want to transform input which looks like

<h:html xmlns:h='http://www.w3.org/1999/xhtml'
        xmlns:m='http://www.w3.org/1998/Math/MathML'>
....
<h:p>Equation: <m:math>...</m:math></h:p>

into

<html xmlns='http://www.w3.org/1999/xhtml'>
....
<p>Equation: <math xmlns='http://www.w3.org/1998/Math/MathML'>...</math></p>

That is, an identity transform which simply changes the namespace prefixes, to use the default namespace for XHTML and MathML elements as appropriate.

This is a tidy-up step at the end of a pure-XML workflow. The above are of course equivalent in XML terms, and thus in XHTML terms, but browsers don't always seem to know that (in my not very systematic tests, Firefox manages both of the above, in the sense of rendering them as maths – well done Firefox! – Safari manages the second but not the first, Chrome doesn't manage either; I'm actually targeting EPUB readers, but it seems wise to be pessimistic about the XHTML parsers there). Trying with the <output method='html'/> XSLT element doesn't make a difference in the output. The XHTML Compatibility Guidelines don't mention namespaces, rather surprisingly. Adding doctype declarations, and even the <meta http-equiv=''...> hack to hint application/xhtml+xml, doesn't seem to make any difference to the browsers' behaviour.

There's nothing I can see in the XSLT 1.0 spec that controls this. The namespace aliasing mentioned there is addressing a different problem; playing with the default namespaces in the XSLT doesn't provide any hints that libxslt seems inclined to take. Other stackexchange questions (eg this one or this one) seem to be largely misunderstandings of XSLT and namespaces. I'm sure I have managed to achieve this at some point in my long XSLT past, but if I did, I can't resurrect it.

I would greatly prefer a solution in XSLT 1.0 simply because I have the tooling and experience available to use that quickly, in libxslt and xsltproc (Saxon is a truly wonderful thing, but I'm reluctant to pay the Java startup cost for possibly lots of successive transformations). This may be the thing that forces me to a later XSLT version, of course, if a later version is truly the only thing that can help.

From a (not very thorough) look at the XSLT 3 spec (eg section 11.1), I can't see anything which obviously addresses this.

If I'm barking up the wrong tree, or if EPUB processors are known to be consistently more clueful about namespaces, so that I'm actually addressing the wrong part of the problem, I'm open to that information as well.

Norman Gray
  • 11,978
  • 2
  • 33
  • 56

1 Answers1

1

You would need a transformation

<xsl:template match="*">
  <xsl:element name="{local-name()}" namespace="namespace-uri()">
     <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>

Set up the identity transformation template for the rest.

And in XSLT 1 you kind of rely on the mercy of the XSLT processor and its serializer to get the output you want but I think for the above template you can expect some consistency even in the 1.0 world.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • That looks very promising, and it does work in the simple case I've tried. The real code is of course slightly more complicated than that in my question, but I'll have a go, and come back to this answer. – Norman Gray Dec 10 '20 at 18:19
  • This does indeed work very well in practice, in the sense that this seems to give enough of a hint to the serialiser. The serialiser doesn't take the hint fully (as you suggest): any templates processed within this construction which contain eg `...` are unfortunately serialised with that prefix, rather than the default one, which is still in scope at that point. That means I can't leave the XSLT elements in the default namespace, but... I'll live. Thanks, Martin, for reassuring me I'm not missing anything. – Norman Gray Dec 16 '20 at 17:35