У меня есть объект XDocument
. Я хочу запросить элементы с определенным именем на любой глубине, используя LINQ. Когда я использую Descendants("element_name")
, я получаю только элементы, которые являются прямыми дочерними элементами текущего уровня. То, что я ищу, является эквивалентом "//element_name" в XPath... нужно ли просто использовать XPath
, или есть способ сделать это с помощью методов LINQ? Спасибо.
Запросить XDocument для элементов по имени на любой глубине
Ответ 1
Потомки должны работать абсолютно нормально. Вот пример:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string xml = @"
<root>
<child id='1'/>
<child id='2'>
<grandchild id='3' />
<grandchild id='4' />
</child>
</root>";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants("grandchild"))
{
Console.WriteLine(element);
}
}
}
Результаты:
<grandchild id="3" />
<grandchild id="4" />
Ответ 2
Пример, указывающий пространство имен:
String TheDocumentContent =
@"
<TheNamespace:root xmlns:TheNamespace = 'http://www.w3.org/2001/XMLSchema' >
<TheNamespace:GrandParent>
<TheNamespace:Parent>
<TheNamespace:Child theName = 'Fred' />
<TheNamespace:Child theName = 'Gabi' />
<TheNamespace:Child theName = 'George'/>
<TheNamespace:Child theName = 'Grace' />
<TheNamespace:Child theName = 'Sam' />
</TheNamespace:Parent>
</TheNamespace:GrandParent>
</TheNamespace:root>
";
XDocument TheDocument = XDocument.Parse( TheDocumentContent );
//Example 1:
var TheElements1 =
from
AnyElement
in
TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
select
AnyElement;
ResultsTxt.AppendText( TheElements1.Count().ToString() );
//Example 2:
var TheElements2 =
from
AnyElement
in
TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
where
AnyElement.Attribute( "theName" ).Value.StartsWith( "G" )
select
AnyElement;
foreach ( XElement CurrentElement in TheElements2 )
{
ResultsTxt.AppendText( "\r\n" + CurrentElement.Attribute( "theName" ).Value );
}
Ответ 3
Вы можете сделать это следующим образом:
xml.Descendants().Where(p => p.Name.LocalName == "Name of the node to find")
где xml
- XDocument
.
Помните, что свойство Name
возвращает объект с LocalName
и a Namespace
. Вот почему вы должны использовать Name.LocalName
, если хотите сравнить по имени.
Ответ 4
Потомки будут делать именно то, что вам нужно, но убедитесь, что вы включили имя пространства имен вместе с именем элемента. Если вы опустите его, вы, вероятно, получите пустой список.
Ответ 5
Есть два способа сделать это,
- Linq-to-xml
- XPath
Ниже приведены примеры использования этих подходов,
List<XElement> result = doc.Root.Element("emails").Elements("emailAddress").ToList();
Если вы используете XPath, вам нужно сделать некоторые манипуляции с IEnumerable:
IEnumerable<XElement> mails = ((IEnumerable)doc.XPathEvaluate("/emails/emailAddress")).Cast<XElement>();
Обратите внимание, что
var res = doc.XPathEvaluate("/emails/emailAddress");
выводит либо нулевой указатель, либо нет результатов.
Ответ 6
Я использую метод расширения XPathSelectElements
, который работает аналогично методу XmlDocument.SelectNodes
:
using System;
using System.Xml.Linq;
using System.Xml.XPath; // for XPathSelectElements
namespace testconsoleApp
{
class Program
{
static void Main(string[] args)
{
XDocument xdoc = XDocument.Parse(
@"<root>
<child>
<name>john</name>
</child>
<child>
<name>fred</name>
</child>
<child>
<name>mark</name>
</child>
</root>");
foreach (var childElem in xdoc.XPathSelectElements("//child"))
{
string childName = childElem.Element("name").Value;
Console.WriteLine(childName);
}
}
}
}
Ответ 7
Следуя @Francisco Goldenstein ответ, я написал метод расширения
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Mediatel.Framework
{
public static class XDocumentHelper
{
public static IEnumerable<XElement> DescendantElements(this XDocument xDocument, string nodeName)
{
return xDocument.Descendants().Where(p => p.Name.LocalName == nodeName);
}
}
}
Ответ 8
мы знаем, что вышеизложенное верно. Джон никогда не ошибается; реальные желания могут пойти немного дальше
<ota:OTA_AirAvailRQ
xmlns:ota="http://www.opentravel.org/OTA/2003/05" EchoToken="740" Target=" Test" TimeStamp="2012-07-19T14:42:55.198Z" Version="1.1">
<ota:OriginDestinationInformation>
<ota:DepartureDateTime>2012-07-20T00:00:00Z</ota:DepartureDateTime>
</ota:OriginDestinationInformation>
</ota:OTA_AirAvailRQ>
Например, обычно проблема в том, как мы можем получить EchoToken в приведенном выше XML-документе? Или как размыть элемент с именем attrbute.
1- Вы можете найти их, используя пространство имен и имя, как показано ниже
doc.Descendants().Where(p => p.Name.LocalName == "OTA_AirAvailRQ").Attributes("EchoToken").FirstOrDefault().Value
2- Вы можете найти его по значению содержимого атрибута, как этот
Ответ 9
Это мой вариант решения на основе метода Linq
и Descendants класса XDocument
using System;
using System.Linq;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument xml = XDocument.Parse(@"
<root>
<child id='1'/>
<child id='2'>
<subChild id='3'>
<extChild id='5' />
<extChild id='6' />
</subChild>
<subChild id='4'>
<extChild id='7' />
</subChild>
</child>
</root>");
xml.Descendants().Where(p => p.Name.LocalName == "extChild")
.ToList()
.ForEach(e => Console.WriteLine(e));
Console.ReadLine();
}
}
Для более подробной информации о методе Desendants
смотрите здесь.
Ответ 10
(Код и инструкции для С# и могут быть слегка изменены для других языков)
Этот пример отлично работает, если вы хотите прочитать родительский Node, который имеет много детей, например, посмотрите на следующий XML;
<?xml version="1.0" encoding="UTF-8"?>
<emails>
<emailAddress>[email protected]</emailAddress>
<emailAddress>[email protected]</emailAddress>
<emailAddress>[email protected]_ig.ca</emailAddress>
</emails>
Теперь с этим кодом ниже (имея в виду, что XML файл хранится в ресурсах (см. ссылки в конце фрагмента для получения справки о ресурсах). Вы можете получить каждый адрес электронной почты в теге "emails".
XDocument doc = XDocument.Parse(Properties.Resources.EmailAddresses);
var emailAddresses = (from emails in doc.Descendants("emailAddress")
select emails.Value);
foreach (var email in emailAddresses)
{
//Comment out if using WPF or Windows Form project
Console.WriteLine(email.ToString());
//Remove comment if using WPF or Windows Form project
//MessageBox.Show(email.ToString());
}
Результаты
Примечание. Для консольного приложения и WPF или Windows Forms вы должны добавить "using System.Xml.Linq;" Используя директиву в верхней части вашего проекта, для консоли вам также потребуется добавить ссылку на это пространство имен, прежде чем добавлять директиву Using. Кроме того, для консоли не будет файла ресурсов по умолчанию в папке "Свойства", поэтому вам нужно вручную добавить файл ресурсов. В статьях MSDN ниже объясните это подробно.