XPathSelectElement всегда возвращает null
Почему этот Xpath не работает с использованием 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>
Код С#:
myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator());
Ответы
Ответ 1
Когда используются пространства имен, они также должны использоваться в запросе XPath. Ваш запрос XPath будет работать только с элементами без пространства имен (как это можно проверить, удалив пространство имен из вашего XML).
Вот пример, показывающий, как вы создаете и передаете менеджер пространства имен:
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.
Надеюсь, что это поможет.
Ответ 2
Вероятно, это комментарий к сообщению @Cumbayah, но я не могу оставлять комментарии ни о чем.
Вероятно, вам лучше использовать что-то вроде этого вместо того, чтобы использовать XmlReader для получения 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 = ...;
Ответ 3
Самый простой способ в вашем случае - использовать XPath оси и node test для имени и позиции node для выбора элемента.
Ваш выбор XPath:
myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator());
Легко перевести на:
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]");
Нет необходимости создавать и передавать XmlNamespaceManager в качестве параметра.
Ответ 4
Есть способ сделать это без каких-либо изменений в xpath. Решение, которое я нашел, заключается в удалении пространства имен при анализе XML в XDocument.
Вот пример:
var regex = @"(xmlns:?[^=]*=[""][^""]*[""])";
var myXDocument = XDocument.Parse(Regex.Replace("MyXmlContent", regex, "", RegexOptions.IgnoreCase | RegexOptions.Multiline))
Теперь, когда пространство имен исчезло, его легче манипулировать.