XmlDocument.SelectNodes not working for namespace attributes
-
I'm trying to iterate the namespace declarations in an XML document so I can add them to the XmlNamespaceManager, but it's not working. Given an XML document like:
<foo:bar xmlns:foo='the URI for foo' />
I expect:System.Xml.XmlDocument doc = new System.Xml.XmlDocument() ;
doc.Load ( args [ 0 ] ) ;
System.Xml.XmlNamespaceManager mgr = new System.Xml.XmlNamespaceManager ( doc.NameTable ) ;
foreach ( System.Xml.XmlNode nod in doc.SelectNodes ( "//*[@xmlns:*]" , mgr ) )
...to select that element, but it doesn't. Am I missing something? Does SelectNodes just not want to select by namespace attributes?
Edit: I now see that this is the defined behaviour of XPath 1.0; I don't like it, but there it is.
-
I'm trying to iterate the namespace declarations in an XML document so I can add them to the XmlNamespaceManager, but it's not working. Given an XML document like:
<foo:bar xmlns:foo='the URI for foo' />
I expect:System.Xml.XmlDocument doc = new System.Xml.XmlDocument() ;
doc.Load ( args [ 0 ] ) ;
System.Xml.XmlNamespaceManager mgr = new System.Xml.XmlNamespaceManager ( doc.NameTable ) ;
foreach ( System.Xml.XmlNode nod in doc.SelectNodes ( "//*[@xmlns:*]" , mgr ) )
...to select that element, but it doesn't. Am I missing something? Does SelectNodes just not want to select by namespace attributes?
Edit: I now see that this is the defined behaviour of XPath 1.0; I don't like it, but there it is.
Kinda sounds like you're putting the cart before the horse. You're trying to walk the document to select the namespaces, but in order to walk the tree with xpaths, you really need the namespaces first. Do you not know the structure of your XML? You're really supposed to know your namespaces first, and just use them. You can, I think, walk the tree through the object model and pull out the namespace prefixes and URIs--using some combination of the NamespaceURI property, and the GetNamespaceOfPrefix and/or GetPrefixOfNamespace methods. There's probably a much more efficient way, though--maybe the XMLReader? Quick search .. try this: http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx[^]
-
Kinda sounds like you're putting the cart before the horse. You're trying to walk the document to select the namespaces, but in order to walk the tree with xpaths, you really need the namespaces first. Do you not know the structure of your XML? You're really supposed to know your namespaces first, and just use them. You can, I think, walk the tree through the object model and pull out the namespace prefixes and URIs--using some combination of the NamespaceURI property, and the GetNamespaceOfPrefix and/or GetPrefixOfNamespace methods. There's probably a much more efficient way, though--maybe the XMLReader? Quick search .. try this: http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx[^]
woopsydoozy wrote:
Do you not know the structure of your XML?
Correct.
woopsydoozy wrote:
in order to walk the tree with xpaths, you really need the namespaces
I know the one for xmlns, and it's in the manager, but I can't use it. I'll read the suggested page. Thanks.
-
Kinda sounds like you're putting the cart before the horse. You're trying to walk the document to select the namespaces, but in order to walk the tree with xpaths, you really need the namespaces first. Do you not know the structure of your XML? You're really supposed to know your namespaces first, and just use them. You can, I think, walk the tree through the object model and pull out the namespace prefixes and URIs--using some combination of the NamespaceURI property, and the GetNamespaceOfPrefix and/or GetPrefixOfNamespace methods. There's probably a much more efficient way, though--maybe the XMLReader? Quick search .. try this: http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx[^]
Having read the suggested page and tried a few more things I now understand a lot more about the problems I'm having, but it still doesn't explain why I can't use SelectNodes to get the information I need.
-
Having read the suggested page and tried a few more things I now understand a lot more about the problems I'm having, but it still doesn't explain why I can't use SelectNodes to get the information I need.
Is your issue maybe in using the namespace in the xpath? In your original XML snippet, for example, if you wanted the "box" element in the foo namespace, you'd select foo:box. You do have to add the "foo" namespace to your manager to use it, though:
mgr.AddNamespace("foo","the URI for foo")
Just declaring a namespacemanager does not automatically load all the namespaces. When it's a default namespace (no prefix), the trick I've used in the past is to add a new namespace that has the same URI:
mgr.AddNamespace("foo2","the URI for foo")
This allows me to query for foo2:box. Or maybe you're just wondering why you can't query using xmlns as an attribute? I don't know the technical answer except that it's special somehow, not available as a standard attribute. You can still access it in xpath/xslt if you need to--check out the namespace-uri method, for example.
-
Is your issue maybe in using the namespace in the xpath? In your original XML snippet, for example, if you wanted the "box" element in the foo namespace, you'd select foo:box. You do have to add the "foo" namespace to your manager to use it, though:
mgr.AddNamespace("foo","the URI for foo")
Just declaring a namespacemanager does not automatically load all the namespaces. When it's a default namespace (no prefix), the trick I've used in the past is to add a new namespace that has the same URI:
mgr.AddNamespace("foo2","the URI for foo")
This allows me to query for foo2:box. Or maybe you're just wondering why you can't query using xmlns as an attribute? I don't know the technical answer except that it's special somehow, not available as a standard attribute. You can still access it in xpath/xslt if you need to--check out the namespace-uri method, for example.
woopsydoozy wrote:
Is your issue maybe in using the namespace in the xpath?
No, I got that now.
woopsydoozy wrote:
Just declaring a namespacemanager does not automatically load all the namespaces
Right, and now I understand why. I hope to write at least a Tip on it soon.
woopsydoozy wrote:
When it's a default namespace ...
Right, even though it seems bogus (I still need to investigate further).
Edit: The W3C spec for XPath 1.0 (section 2.3 Node tests) says in part: " A QName in the node test is expanded into an expanded-name using the namespace declarations from the expression context. This is the same way expansion is done for element type names in start and end-tags except that the default namespace declared with xmlns is not used: if the QName does not have a prefix, then the namespace URI is null (this is the same way attribute names are expanded). " [http://www.w3.org/TR/xpath/#node-tests](http://www.w3.org/TR/xpath/#node-tests)[[^](http://www.w3.org/TR/xpath/#node-tests "New Window")] So it's W3C's fault and I still don't see why they would define it that way. :mad:
woopsydoozy wrote:
maybe you're just wondering why you can't query using xmlns as an attribute
Yes, and also as the namespace of an attribute.
Edit: The W3C spec for XPath 1.0 (section 5.3 Attribute Nodes) says in part: " There are no attribute nodes corresponding to attributes that declare namespaces "
-
woopsydoozy wrote:
Is your issue maybe in using the namespace in the xpath?
No, I got that now.
woopsydoozy wrote:
Just declaring a namespacemanager does not automatically load all the namespaces
Right, and now I understand why. I hope to write at least a Tip on it soon.
woopsydoozy wrote:
When it's a default namespace ...
Right, even though it seems bogus (I still need to investigate further).
Edit: The W3C spec for XPath 1.0 (section 2.3 Node tests) says in part: " A QName in the node test is expanded into an expanded-name using the namespace declarations from the expression context. This is the same way expansion is done for element type names in start and end-tags except that the default namespace declared with xmlns is not used: if the QName does not have a prefix, then the namespace URI is null (this is the same way attribute names are expanded). " [http://www.w3.org/TR/xpath/#node-tests](http://www.w3.org/TR/xpath/#node-tests)[[^](http://www.w3.org/TR/xpath/#node-tests "New Window")] So it's W3C's fault and I still don't see why they would define it that way. :mad:
woopsydoozy wrote:
maybe you're just wondering why you can't query using xmlns as an attribute
Yes, and also as the namespace of an attribute.
Edit: The W3C spec for XPath 1.0 (section 5.3 Attribute Nodes) says in part: " There are no attribute nodes corresponding to attributes that declare namespaces "
Michael Kay rocks (check out his books, if you're doing anything with XSLT): http://www.stylusstudio.com/xsllist/200403/post30310.html And attributes don't inherit the namespaces of their containing element by default. They only have namespaces if they have their own namespace prefix (which I don't think I've ever seen in practice). So if you had this XML:
and you wanted to find out what was in the box, you'd add foo and its URI to your namespace manager and get /demo/foo:box/@contents. Only with this XML:
would you need to specify the attribute's namespace: /demo/foo:box/@foo:contents.
-
Michael Kay rocks (check out his books, if you're doing anything with XSLT): http://www.stylusstudio.com/xsllist/200403/post30310.html And attributes don't inherit the namespaces of their containing element by default. They only have namespaces if they have their own namespace prefix (which I don't think I've ever seen in practice). So if you had this XML:
and you wanted to find out what was in the box, you'd add foo and its URI to your namespace manager and get /demo/foo:box/@contents. Only with this XML:
would you need to specify the attribute's namespace: /demo/foo:box/@foo:contents.
Thanks, I'll have to look into some books. The rest of that I understand, but it still remains that when there's a namespace attribute, like:
<foo:bar xmlns:foo='foo' />
I can't select it even though the namespace is in the manager. I really suspect that MS made some controversial choices in implementing System.Xml.XPath.XPathNavigator.Select when it comes to the default namespace and the xmlns namespace. So I may have to implement my own and I don't look forward to it.