|
dynamic namespaces with XSLT
I had an interesting problem to solve recently regarding Xml Schemas, XSLT and namespaces. The task was to use XSLT to create Xml Schema dynamically. This was something I had done before and figured I would pull upon previous work to give me a head start. The interesting part was the requirements around creating namespaces. Dynamically created namespaces can be tricky enough, especially if you are not yet using XSLT 2.0 as I was. The added feature was that I needed to use pre-defined namespace prefixes on those dynamically generated namespaces.
In short, I was tasked with generating namespaces in a resulting Xml Schema, based on data dynamically created at processing time, and with namespace prefixes predefined.
The tempation is to try something like this:
<xsl:attribute name="xmlns"><xsl:value-of select="$xmlns"/></xsl:attribute>
But that doesn't work. The solution is to create a dummy attribute and copy it to the result via the namespace axis and local-name.
In the scenario here, I want to create 3 namespaces, one for the default and target, a second for "common" components that are to be imported, and a third for "custom" components that are used for extensions.
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://xmlns.myexample.com/version3" targetNamespace="http://xmlns.myexample.com/version3" xmlns:common="http://xmlns.myexample.com/common/version3" xmlns:custom="http://xmlns.myexample.com/custom/version3"
elementFormDefault ="qualified"
attributeFormDefault="unqualified">
<xsd:import
schemaLocation="common.xsd"/>
<xsd:import
schemaLocation="custom.xsd"/>
</xsd:schema>
Here is the XSLT. I used result tree fragments here, so make sure your processor supports it.
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<!-- the version of the resulting schema -->
<xsl:variable name="version">version3</xsl:variable>
<!-- the version of the common components imported schema -->
<xsl:variable name="commonVersion">version2</xsl:variable>
<!-- the version of the custom components imported schema -->
<xsl:variable name="customVersion">version1</xsl:variable>
<!-- the targetNamespace -->
</xsl:variable>
<!-- the common components namespace -->
</xsl:variable>
<!-- the custom components namespace -->
</xsl:variable>
<!-- we cannot do this:
="http://xmlns.myexample.com/{$commonVersion}"
we must add the namespaces generated above to dummy attributes -->
<xsl:variable name="default-ns-node">
<xsl:element name="default-ns-element" namespace="{$target}"/>
</xsl:variable>
<xsl:variable name="common-ns-node">
<xsl:element name="common-ns-element" namespace="{$common}">
<xsl:attribute name="common:dummy" namespace="{$common}"/>
</xsl:element>
</xsl:variable>
<xsl:variable name="custom-ns-node">
<xsl:element name="custom-ns-element" namespace="{$common}">
<xsl:attribute name="custom:dummy" namespace="{$custom}"/>
</xsl:element>
</xsl:variable>
<!-- ========================================== -->
<xsd:schema>
<!-- since we cannot do xsl:copy, we simply copy the namespace axis referring to the local-name only -->
<xsl:copy-of select="$default-ns-node/*/namespace::*[local-name()='']"/>
<xsl:copy-of select="$common-ns-node/*/namespace::*[local-name()='common']"/>
<xsl:copy-of select="$custom-ns-node/*/namespace::*[local-name()='custom']"/>
<!-- form defaults and targetNamespace attributes don't need special treatment, so they can be simply added with xsl:attribute -->
<xsl:attribute name="elementFormDefault">qualified</xsl:attribute>
<xsl:attribute name="attributeFormDefault">unqualified</xsl:attribute>
<xsl:attribute name="targetNamespace"><xsl:value-of select="$target"/></xsl:attribute>
<!-- ========================================== -->
<!-- the namespace attribute in import statements can use variables directly -->
<xsd:element name="root">
<xsd:complexType>
|