tags:

views:

30

answers:

1

I'm converting a set of XML documents from one format, which doesn't include namespace prefixes, to another, which does. Everything is relatively straightforward, but it's a bit repetitive in the XMLNS output. Below is an example.

(Very Simple) Input XML

<?xml version="1.0"?>
<a/>

XSLT

<!-- xmlns="http://www.w3.org/1999/xhtml" -->

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"

    xmlns:a="urn:data.test-a"
    xmlns:b="urn:data.test-b"
    xmlns:c="urn:data.test-c"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:local.test schema/test.xsd"

    version="1.0">

    <xsl:output method="xml" indent="yes" />

    <xsl:template match="/a">
        <xsl:element name="a:test-a">
            <!-- attempted fix -->
            <xsl:copy-of select="namespace::*"/>

            <!-- is there a better way to get this in there? -->
            <xsl:attribute name="xsi:schemaLocation">urn:local.test schema/test.xsd</xsl:attribute>

            <xsl:element name="b:test-b">
                <xsl:element name="c:test-c">content</xsl:element>
            </xsl:element>

        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Output

<?xml version="1.0"?>
<a:test-a
    xmlns:a="urn:data.test-a"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:local.test schema/test.xsd">
  <b:test-b xmlns:b="urn:data.test-b">
    <c:test-c xmlns:c="urn:data.test-c">content</c:test-c>
    <c:test-c xmlns:c="urn:data.test-c">content</c:test-c>
  </b:test-b>
  <b:test-b xmlns:b="urn:data.test-b">
    <c:test-c xmlns:c="urn:data.test-c">content</c:test-c>
    <c:test-c xmlns:c="urn:data.test-c">content</c:test-c>
  </b:test-b>
</a:test-a>

Desired Output

<?xml version="1.0"?>
<a:test-a
    xmlns:a="urn:data.test-a"
    xmlns:b="urn:data.test-b"
    xmlns:c="urn:data.test-c"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:local.test schema/test.xsd">
  <b:test-b>
    <c:test-c>content</c:test-c>
    <c:test-c>content</c:test-c>
  </b:test-b>
  <b:test-b>
    <c:test-c>content</c:test-c>
    <c:test-c>content</c:test-c>
  </b:test-b>
</a:test-a>

Basically, I want to consolidate the namespace attributes into the root element. I've been doing some research and I thought I had this locked up using as seen here. But, it's not having the desired effect; I assume I'm either using it incorrectly or am restricted by xsltproc's capabilities.

Performing a second pass to clean up the XMLNS entries would be a fine solution as well.

Also, in case it restricts a solution, I think my environment will be limited to XSLT 1.0.

Thanks for any tips.

PS. A smaller question is if there's a better way to get that schemaLocation attribute in the output, but that's minor.

+1  A: 

This is probably the shortest transformation that satisfies your requirements:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

    xmlns:a="urn:data.test-a"
    xmlns:b="urn:data.test-b"
    xmlns:c="urn:data.test-c"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:local.test schema/test.xsd">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="a">
        <a:test-a xmlns:a="urn:data.test-a"
           xmlns:b="urn:data.test-b"
           xmlns:c="urn:data.test-c"
           xsi:schemaLocation="urn:local.test schema/test.xsd">
            <b:test-b>
                <c:test-c>content</c:test-c>
            </b:test-b>
        </a:test-a>
    </xsl:template>
</xsl:stylesheet>

when this transformation is performed on the provided XML document:

<a/>

the wanted, correct result is produced:

<a:test-a xmlns:a="urn:data.test-a"
   xmlns:b="urn:data.test-b"
   xmlns:c="urn:data.test-c"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="urn:local.test schema/test.xsd">
   <b:test-b>
      <c:test-c>content</c:test-c>
   </b:test-b>
</a:test-a>

However, be warned: Placing all namespace nodes at the top element, even when they are not needed there, is not a recommended practice, because all namespace nodes are copied to all descendent elements and this leads to waste of a lot of memory.

Dimitre Novatchev
Ah, so you just directly insert them. Thanks for the tip. I can appreciate the memory concerns. At this stage the goal is to mirror the sample files. It'll be easy enough to switch to a more efficient, if also more verbose, option when it's ready for deployment.
fracai