views:

42

answers:

1

hi,

how do I read namespaces from the XML document?

<fx:FIBEX xmlns:fx="http://www.asam.net/xml/fbx" xmlns:can="http://www.asam.net/xml/fbx/can" xmlns:flexray="http://www.asam.net/xml/fbx/flexray"      
    xmlns:ho="http://www.asam.net/xml" xmlns:ni="http://www.ni.com/xnet"    
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     
    xsi:schemaLocation="http://www.asam.net/xml/fbx/all http://www.asam.net/xml/fbx/2_0_1/xsd/fibex4multiplatform.xsd"     
     VERSION="2.0.1">

How can I read the namespaces from this root element?

In fact the ultimate question is "how to retrieve a node using xPath like

//fx:ELEMENTS/fx:CLUSTERS/fx:CLUSTER 

?

I have tried XmlDocument.SelectSingleNode() with that string but that gave me some error about XmlNamespaces and I found on google that you need to have an XmlNamespaceManager in place.

+1  A: 

Your question actually seems to include two different questions:

  1. How to select elements that are declared in some namespace?
  2. How to select the namespace nodes of an element?

Question 1

In order to select nodes that are in some namespace you need to specify a prefix-namespace mapping. All XPath processors allow this but how it is done depends on the implementation. Generally this means creating a pair - a prefix and a URI - and then adding this pair as a namespace context. The URI is important. It needs to be exactly the same than the namespace URI of your element. The prefix doesn't need to be the same that was used in the document, it can be anything but you need a prefix even if the element was in default namespace and therefore unprefixed.

After your XPath resolver is aware of the needed namespaces you can select elements just as you did in your question: //fx:ELEMENTS/fx:CLUSTERS/fx:CLUSTER. Make sure that you are using the same prefix you used in the mapping (if the prefix differs from the one in the document). Note, that in XPath expressions, elements without a prefix are always assumed to have no namespace, so you need to use namespace prefixes in your XPath expression even when selecting elements from a default namespace.

For dirty hacks you can also try to avoid using element names completely. In Xpath * matches all element nodes and node() all nodes. You could narrow down the selection with creative use of predicates that use functions like name(), local-name(), position() or last(). For example XPath expression //*[name()='fx:CLUSTER'] selects all elements that have prefix "fx" and local name "CLUSTER" and this works even without the prefix-namespace mapping. Beware though that in XML, prefixes are not guaranteed to stay the same and they also can be reused/overridden in a document so that same prefix refers to different URIs in different parts of a document.

Question 2

Namespace nodes are selected using namespace:: axis. Local part of the name of the namespace node is the namespace prefix and the string-value is the namespace URI. Node for the default namespace has an empty name. You can select all the namespace nodes with URI http://example.com/ns with XPath expression //namespace::*[.=http://example.com/ns] and to select all the namespace nodes with prefix "px:" use //namespace::*[name()='px'] . Just like with attribute nodes, a namespace node is not a child of the element it belongs to, yet the element is the parent of that namespace node. Use parent:: axis to get the owner of the namespace node.

Namespaces need not to be declared in the root element (although this is common and highly reasonable) but they can be declared in any element. Searching for namespace nodes of the root element doesn't guarantee that you know all the namespace URIs and prefixes used in this document.

The catch is that all elements have one or more namespace nodes irrespective of whether the element has explicit namespace declarations or not. Elements have a set of namespace nodes, one for the default namespace if it is in scope for this element and one for each prefix that is in scope for this element, including the xml prefix. Below is a code example of the namespace nodes in different situations.

This example has 3 files: an XML document with different namespaces, an XSLT document that prints the values of namespace nodes for every element, and the output of the XSLT document.

Processed XML document

<root xmlns:ns1="http://example.com/namespace-1"&gt;
 <nothing/>
 <default xmlns="http://example.com/default"&gt;
  <override xmlns=""/>
 </default>
 <ns1:namespace-1 ns1:attribute-1="x" attribute-2="y"/>
 <ns2:namespace-2 xmlns:ns2="http://example.com/namespace-2"/&gt;
</root>

XSLT document

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

 <xsl:output encoding="utf-8" method="text"/>

 <xsl:template match="/">
  <xsl:text>Find and print all element names and namespace nodes&#10;&#10;</xsl:text>
  <xsl:apply-templates select="//*|//@*"/>
  <xsl:text>End of tests&#10;</xsl:text>
 </xsl:template>

 <xsl:template match="*|@*">
  <xsl:text>name          = "</xsl:text>
  <xsl:value-of select="name(.)"/>
  <xsl:text>"&#10;</xsl:text>
  <xsl:text>namespace URI = "</xsl:text>
  <xsl:value-of select="namespace-uri(.)"/>
  <xsl:text>"&#10;</xsl:text>
  <xsl:text>This node has </xsl:text>
  <xsl:value-of select="count(./namespace::*)"/>
  <xsl:text> namespace nodes:&#10;</xsl:text>
  <xsl:for-each select="./namespace::*">
   <xsl:value-of select="position()"/>
   <xsl:text>. namespace prefix = "</xsl:text>
   <xsl:value-of select="name(.)"/>
   <xsl:text>"&#10;   namespace URI    = "</xsl:text>
   <xsl:value-of select="."/>
   <xsl:text>"&#10;</xsl:text>
  </xsl:for-each>
  <xsl:text>&#10;</xsl:text>
 </xsl:template>
</xsl:stylesheet>

Output of the XSLT document

Find and print all element names and namespace nodes

name          = "root"
namespace URI = ""
This node has 2 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"

name          = "nothing"
namespace URI = ""
This node has 2 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"

name          = "default"
namespace URI = "http://example.com/default"
This node has 3 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"
3. namespace prefix = ""
   namespace URI    = "http://example.com/default"

name          = "override"
namespace URI = ""
This node has 3 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"
3. namespace prefix = ""
   namespace URI    = ""

name          = "ns1:namespace-1"
namespace URI = "http://example.com/namespace-1"
This node has 2 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"

name          = "ns1:attribute-1"
namespace URI = "http://example.com/namespace-1"
This node has 0 namespace nodes:

name          = "attribute-2"
namespace URI = ""
This node has 0 namespace nodes:

name          = "ns2:namespace-2"
namespace URI = "http://example.com/namespace-2"
This node has 3 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"
3. namespace prefix = "ns2"
   namespace URI    = "http://example.com/namespace-2"

End of tests

Some things that the example shows:

  1. All elements have a node for the xml namespace
  2. Elements root and nothing have a node for prefixed namespace "namespace-1" even though the elements don't use it
  3. The name of the default namespace is empty, as seen on default
  4. Element root doesn't have a node for the default namespace but override has, even though neither of them belongs to a namespace. This is because override has a default namespace declaration, only this case it is resetted to empty.
  5. namespace-1 has similar namespace nodes to element nothing although the other one is prefixed and uses a namespace and the other one does not
  6. namespace-2 is the only element to have a node for the ns2 prefix because sibling elements are not in scope for a namespace declaration
  7. Funnily, attributes can belong to a namespace but they still don't seem to have any namespace nodes. Accoring to specs, namespace nodes also have expanded name but the URI is always null.

When comparing namespaces, bear in mind that elements don't share namespace nodes. Even if parent and child would have all similar namespace nodes they actually are not same nodes. Just like variables of two different objects are not the same even though they would have same name and same value.

jasso