В последнее время я использую XPathDocument и XNavigator для анализа XML-файла для заданного XPath и атрибута. Он работает очень хорошо, когда я заранее знаю, что такое XPath.
Однако иногда XPath будет одним из нескольких возможных значений XPath, и я хотел бы иметь возможность проверить, существует ли данный XPath или нет.
Если я ошибаюсь в номенклатуре, вот что я называю XPath - учитывая этот XML-объект:
<foo>
<bar baz = "This is the value of the attribute named baz">
</foo>
Я мог бы искать то, что я называю XPath «// foo / bar», а затем читать атрибут «baz», чтобы получить значение.
Пример кода, который я использую для этого:
XPathDocument document = new XPathDocument(filename);
XPathNavigator navigator = document.CreateNavigator();
XPathNavigator node = navigator.SelectSingleNode("//foo/bar");
if (node.HasAttributes)
{
Console.WriteLine(node.GetAttribute("baz", string.Empty));
}
Теперь, если вызов navigator.SelectSingleNode завершился неудачно, он вернет исключение NullReferenceException или XPathException. Я могу поймать и то, и другое и преобразовать приведенное выше в тест, чтобы увидеть, возвращает ли данный XPath исключение, но мне было интересно, есть ли лучший способ?
Ничего очевидного в Intellisense не увидел. XPathNavigator имеет .HasAttributes и .HasChildren, но, если не считать повторения пути по одному узлу за раз, я не вижу ничего более приятного для использования.





Если вы указали допустимый XPath, но он ничего не соответствует, SelectSingleNode не будет бросать a NullReferenceException - он просто вернет null.
Если вы передадите SelectSingleNode какой-нибудь синтаксически недопустимый XPath, тогда он выдаст XPathException.
Поэтому обычно вам просто нужно проверить, было ли возвращаемое значение нулевым или нет.
По памяти, может содержать ошибки.
XDocument doc = XDocument.Load("foo.xml");
var att = from a in doc.Descendants("bar")
select a.Attribute("baz")
foreach (var item in att) {
if (item != null) { ... }
}
Поскольку в заголовке вопроса упоминается Linq, я бы сделал это следующим образом.
Используйте .FirstOrDefault в запросе, если вам нужен только один
Если node == null, то node.HasAttributes выбросит NullReferenceException. Такая ситуация возникает, когда //foo/bar не соответствует ни одному элементу XML-документа.
var node = XDocument.Load(filename)
.Descendants("bar")
.SingleOrDefault(e=>e.Attribute("baz") != null);
if (node != null) Console.WriteLine(node.Attribute("baz").Value);
var baz = navigator.SelectSingleNode("//foo/bar/@baz");
if (baz != null) Console.WriteLine(baz);
Я, наверное, был бы более конкретным в моем xpath.
var doc = XDocument.Load(fileName);
var results = from r in doc.XPathSelectElements("/foo/bar[count(@baz) > 0]")
select r.Attribute("baz");
foreach (String s in results)
Console.WriteLine(s);
Я думаю, что создавать объект XMLNode, выполняя navigator.SelectSingleNode (...), нецелесообразно.
Вместо этого вы должны использовать navigator.Evaluate ():
if (Convert.ToBoolean(navigator.Evaluate(@"boolean(//foo/bar)"))) {...}
Я сравнил скорость этого метода с SelectSingleNode для 500 000 итераций каждая (с выражением XPath в виде строки, не скомпилированной). Метод SelectSingleNode был на 16-18% быстрее, чем логический метод. Я повторил тест несколько раз, и результаты стабильны.
При чем здесь LINQ? XPathNavigator и XPathDocument не являются частью LINQ to XML.