views:

93

answers:

1

So I have an input file that uses my company's namespace in the default namespace (xmlns="companyURL") but I want my output file to use something other than the default namespace (xmlns:cmp="companyURL"). So I construct my file using the cmp namespace, but then I want to copy some of the inner elements:

<xsl:element name="cmp:container">
  <xsl:for-each select="foo">
    <xsl:copy-of select="." />
  </xsl:for-each>
</xsl:element>

Unfortunately, what this does is define the default namespace for each of those inner elements, making the file incredibly verbose and ugly. Simplified example:

Source:

<foo xmlns="companyURL">
  <num1>asdf</num1>
  <num2>ghjkl</num2>
</foo>

Turns into:

<cmp:container xmlns:cmp="companyURL">
  <num1 xmlns="companyURL">asdf</num1>
  <num2 xmlns="companyURL">ghjkl</num2>
</cmp:container>

Of course, companyURL is big and long and ugly, and it's the same in both places, so I would prefer the above result to just be the following:

<cmp:container xmlns:cmp="companyURL">
  <cmp:num1>asdf</cmp:num1>
  <cmp:num2>ghjkl</cmp:num2>
</cmp:container>

Is there an easy way to do this, or should I convert everything under the cmp namespace to the default namespace? I would prefer to use the explicit namespace naming if possible, it aids in understanding the XSLT in my experience.

+2  A: 

This transformation:

 <xsl:template match="*">
     <xsl:element name="cmp:{name()}" namespace="CompanyURL">
       <xsl:copy-of select="@*"/>
       <xsl:apply-templates/>
     </xsl:element>
 </xsl:template>
 <xsl:template match="/*">
     <cmp:container xmlns:cmp="CompanyURL">
       <xsl:copy-of select="@*"/>
       <xsl:apply-templates/>
     </cmp:container>
 </xsl:template>
</xsl:stylesheet>

when performed on the provided XML document:

<foo xmlns="companyURL">
  <num1>asdf</num1>
  <num2>ghjkl</num2>
</foo>

produces the wanted, correct result:

<cmp:container xmlns:cmp="CompanyURL">
   <cmp:num1>asdf</cmp:num1>
   <cmp:num2>ghjkl</cmp:num2>
</cmp:container>
Dimitre Novatchev
Why do you have `<xsl:copy-of select="@*"/>` in your XSLT?
adam_0
@adam_0: This copies all attributes of the element. In your concrete example there are no attributes, but if we want to use this code as a general convertor, it must be able to convert correctly all documents -- including such in which there are elements with attributes.
Dimitre Novatchev
My XML Schema specifies that there will be no attributes, so I'm guessing it's fine to leave that line out?
adam_0
If you are 100% sure you can remove it, but leaving it doesn't hurt anything -- simply no attributes will be copied. It is always better to have more generic code. Imagine that tomorrow there is a change in the schema and there would be attributes. Then you'd need to add copying of attributes. In case you used the more generic code from the very start, this change in schema will not affect you at all.
Dimitre Novatchev
Thanks for your help and powerful, concise answers. +1
adam_0
A follow-up I just thought about: could I make this work for any namespace prefix by changing the top template line of `<xsl:element name="cmp:{name()}" namespace="CompanyURL">` to `<xsl:element name="cmp:{local-name()}" namespace="CompanyURL">` (effectively trimming off any namespace prefix and putting on my own)?
adam_0
@adam_0: Yes, this is the way to do prefix-renaming.
Dimitre Novatchev