it-swarm.dev

Interroga un documento X per gli elementi per nome a qualsiasi profondità

Ho un oggetto XDocument. Voglio cercare elementi con un nome particolare a qualsiasi profondità usando LINQ. Quando uso Descendants("element_name"), ottengo solo elementi che sono figli diretti del livello corrente. Quello che sto cercando è l'equivalente di "// element_name" in XPath ... dovrei semplicemente usare XPath, o c'è un modo per farlo usando i metodi LINQ? Grazie.

139
Rich

I discendenti dovrebbero funzionare assolutamente bene. Ecco un esempio:

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

Risultati:

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

206
Jon Skeet

Un esempio che indica lo spazio dei nomi:

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

Puoi farlo in questo modo:

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

dove xml è un XDocument.

Tenere presente che la proprietà Name restituisce un oggetto con LocalName e Namespace. Ecco perché devi usare Name.LocalName se vuoi confrontare per nome.

35

I discendenti faranno esattamente ciò di cui hai bisogno, ma assicurati di aver incluso un nome dello spazio dei nomi insieme al nome dell'elemento. Se lo ometti, probabilmente otterrai un elenco vuoto.

22
Nenad Dobrilovic

Ci sono due modi per raggiungere questo obiettivo,

  1. LINQ to XML
  2. XPath

I seguenti sono esempi di utilizzo di questi approcci,

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

Se usi XPath, devi eseguire alcune manipolazioni con IEnumerable:

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

Nota che

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

risulta un puntatore nullo o nessun risultato.

11
roland roos

Sto usando il metodo di estensione XPathSelectElements che funziona allo stesso modo di XmlDocument.SelectNodes metodo:

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

Dopo la risposta di @Francisco Goldenstein, ho scritto un metodo di estensione

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

sappiamo che quanto sopra è vero. Jon non sbaglia mai; i desideri della vita reale possono andare un po 'oltre

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

Ad esempio, di solito il problema è, come possiamo ottenere EchoToken nel documento XML sopra? O come sfocare l'elemento con il nome attrbute.

1- Li puoi trovare accedendo con lo spazio dei nomi e il nome come sotto

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

2- Puoi trovarlo dal valore del contenuto dell'attributo, come questo

0
Hamit YILDIRIM

Questa mia variante della soluzione basata su Linq e sul metodo Descendants della classe 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();
    }
}

Risultati:

Per maggiori dettagli sul metodo Desendants dai un'occhiata qui.

0
Mselmi Ali