Öğeler için XDocument'i herhangi bir derinlikte adıyla sorgulama


143

Bir XDocumentnesnem var. LINQ kullanarak herhangi bir derinlikte belirli bir ada sahip öğeleri sorgulamak istiyorum. Kullandığımda Descendants("element_name"), sadece mevcut seviyenin doğrudan çocukları olan öğeler alıyorum. Ne aradığım XPath "// element_name" eşdeğeridir ... Ben sadece kullanmalıyım XPath, ya da LINQ yöntemleri kullanarak bunu yapmanın bir yolu var mı? Teşekkürler.

Yanıtlar:


213

Torunları kesinlikle iyi çalışmalıdır. İşte bir örnek:

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

Sonuçlar:

<grandchild id="3" />
<grandchild id="4" />


1
Bir öğe adı bir xml belgesinde çoğaltılırsa bunu nasıl çözersiniz? Örneğin: xml, <Part> öğesinin alt öğelerine sahip bir <Cars> koleksiyonu ve ayrıca <Part> alt öğelerine sahip bir <Planes> koleksiyonu içeriyorsa ve yalnızca Otomobiller için Parçaların bir listesini istiyorsanız.
54'te pfeds

12
@pfeds: O zaman kullanırdım doc.Descendants("Cars").Descendants("Part")(ya .Elements("Part")da sadece doğrudan çocuk olsaydılar
Jon Skeet

8
Altı yıl ve hala harika bir örnek. Aslında, bu hala MSDN açıklamasından çok daha yararlı :-)
EvilDr

Ve hala kötü bir örnek, Dr., çünkü "Otomobil" yoksa, yukarıdaki kod bir NPE ile sonuçlanacaktır. Belki .? yeni C # 'dan nihayet geçerli olacak
Dror Harari

3
@DrorHarari Hayır, hiçbir istisnası atılır: Deneyin var foo = new XDocument().Descendants("Bar").Descendants("Baz"); Çünkü Descendantsdöner bir boşaltmak IEnumerable<XElement>değil null.
DareDude

54

Ad alanını gösteren bir örnek:

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

2
Ancak, kaynak xml'imin bir ad alanı yoksa ne olur? Sanırım bir kod ekleyebilirsiniz (içine bakmak zorunda), ama neden bu gerekli? Her durumda, root.Descendants ("myTagName") kodumun derinliklerinde üç veya dört seviye gömülü öğeler bulamaz.
EoRaptor013

2
Teşekkürler! Datacontract serileştirme kullanıyoruz. Bu, <MyClassEntries xmlns: i = " w3.org/2001/XMLSchema-instance " xmlns = " schemas.datacontract.org/2004/07/DataLayer.MyClass "> gibi bir başlık oluşturur ve neden alamadığımı bilmiyordum herhangi bir torun. { Schemas.datacontract.org/2004/07/DataLayer.MyClass } önekini eklemem gerekiyordu .
Kim

38

Bunu şu şekilde yapabilirsiniz:

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

nerede xmla XDocument.

Özelliğin Namea LocalNameve a öğelerine sahip bir nesne döndürdüğünü unutmayın Namespace. Bu yüzden Name.LocalNameisimle karşılaştırmak istiyorsanız kullanmak zorundasınız .


Tüm EmbeddedResource düğümünü c # proje dosyasından almaya çalışıyorum ve bu sadece çalışır yolu. XDocument belgesi = XDocument.Load (csprojPath); IEnumerable <XElement> embeddedResourceElements = document.Descendants ("EmbeddedResource"); Çalışmıyor ve nedenini anlamıyorum.
Eugene Maksimov

22

Torunları tam olarak ihtiyacınız olanı yapacak, ancak öğenin adıyla birlikte bir ad alanı adı eklediğinizden emin olun. Bunu atlarsanız, muhtemelen boş bir liste alırsınız.


11

Bunu başarmanın iki yolu vardır,

  1. Linq xml
  2. XPath

Bu yaklaşımları kullanmanın örnekleri aşağıdadır,

List<XElement> result = doc.Root.Element("emails").Elements("emailAddress").ToList();

XPath kullanıyorsanız, IEnumerable ile biraz manipülasyon yapmanız gerekir:

IEnumerable<XElement> mails = ((IEnumerable)doc.XPathEvaluate("/emails/emailAddress")).Cast<XElement>();

Bunu not et

var res = doc.XPathEvaluate("/emails/emailAddress");

sonuç bir boş gösterici veya sonuç yok.


1
sadece söz XPathEvaluateiçindedir System.Xml.XPathad.
Tahir Hassan

XPathEvaluate hile yapmalıdır, ancak sorgunuz yalnızca belirli bir derinlikte düğümleri alır (bir). Belgede nerede olurlarsa olsunlar "e-posta" adlı tüm öğeleri seçmek isterseniz, "// e-posta" yolunu kullanırsınız. Açıkçası bu tür yollar daha pahalıdır, çünkü adın ne olursa olsun tüm ağacın yürümesi gerekir, ancak ne yaptığınızı bilmeniz koşuluyla oldukça uygun olabilir.
Dağ Dag

8

XPathSelectElementsYönteme aynı şekilde çalışan uzantı yöntemi kullanıyorum 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);
            }
        }
    }
}

1

@Francisco Goldenstein cevabını takiben bir uzatma yöntemi yazdım

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

0

yukarıdakilerin doğru olduğunu biliyoruz. Jon asla yanlış değildir; gerçek hayat dilekleri biraz daha ileri gidebilir

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

Örneğin, genellikle sorun, yukarıdaki xml belgesinde EchoToken'i nasıl alabiliriz? Veya öğeyi attrbute adıyla bulanıklaştırma.

1- Bunları ad alanı ve aşağıdaki adla erişerek bulabilirsiniz.

doc.Descendants().Where(p => p.Name.LocalName == "OTA_AirAvailRQ").Attributes("EchoToken").FirstOrDefault().Value

2- Bunu içerik değeri değerine göre bulabilirsiniz, bunun gibi


0

Bu benim sınıftaki çözümün Linqve Descendants yöntemine dayanan varyantımXDocument

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

Sonuçlar:

DesendantsYöntem hakkında daha fazla bilgi için buraya bakınız.


-1

(Kod ve Talimatlar C # içindir ve diğer diller için biraz değiştirilmesi gerekebilir)

Çok sayıda çocuğu olan bir Üst Düğümden okumak istiyorsanız bu örnek mükemmel çalışır, örneğin aşağıdaki XML'ye bakın;

<?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>

Şimdi bu kodla (XML Dosyasının kaynaklarda saklandığını unutmayın (Kaynaklar hakkında yardım için snippet'in sonundaki bağlantılara bakın) "E-postalar" etiketi içinde her e-posta adresini alabilirsiniz.

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

Sonuçlar

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

Not: Konsol Uygulaması ve WPF veya Windows Formları için "using System.Xml.Linq;" Projenizin üst kısmındaki yönergeyi kullanarak, Console için, Kullanma yönergesini eklemeden önce bu ad alanına da bir başvuru eklemeniz gerekir. Ayrıca Konsol için "Özellikler klasörü" altında varsayılan olarak hiçbir Kaynak dosyası olmayacaktır, bu nedenle Kaynak dosyasını manuel olarak eklemeniz gerekir. Aşağıdaki MSDN makaleleri, bunu ayrıntılı olarak açıklamaktadır.

Kaynak Ekleme ve Düzenleme

Nasıl yapılır: Kaynak Ekleme veya Kaldırma


1
Burada kaba olmak istemiyorum, ancak örneğiniz torunları göstermiyor. emailAddress, e-postaların bir çocuğu. Torunları ad alanları kullanmadan kullanmanın bir yolu olup olmadığını merak ediyorum.
SoftwareSavant
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.