困ったときにそのまま使える LINQ to XML コードサンプル 18 パターン [2/2]
LINQ to XML の単純なコードサンプル集の後半戦、LINQ と XPath によるノードの選択方法です。
XML 要素の選択 (XPath と LINQ)
XPath による単一要素の選択
上の 「要素を含む XML ファイルの作成」で作成した次の XML ファイルを考えます。
<?xml version="1.0" encoding="utf-8"?> <!--LINQ to XML Sample http://keicode.com/--> <Employees> <Employee> <FirstName>Keisuke</FirstName> <LastName>Oyama</LastName> <ID>123</ID> </Employee> </Employees>
ここから FirstName=Keisuke である、Employee 要素を取り出して、その要素内の ID を取得します。
var xelm = XElement.Load( @"C:\Temp\LINQ_to_XML_Sample2.xml" ); var emp = xelm.XPathSelectElement( "//Employee[FirstName='Keisuke']" ); MessageBox.Show( emp.Element( "ID" ).Value );
上記の実行結果は次のようになります。
尚、XPath 関連のメソッドを利用するには using System.Xml.XPath を指定します。
LINQ クエリーによる単一要素の選択 (XElement の利用)
上の例では XPath を用いて要素を取り出しましたが、ここでは LINQ を用いて要素を抽出します。
ここでは要素の値によって要素を取得しています。Element の Value を利用します。
var xelm = XElement.Load( @"C:\Temp\LINQ_to_XML_Sample2.xml" ); var emp = ( from p in xelm.Elements( "Employee" ) where p.Element( "FirstName" ).Value == "Keisuke" select p ).Single(); MessageBox.Show( emp.Element( "ID" ).Value );
上記の実行結果は次のようになります。
LINQ クエリーによる単一要素の選択 (XDocument の利用)
XDocument を用いて上記と同様の要素選択をするには、XDocument クラスの Descendants メソッドを利用します。
var xdoc = XDocument.Load( @"C:\Temp\LINQ_to_XML_Sample2.xml" ); var emp = ( from p in xdoc.Descendants( "Employee" ) where p.Element( "FirstName" ).Value == "Keisuke" select p ).Single(); MessageBox.Show( emp.Element( "ID" ).Value );
上記の実行結果は次のようになります。
LINQ クエリーによる複数要素の選択 (属性値によるクエリー)
上の「属性付きの複数要素を含む XML ファイルの作成」で作成した XML ファイルに対して、 Gender (性別) 属性が "Male" (男性) である Employee 要素を抽出しましょう。
LINQ にて Attribute の条件を指定しています。
var xelm = XElement.Load( @"C:\Temp\LINQ_to_XML_Sample4.xml" ); var emps = ( from p in xelm.Elements( "Employee" ) where p.Attribute( "Gender" ).Value == "Male" select p ); foreach( var emp in emps ) { MessageBox.Show( emp.Element( "FirstName" ).Value ); }
この結果、以下のように指定した属性を持つ要素のみ (この場合、性別が男性の要素のみ) が取得できます。
XPath による複数要素の選択 (属性値による選択)
XPath を用いて、属性値による複数要素の選択を行うには以下のようにします。
XElement の XPathSelectElements メソッドを用いて属性値の選択を行います。 属性値の条件を記述するには、@属性 + 条件 という構文とします。
以下の例では Gender 属性の値が "Female" (女性) である Employee 要素を選択するよう指定しています。
var xelm = XElement.Load( @"C:\Temp\LINQ_to_XML_Sample4.xml" ); var emps = xelm.XPathSelectElements( "//Employee[@Gender='Female']" ); foreach ( var emp in emps ) { MessageBox.Show( emp.Element( "FirstName" ).Value ); }
この結果、以下のように指定した属性を持つ要素のみが取得できました。
LINQ による複数要素の選択とソート
上の「属性付きの複数要素を含む XML ファイルの作成」で作成した XML ファイルに対して、 Age (年齢) の値によって降順に並べ変えて要素を取得します。
LINQ で昇順に並べるには orderby 評価値、降順に並べ替えるには orderby 評価値 descending とします。
var xelm = XElement.Load( @"C:\Temp\LINQ_to_XML_Sample4.xml" ); var emps = ( from p in xelm.Elements( "Employee" ) orderby int.Parse( p.Attribute( "Age" ).Value ) descending select p ); foreach ( var emp in emps ) { MessageBox.Show( emp.Element( "FirstName" ).Value ); }
この結果、次のように Age 属性値の降順で Employee 要素が取得できました。
→ →
尚、ここで注意する点は XAttribute 及び XElement の Value 属性は文字列として取得されると言う点です。 単純な文字列の並べ替えと数値の順番は異なりますので、注意が必要です。
例えば "1", "2", "15" という文字列値が存在した場合、文字列値のまま昇順に並べると "1", "15", "2" となります。 しかしこれを数値の値として 1, 2, 15 という順番で取得したい場合は、上記のコード例のように int.Parse メソッドで数値として評価する必要があります。
匿名クラスオブジェクトを返す LINQ クエリー
LINQ クエリーの結果として返される値を匿名クラスのコレクションとする場合は、 以下のように LINQ にて select new { ... } を返します。
var xelm = XElement.Load( @"C:\Temp\LINQ_to_XML_Sample4.xml" ); var emps = ( from p in xelm.Elements( "Employee" ) orderby int.Parse( p.Attribute( "Age" ).Value ) select new { Description = p.Element("LastName").Value + " : " + p.Attribute("Age").Value } ); foreach ( var emp in emps ) { MessageBox.Show( emp.Description ); }
この結果、以下のように Description というプロパティを持ったクラスが返されていることがわかります。
→ →
事前定義されたクラスのインスタンスのコレクションを返す LINQ クエリ
準備として次のような Employee クラスを定義します。
public class Employee { string firstName; string lastName; public Employee( string fname , string lname ) { this.firstName = fname; this.lastName = lname; } public void sayHello() { MessageBox.Show( string.Format( "Hello, I'm {0} {1}." , this.firstName , this.lastName ) ); } }
コンストラクタで、ファーストネーム (fname) とラストネーム (lname) を受け取り、 sayHello メソッドで、"Hello, I'm 名前." というメッセージボックスを表示します。
さて、LINQ の結果としてこの Employee クラスのインスタンスのコレクションを返しましょう。
ポイントは select にて new Employee としてコンストラクタを呼び出している ところです。
var xelm = XElement.Load( @"C:\Temp\LINQ_to_XML_Sample4.xml" ); var emps = ( from p in xelm.Elements( "Employee" ) orderby int.Parse( p.Attribute( "Age" ).Value ) select new Employee( p.Element( "FirstName" ).Value , p.Element( "LastName" ).Value ) ); foreach ( var emp in emps ) { emp.sayHello(); }
この結果、確かに Employee クラスのメソッド sayHello が呼び出されていることが判ります。
→ →
LINQ で集計関数を使う
上の「属性付きの複数要素を含む XML ファイルの作成」で作成した XML ファイルに対して、 Age (年齢) の値の最大値を取得しましょう。
Max 集計関数を利用します。
var xelm = XElement.Load( @"C:\Temp\LINQ_to_XML_Sample4.xml" ); int max = ( from p in xelm.Elements( "Employee" ) select int.Parse( p.Attribute( "Age" ).Value ) ).Max(); MessageBox.Show( max.ToString() );
この結果、確かに以下のように Age の最大値が取得できました。