XPathSelectElement always returns null
Solution 1
When namespaces are used, these must be used in the XPath query also. Your XPath query would only work against elements with no namespace (as can be verified by removing the namespace from your XML).
Here's an example showing how you create and pass a namespace manager:
var xml = ... XML from your post ...;
var xmlReader = XmlReader.Create( new StringReader(xml) ); // Or whatever your source is, of course.
var myXDocument = XDocument.Load( xmlReader );
var namespaceManager = new XmlNamespaceManager( xmlReader.NameTable ); // We now have a namespace manager that knows of the namespaces used in your document.
namespaceManager.AddNamespace( "prefix", "http://www.MyNamespace.ca/MyPath" ); // We add an explicit prefix mapping for our query.
var result = myXDocument.XPathSelectElement(
"//prefix:Plugin/prefix:UI[1]/prefix:PluginPageCategory[1]/prefix:Page[1]/prefix:Group[1]/prefix:CommandRef[2]",
namespaceManager
); // We use that prefix against the elements in the query.
Console.WriteLine(result); // <CommandRef ...> element is printed.
Hope this helps.
Solution 2
This should probably be a comment on @Cumbayah's post, but I can't seem to leave comments on anything.
You are probably better off using something like this instead of using XmlReader to get the nametable.
var xml = ... XML from your post ...;
var myXDocument = XDocument.Parse(xml);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("prefix", "http://www.MyNamespace.ca/MyPath");
var result = ...;
Solution 3
The easiest way in your case is to use XPath axes and node test for node name and position to select the element. Your XPath selection:
myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator());
Can be easily translate to:
myXDocument.XPathSelectElement("/child::node()[local-name()='Plugin']/child::node()[local-name()='UI'][position()=1]/child::node()[local-name()='PluginPageCategory'][position()=1]/child::node()[local-name()='Page'][position()=1]/child::node()[local-name()='Group'][position()=1]/child::node()[local-name()='CommandRef'][position()=2]");
There is no need to create and pass XmlNamespaceManager as parameter.
Jean-Philippe Leclerc
Updated on October 24, 2020Comments
-
Jean-Philippe Leclerc over 3 years
Why is this Xpath not working using XDocument.XPathSelectElement?
Xpath:
//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]
XML
<Plugin xmlns="http://www.MyNamespace.ca/MyPath"> <UI> <PluginPageCategory> <Page> <Group> <CommandRef> <Images> </Images> </CommandRef> <CommandRef> <Images> </Images> </CommandRef> </Group> </Page> </PluginPageCategory> </UI> </Plugin>
C# Code:
myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator());
-
Admin about 13 years"Now that the namespace is gone" you might not have a well formed XML document...
-
Jean-Philippe Leclerc about 13 yearsWell... it works perfectly for me. And note that it can be useful when you can't change the xpath.
-
Dzmitry Lahoda almost 12 yearsJust want to point issue that default namespace should be prefixed. I.e. namespaceManager.AddNamespace("","MyNamespace.ca/MyPath") will make DefaultNamespace property rigth as expected, but it will not be used by XPathSelectElement. Misleading!!!
-
PRMan about 8 yearsIt doesn't work perfectly in all cases, specifically the case where two elements without their namespaces would be identical.
-
mike nelson about 7 yearsAwesome, this works great. Namespaces should be simple but are very tricky so this solution is very useful.
-
crush over 6 yearsWhat is the point of loading the
NameTable
from the XmlReader if you have to explicitly add a prefix/ns combination anyways? I assume you are doing that because you don't know the prefix that the document is using for the desired namespace. But, wouldn't that also mean it's unnecessary to initialize theXmlNamespaceManager
with theNameTable
? -
Donny V. over 4 yearsI would just remove the namespace from the string. If you know that it is unique.