任意の深さで名前によって要素の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のみのパーツのリストが必要な場合。
2012

12
@pfeds:その後、私は使用したいdoc.Descendants("Cars").Descendants("Part")可能性(または.Elements("Part")。彼らは唯一の直接の子だったら
ジョンスキート

8
6年経った今でも素晴らしい例です。実際、これはMSDNの説明よりもはるかに役立ちます:-)
EvilDr 2015

そして、それはまだ悪い例です、博士。「車」がない場合、上記のコードはNPEになります。多分。?新しいC#から最終的にそれが有効になります
Dror Harari 2015

3
@DrorHarariいやは、例外がスローされていない:試してみるvar foo = new XDocument().Descendants("Bar").Descendants("Baz"); のでDescendantsリターンは空にIEnumerable<XElement>していませんnull
DareDude 2016

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 2010

2
ありがとう!データコントラクトのシリアル化を使用しています。<:私は「= MyClassEntriesののxmlnsこれは、のようなヘッダを作成w3.org/2001/XMLSchema-instance『のxmlns =』schemas.datacontract.org/2004/07/DataLayer.MyClassを」>と私はなっていなかった理由を私は困惑しました。子孫。{ schemas.datacontract.org/2004/07/DataLayer.MyClass }プレフィックスを追加する必要がありました。
キム

38

あなたはこの方法でそれを行うことができます:

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

どこxmlですXDocument

プロパティNameはa LocalNameとa を持つオブジェクトを返すことに注意してくださいNamespace。そのName.LocalNameため、名前で比較する場合に使用する必要があります。


私はすべてのEmbeddedResourceノードをc#プロジェクトファイルから取得しようとしていますが、これが機能する唯一の方法です。XDocumentドキュメント= XDocument.Load(csprojPath); IEnumerable <XElement> embeddedResourceElements = document.Descendants( "EmbeddedResource"); 動作しませんと私は理由がわかりません。
Eugene Maksimov

22

子孫はあなたが必要とするものを正確に行いますが、要素の名前と一緒に名前空間名を含めたことを確認してください。省略した場合、空のリストが表示される可能性があります。


11

これを行うには2つの方法があります。

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

結果はnullポインタか、結果がありません。


1
それXPathEvaluateSystem.Xml.XPath名前空間にあることに言及するだけです。
Tahir Hassan

XPathEvaluateでうまくいくはずですが、クエリは特定の深さ(1)のノードのみを取得します。ドキュメント内のどこにあるかに関係なく、「email」という名前のすべての要素を選択する場合は、パス「// email」を使用します。名前が何であれツリー全体を歩く必要があるため、このようなパスは明らかにコストが高くなりますが、何をしているのかを知っていれば、非常に便利です。
ダグ2013年

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ファイルはリソースに保存されることに注意してください(リソースのヘルプについては、スニペットの最後にあるリンクを参照してください)。各メールアドレスは、「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());
}

結果

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

注:コンソールアプリケーションおよびWPFまたはWindowsフォームの場合、「using System.Xml.Linq;」を追加する必要があります。プロジェクトの上部にあるUsingディレクティブ。Consoleの場合、Usingディレクティブを追加する前に、このネームスペースへの参照を追加する必要があります。また、コンソールの場合、デフォルトでは「プロパティフォルダー」の下にリソースファイルがないため、手動でリソースファイルを追加する必要があります。以下のMSDNの記事で、これについて詳しく説明しています。

リソースの追加と編集

方法:リソースを追加または削除する


1
ここでは意地悪になりたくないが、あなたの例は孫を示していない。emailAddressはemailの子です。名前空間を使用せずに子孫を使用する方法があるかどうか疑問に思っていますか?
SoftwareSavant
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.