XPathSelectElement always returns null

20,881

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.

Share:
20,881
Jean-Philippe Leclerc
Author by

Jean-Philippe Leclerc

Updated on October 24, 2020

Comments

  • Jean-Philippe Leclerc
    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
    Admin about 13 years
    "Now that the namespace is gone" you might not have a well formed XML document...
  • Jean-Philippe Leclerc
    Jean-Philippe Leclerc about 13 years
    Well... it works perfectly for me. And note that it can be useful when you can't change the xpath.
  • Dzmitry Lahoda
    Dzmitry Lahoda almost 12 years
    Just 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
    PRMan about 8 years
    It doesn't work perfectly in all cases, specifically the case where two elements without their namespaces would be identical.
  • mike nelson
    mike nelson about 7 years
    Awesome, this works great. Namespaces should be simple but are very tricky so this solution is very useful.
  • crush
    crush over 6 years
    What 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 the XmlNamespaceManager with the NameTable?
  • Donny V.
    Donny V. over 4 years
    I would just remove the namespace from the string. If you know that it is unique.