views:

65

answers:

2

I am writing an XSL template that pulls data from many secondary sources. An example secondary document looks like this:

<toplevel xmlns:foo1="http://foo1"&gt;
 <path xmlns="http://foo1"&gt;
   <mytag>bar</mytag>
  </path>
</toplevel>

In the XSL, I am doing this:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:foo1="http://foo1"
 exclude-result-prefixes="foo1">
  <xsl:variable name="secondary1" select="document('secondary1.xml')/toplevel"/>
  <foo>
    <xsl:value-of select="$secondary1//foo1:path/foo1:mytag"/>
  </foo>
</xsl:stylesheet>

With a lot of secondary sources, each of which uses a different namespace, prefixing every single tag is tedious, and that much repetition can't be the right thing to do anyway. Is there a way to use document() such that the namespace of the imported node-set is stripped (or to achieve the same effect another way)?

+2  A: 

In essence, a node with a namespace is an entirely different animal than a node with another namespace - even if they happen to share the same local name. (This is much the same way namespaces work everywhere else - there is really no easy way of "ignoring" namespaces. Think of ignoring namespaces when referring to classes in C#.)

The clean approach would be to mention each namespace you might encounter in the XSLT and work with prefixes, even if it seems repetitive.

The not-so-clean way is this:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:variable name="secondary1" select="document('secondary1.xml')"/>

  <xsl:template match="/">
    <foo source="1">
      <xsl:value-of select="
        $secondary1//*[local-name() = 'path']/*[local-name() = 'mytag']
      "/>
    </foo>
  </xsl:template>
</xsl:stylesheet>

This is not really more pleasing to the eye than working with prefixes, it's longer and harder to read, it is ambiguous, and last but not least - it is slower because the engine must test a predicate on every step on the XPath. Take your pick.

Tomalak
+3  A: 

In XPath/XSLT 1.0, to select a namespace-qualified element by name, you have to use a prefix. In XSLT 2.0, you can use the xpath-default-namespace feature, which allows you to set the default namespace for XPath expressions, so you don't have to use prefixes anymore. See XSLT 2.0: xpath-default-namespace for more details. You can use this attribute on any element in your stylesheet, and it takes effect for all descendant elements unless overridden. (Qualify it with xsl: when you want to put it on a non-XSLT element, i.e. a literal result element.)

In XPath 1.0, you can also select elements by local name rather clumsily using, for example, *[local-name() = 'path']/*[local-name() = 'mytag']. In XPath 2.0, for greater succinctness, you can use namespace wildcards, as in *:path/*:mytag, as described here. This was a somewhat controversial addition, since it seems to encourage and/or justify the same dubious use of namespaces that your system is apparently employing.

Evan Lenz
+1 for adding the 2.0 subtleties.
Tomalak