views:

321

answers:

2

I am working on creating an XSL to format some incoming XML from an API I am integrating. The xml I receive looks like:

<items>
  <item xmlns="http://www.tempuri.org/Item.xsd"&gt;
    <key>value</key>
  </item>
  <item>
    <key>value</key>
  </item>
  <item xmlns="http://www.tempuri.org/Item.xsd"&gt;
    <key>value</key>
  </item>
</items>

Some of the "item" nodes have the "xmlns" attribute defined, while others do not. When I attempt to iterate through the results in my XSL, it is not finding the nodes that have the xmlns attribute defined.

<xsl:for-each select="item">
  <xsl:value-of select="key" />
</xsl:for-each>

I am sort of new to the whole XSL thing, so I'm not sure what I am doing wrong.

+1  A: 

You'll need to define the same namespace in your <xsl:stylesheet> definition.

Then:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns="http://www.tempuri.org/Item.xsd"
    extension-element-prefixes="exsl"
    encoding="utf-8">

. . .

<xsl:for-each select="item|ns:item">
    <xsl:value-of select="key|ns:key"/>
</xsl:for-each>

Really you should try and use the same namespace for things that are the same.

Kyle Butt
Thank you. This worked great. However, it brings up another question. Once I am in the "ns:item" element, is there a way I can access each "key" by typing "key" instead of "ns:key"?
LGFaler
no, the way that namespaces are defined, key and ns:key are different. By changing the default namespace on an item, you change the namespace on the keys inside of it.
Kyle Butt
A: 

First off, if you don't understand how XML namespaces work and what they mean, you're going to have no end of problems. This issue you're struggling with is the tip of the iceberg.

That said, you can deal with namespaces the way Kyle Butt suggested, assigning a transform-wide mapping of prefixes to namespaces in the stylesheet element and then using the prefixes in your XPath expression.

But what if you don't know the namespaces? Then you have to use a blunter instrument:

<xsl:for-each select="*[local-name()='item']">
  <xsl:value-of select="*[local-name()='key']"/>
</xsl:for-each>

Using local-name() ignores namespaces completely. But you can't use it in a node test (which has to be a name like foo or a qualified name like x:foo), you can only use it in a predicate. Which is why the unlovely *[local-name()='foo'] is the only way to do it.

Robert Rossney