어떤 깊이에서든 이름으로 요소에 대한 XDocument 쿼리


143

나는이 XDocument개체를. LINQ를 사용하여 특정 깊이의 특정 이름을 가진 요소를 쿼리하고 싶습니다. 을 사용할 때 Descendants("element_name")현재 수준의 직접적인 하위 요소 만 얻습니다. 내가 찾고있는 것은 XPath의 "// element_name"과 같습니다. 방금 사용해야합니까 XPath, 아니면 LINQ 메소드를 사용하여 수행 할 수 있습니까? 감사.

답변:


213

후손은 절대적으로 잘 작동해야합니다. 예를 들면 다음과 같습니다.

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


1
XML 문서 내에서 요소 이름이 복제 된 경우 어떻게 해결해야합니까? 예를 들어, xml에 <Part>의 하위 요소가있는 <Cars> 컬렉션과 <Part>의 하위 요소가있는 <Planes> 컬렉션이 포함 된 경우 Cars의 파트 목록 만 원합니다.
pfeds

12
@pfeds : 그런 다음 사용합니다 doc.Descendants("Cars").Descendants("Part")(또는 아마도 .Elements("Part")직계 자녀 인 경우에만 가능)
Jon Skeet

8
6 년이 지났지 만 여전히 훌륭한 예입니다. 실제로 이것은 MSDN 설명보다 훨씬 더 유용합니다 :-)
EvilDr

"Cars"가 없다면 위의 코드는 NPE가 될 것이기 때문에 여전히 나쁜 예입니다. Dr. 아마.? 새로운 C #에서 마침내 그것을 유효하게 만들 것입니다
Dror Harari

3
@DrorHarari 아니는, 예외는 throw되지 : 밖으로 시도 var foo = new XDocument().Descendants("Bar").Descendants("Baz"); 하기 때문에 Descendants반환 빈 IEnumerable<XElement>하지 null.
DareDude

54

네임 스페이스를 나타내는 예제 :

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
그러나 소스 XML에 네임 스페이스가 없으면 어떻게해야합니까? 코드에 하나를 추가 할 수 있다고 가정하지만 왜 그럴 필요가 있습니까? 어쨌든 root.Descendants ( "myTagName")는 내 코드에서 3-4 단계 깊이에 묻힌 요소를 찾지 않습니다.
EoRaptor013

2
감사! 데이터 계약 직렬화를 사용하고 있습니다. <: 내가 "= MyClassEntries의 XMLNS이 같은 헤더를 생성 w3.org/2001/XMLSchema-instance "의 xmlns = " schemas.datacontract.org/2004/07/DataLayer.MyClass을 "> 내가 점점되지 않은 이유를 나는 난처한 상황에 빠진했다 모든 자손. { schemas.datacontract.org/2004/07/DataLayer.MyClass } 접두사 를 추가해야했습니다 .
Kim

38

이 방법으로 할 수 있습니다 :

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

어디 xml있어XDocument .

이 속성 Name은 a LocalName및 a 가있는 객체를 반환합니다 Namespace. 따라서 Name.LocalName이름별로 비교 하려면 사용해야 합니다.


c # 프로젝트 파일에서 모든 EmbeddedResource 노드를 가져 오려고하는데 이것이 작동하는 유일한 방법입니다. XDocument 문서 = XDocument.Load (csprojPath); IEnumerable <XElement> embeddedResourceElements = document.Descendants ( "EmbeddedResource"); 작동하지 않으며 이유를 이해하지 못합니다.
유진 Maksimov

22

자손은 필요한 것을 정확하게 수행하지만 요소 이름과 함께 네임 스페이스 이름을 포함했는지 확인하십시오. 생략하면 빈 목록이 표시 될 수 있습니다.


11

이를 달성하는 두 가지 방법이 있습니다.

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

결과는 널 포인터이거나 결과가 없습니다.


1
네임 스페이스에 있다는 것만 언급하면 XPathEvaluate됩니다 System.Xml.XPath.
Tahir Hassan

XPathEvaluate는 트릭을 수행해야하지만 쿼리는 특정 깊이 (1)의 노드 만 사용합니다. 문서에서 발생하는 위치와 상관없이 "email"이라는 이름의 모든 요소를 ​​선택하려면 "// email"경로를 사용하십시오. 이름이 무엇이든간에 전체 트리를 걸어야하기 때문에 그러한 경로는 분명히 더 비쌉니다.
Dag

8

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

1

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

0

우리는 위의 사실을 알고 있습니다. 존은 결코 틀리지 않습니다. 실제 소원은 조금 더 갈 수 있습니다

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

예를 들어, 일반적으로 문제는 위의 XML 문서에서 EchoToken을 어떻게 얻을 수 있습니까? 또는 이름이 attrbute 인 요소를 흐리게하는 방법.

1- 네임 스페이스와 아래의 이름으로 액세스하면 찾을 수 있습니다

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

2- 다음 과 같은 속성 컨텐츠 값으로 찾을 수 있습니다.


0

이것은 클래스 Linq의 기반 및 후손 방법 에 대한 솔루션의 변형입니다.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방법 에 대한 자세한 내용은 여기를 참조하십시오.


-1

(코드 및 지침은 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의 경우 "using System.Xml.Linq;"를 추가해야합니다. 콘솔 상단에 프로젝트 상단에 지시문을 사용하면 Using 지시문을 추가하기 전에이 네임 스페이스에 대한 참조를 추가해야합니다. 또한 콘솔의 경우 기본적으로 "속성 폴더"아래에 리소스 파일이 없으므로 리소스 파일을 수동으로 추가해야합니다. 아래의 MSDN 기사에서이를 자세히 설명합니다.

리소스 추가 및 편집

방법 : 리소스 추가 또는 제거


1
여기에 서고 싶지는 않지만 모범은 손자를 보여주지 않습니다. emailAddress는 이메일의 하위입니다. 네임 스페이스를 사용하지 않고 자손을 사용할 수있는 방법이 있는지 궁금합니다.
SoftwareSavant
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.