views:

238

answers:

1

I'm transforming an XML Schema using XSLT 2.0. The first schema (s1.xsd) imports a second schema (s2.xsd) as follows:

Content of s1.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema.xsd"
    xmlns:ns1="URI1" targetNamespace="URI2" 
    elementFormDefault="qualified" attributeFormDefault="unqualified">
    <import namespace="URI1" schemaLocation="s2.xsd"/>
    <element name="element1"/>
    <element name="element2"/>
</schema>

and content of s2.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema"
    xmlns:ns1="URI1" targetNamespace="URI1">
    <attribute name="attr1"/>
<schema>

My XSLT declares the XS namespace as follows:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;  

I would like to merge the nodes of s2.xsd into the <schema>-element of s1.xsd. So far, I've tried

<xsl:template name="merge_imported_schemas">
    <xsl:for-each select="/schema/import[@namespace = //namespace::*]">
        <!-- file exists? -->
        <xsl:choose>
            <xsl:when test="boolean(document(@schemaLocation))">
                <!-- schema found -->
                <xsl:copy-of select="document(@schemaLocation)/*/node()"/>
            </xsl:when>
            <xsl:otherwise>
                <!-- schema not found -->
                <xsl:message terminate="yes">
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each>
</xsl:template>

but I don't get the desired result. Could anyone please tell me what I'm doing wrong? I suspect there is a namespace-collision here, but honestly I find using namespaces a little confusing. Thanks!

+2  A: 

You need to qualify the elements in your XPath. At the moment select="/schema/import[@namespace = //namespace::*]"> doesn't match anything at all, because there is no element /schema. The XPath is trying to match elements with no namespace.

Change it to select="/xs:schema/xs:import[@namespace = //namespace::*]"> and it should work.

Remember, namespace prefixes are an alias for the namespace URI, and if you have a default namespace (as in your xsd files), elements with no prefix are still namespace-qualified.

As an aside, instead of <xsl:for-each select="/schema/import[@namespace = //namespace::*]">, you might have more success using <xsl:apply-templates select="/xs:schema/node()", and defining different templates for the different kinds of node that you wish to copy into the output tree.

Paul Butcher
Thanks Paul, I forgot. :) Having augmented my XPath query with the correct namespace prefixes, I decided to try to count the number of nodes. Like this:Before: <xsl:value-of select="count(xs:schema/*)"/><xsl:call-template name="merge_imported_schemas"/>After: </xsl:text><xsl:value-of select="count(xs:schema/*)"/>Unfortunately, they both give me the same number (44; which incidently is only 2 more than the answer to everything).
conciliator
From the information given, it looks like you might be discarding the elements from the original schema.
Paul Butcher
Well, that could be it. However, I changed the <xsl:copy-of ...> with <xsl:value-of select="document(@schemaLocation)/*/*"/> (counting the number of elements right below /schema in s2.xsd). It says 22... I'm kind'a lost.
conciliator
`copy-of` was most likely correct, but as far as I can see, you are only copying elements from the imported document into the output document, and not copying any elements from the source document.
Paul Butcher
I think, sir, that you are absolutely right. I've misunderstood the effect of copy-of. (+1)
conciliator