Search code examples
xmlxsltxsitype

How to emit an xsi:type attribute within a stylesheet?


I have an XSLT stylesheet that needs to emit an XML fragment that looks like this (the "..." bits are elided for brevity) :

<MyOuterType xmlns:xsi="..." xsi:type="foo:MyInnerType" xmlns:foo="..."/>

The stylesheet looks like this :

<xsl:stylesheet xmlns:foo="..." xmlns:xsi="...">
  ...
  <xsl:template match="...">
    <xsl:element name="MyOuterType">
      <xsl:attribute name="xsi:type">foo:MyInnerType</xsl:attribute>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

The XSLT processor's output looks like this :

<MyOuterType xmlns:xsi="..." xsi:type="foo:MyInnerType"/>

In other words, it's missing the xmlns:foo declaration. In fact the declaration is completely absent from the emitted XML, despite it being declared in the stylesheet. This messes things up later, when I need to deserialize the emitted XML.

How can I get my xmlns:foo to appear in the emitted XML? The processor implements XSLT 1.0.


Solution

  • It depends whether you know the namespace you want to generate statically, or whether it's based on information in the input document.

    If you know it statically, just use a literal result element instead of xsl:element:

    <MyOuterType xsi:type="foo:MyInnerType" xmlns:foo="....."/>
    

    If it's a dynamic decision, it's a bit more complicated. XSLT 2.0 has an xsl:namespace instruction to generate namespaces in much the same way that xsl:attribute generates attributes. In XSLT 1.0, the trick is to create an element that contains the required namespace, and then use xsl:copy to copy the namespace node:

    <xsl:variable name="dummy">
      <xsl:element name="foo:dummy" namespace="{$param}"/>
    </xsl:variable>
    <MyOuterType xsi:type="foo:MyInnerType">
      <xsl:copy-of select="exsl:node-set($dummy)/*/namespace::foo"/>
    </MyOuterType>
    

    Unfortunately there are one or two not-quite-XSLT-1.0 processors out there that don't implement namespace nodes correctly, so exercise caution.