Запрос XDocument для элементов по имени на любой глубине


у меня есть

8 134

8 ответов:

потомки должны работать абсолютно нормально. Вот пример:

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" />

пример, указывающий на пространство имен:

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 );
}

вы можете сделать это так:

xml.Descendants().Where(p => p.Name.LocalName == "Name of the node to find")

здесь xml это XDocument.

имейте в виду, что собственность Name возвращает объект, который имеет LocalName и Namespace. Вот почему вы должны использовать Name.LocalName если вы хотите сравнить по названию.

потомки будут делать именно то, что вам нужно, но убедитесь, что вы включили имя пространство имен с именем элемента. Если вы опустите его, вы, вероятно, получите пустой список.

есть два способа сделать это,

  1. Linq-to-xml
  2. 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");

результаты либо нулевой указатель, либо нет результатов.

Я использую 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);
            }
        }
    }
}

после ответа @ 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);
        }
    }
}

(код и инструкции для C# и, возможно, потребуется немного изменить для других языков)

этот пример отлично работает, если вы хотите читать с родительского узла, который имеет много детей, например, посмотрите на следующий XML;

<?xml version="1.0" encoding="UTF-8"?> 
<emails>
    <emailAddress>jdoe@set.ca</emailAddress>
    <emailAddress>jsmith@hit.ca</emailAddress>
    <emailAddress>rgreen@set_ig.ca</emailAddress> 
</emails>

теперь с помощью этого кода ниже (имея в виду, что XML-файл хранится в ресурсах (см. ссылки в конце фрагмента для справки о ресурсах) вы можете получить каждый адрес электронной почты в разделе " Электронные письма" метка.

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());
}

результаты

  1. jdoe@set.ca
  2. jsmith@hit.ca
  3. rgreen@set_ig.ca

Примечание: для консольных приложений и WPF или Windows Forms необходимо добавить "использование системы.XML.Linq; " используя директиву в верхней части вашего проекта, для консоли вам также нужно будет добавить ссылку на это пространство имен перед добавлением директивы Using. Также для консоли не будет файла ресурсов по умолчанию под "папка свойств", поэтому вам нужно вручную добавить файл ресурсов. В статьях MSDN ниже, объясните это подробно.

добавление и редактирование ресурсов

как добавить или удалить ресурсы