いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Linq入門記-80 (LINQ to XML, ナビゲーション, Descendants, Ancestors)

LINQ to XMLでのナビゲーション系のプロパティについて.

過去の内容は以下から見れます。よろしければご参照くださいませ。


今回は子孫要素を取得するDescendantsメソッドと先祖要素を取得するAncestorsメソッドです。
2つとも、個人的にはLINQ to XMLの中で最もよく利用するメソッドの一つです。


Descendantsメソッドは、XContainerクラスに所属するメソッドです。
Ancestorsメソッドは、XNodeクラスに所属するメソッドです。


どちらも、引数無しとXNameを受け取るオーバーロードを持っています。
引数無しの方は、自分を起点として子孫または先祖の要素を取得します。
引数有りの方は、自分を起点とした子孫または先祖の中に指定したXNameを持つ要素を探してくれます。


注意点として

  • Descendantsメソッドの方は、ネストした子孫要素のデータも取得できる。
  • Ancestorsメソッドの方は、自分よりも上に存在する兄弟要素は取得できない。あくまで先祖の要素が取得対象となる。
  • Ancestorsメソッドで返されるコレクションは、ドキュメントの逆順になっている。

というのがあります。



以下、サンプルです。

namespace Gsf.Samples
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Xml.Linq;

  #region LinqSamples-76
  /// <summary>
  /// LINQ to XMLのサンプルです.
  /// </summary>
  /// <remarks>
  /// ナビゲーション(Descendants, Ancestorsメソッド)のサンプルです.
  /// </remarks>
  public class LinqSamples76 : IExecutable
  {
    public void Execute()
    {
      //
      // Descendants(XName)
      //   現在の要素を起点として子孫要素を取得する.
      //   子孫の範囲は、直下だけでなく、ネストした子孫階層のデータも
      //   取得できる. Linq To XMLでよく利用するメソッドの一つ.
      //
      var root = BuildSampleXml();
      var elem = root.Descendants();

      Console.WriteLine("Count={0}", elem.Count());
      Console.WriteLine("=====================================");

      // "Customer"という名前の子孫要素を取得
      elem = root.Descendants("Customer");
      Console.WriteLine("Count={0}", elem.Count());
      Console.WriteLine("First item:");
      Console.WriteLine(elem.First());
      Console.WriteLine("=====================================");

      // 属性付きで絞り込み
      elem = root.Descendants("Customer").Where(x => x.Attribute("CustomerID").Value == "HUNGC");
      Console.WriteLine("Count={0}", elem.Count());
      Console.WriteLine("First item:");
      Console.WriteLine(elem.First());
      Console.WriteLine("=====================================");

      // クエリ式で利用
      elem = from node in root.Descendants("Customer")
             let attr = node.Attribute("CustomerID").Value
             where attr.StartsWith("L")
             from child in node.Descendants("Region")
             where child.Value == "CA"
             select node;

      Console.WriteLine("Count={0}", elem.Count());
      Console.WriteLine("First item:");
      Console.WriteLine(elem.First());
      Console.WriteLine("=====================================");

      // 直接2階層下の要素名を指定
      elem = from node in root.Descendants("Region")
             where node.Value == "CA"
             select node;

      Console.WriteLine("Count={0}", elem.Count());
      Console.WriteLine("First item:");
      Console.WriteLine(elem.First());
      Console.WriteLine("=====================================");

      //
      // Ancestors(XName)      
      //   現在の要素の先祖要素を取得する.
      //   兄弟要素は取得できない(件数が0件となる)
      //   あくまで自分の先祖となる要素を指定する.
      //
      root = BuildSampleXml();
      var startingPoint = root.Descendants("Region").Where(x => x.Value == "CA").First();

      var ancestors = startingPoint.Ancestors();

      Console.WriteLine("Count={0}", ancestors.Count());
      Console.WriteLine("First item:");
      Console.WriteLine(ancestors.First());
      Console.WriteLine("=====================================");

      // ContactNameは、現在の要素(Region)の先祖(FullAddress)ではないため指定しても取得できない
      ancestors = startingPoint.Ancestors("ContactName");

      Console.WriteLine("Count={0}", ancestors.Count());
      if (ancestors.Any())
      {
        Console.WriteLine("First item:");
        Console.WriteLine(ancestors.First());
      }

      Console.WriteLine("=====================================");

      // FullAddress要素の兄弟要素となるContactNameは取得できない
      startingPoint = root.Descendants("FullAddress").First();
      ancestors = startingPoint.Ancestors("ContactName");

      Console.WriteLine("Count={0}", ancestors.Count());
      if (ancestors.Any())
      {
        Console.WriteLine("First item:");
        Console.WriteLine(ancestors.First());
      }

      Console.WriteLine("=====================================");

      // FullAddress要素の先祖であるCustomer要素は取得できる.
      startingPoint = root.Descendants("FullAddress").First();
      ancestors = startingPoint.Ancestors("Customer");

      Console.WriteLine("Count={0}", ancestors.Count());
      if (ancestors.Any())
      {
        Console.WriteLine("First item:");
        Console.WriteLine(ancestors.First());
      }

      Console.WriteLine("=====================================");
    }

    XElement BuildSampleXml()
    {
      //
      // サンプルXMLファイル
      //  see: http://msdn.microsoft.com/ja-jp/library/vstudio/bb387025.aspx
      //
      return XElement.Load(@"xml/CustomersOrders.xml");
    }
  }
  #endregion
}


実行すると以下のようになります。

  Count=356
  =====================================
  Count=4
  First item:
  
    Great Lakes Food Market
    Howard Snyder
    Marketing Manager
    (503) 555-7555
    
      
2732 Baker Blvd.
Eugene OR 97403 USA
===================================== Count=1 First item: Hungry Coyote Import Store Yoshi Latimer Sales Representative (503) 555-6874 (503) 555-2376
City Center Plaza 516 Main St.
Elgin OR 97827 USA
===================================== Count=1 First item: Let's Stop N Shop Jaime Yorres Owner (415) 555-5938
87 Polk St. Suite 5
San Francisco CA 94117 USA
===================================== Count=1 First item: CA ===================================== Count=4 First item:
87 Polk St. Suite 5
San Francisco CA 94117 USA
===================================== Count=0 ===================================== Count=0 ===================================== Count=1 First item: Great Lakes Food Market Howard Snyder Marketing Manager (503) 555-7555
2732 Baker Blvd.
Eugene OR 97403 USA
=====================================


以下、参考リソースです.

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。