連載:[完全版]究極のC#プログラミング

Chapter18 LINQ to XML

川俣 晶
2010/05/06

18.5 LINQ to XMLというブレークスルー

 その答えは、ズバリ「LINQ to XML」ということになる。LINQ to XMLとは、LINQというフレームワークをXMLに対応させたもの……という理解では不十分である。それは、単なるクエリのフレームワークを超えて、XML文書を扱うAPIそのものの大幅な改善手段として用意されている

 ただし、E4Xのように、言語仕様にXML対応を組み込むほどの過激さは見せていない。言語仕様はあくまでXMLに対して、独立を貫いている。だが、その範囲内で可能な限りコンパクトにソースコードを記述できるように、さまざまな工夫が凝らされている。

 実際に、上記のリスト18.1リスト18.2と同じ機能を記述したサンプルコード、リスト18.5を見てみよう。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

class Program
{
  (XML文書の定義はリスト18.1と同じなので省略)

  static void Main(string[] args)
  {
    var doc = XElement.Parse(xmldoc);
    XNamespace ex =
        "http://www.piedey.co.jp/example/linqtoxml200811";

    var query = from n in doc.Descendants(ex + "name")
                where n.Attribute("id").Value == "M"
                select n;

    foreach (var elem in query)
    {
      Console.WriteLine(elem.Value);
    }
  }
}
リスト18.5 LINQ to XMLでリスト18.1を書き直した例

 まず、長い名前がコード上から消えたことを確認しよう。最も長いものが「Descendants」の11文字、次が「XNamespace」の10文字である。この程度なら、長さで苦痛を感じることはない。ちなみに、Descendantsメソッドは、子孫要素のうち指定された名前の要素を列挙する機能を持つ。この場合は、ドキュメントのルート要素以外のすべての要素が対象になる。

 次に、暗黙の型変換や演算子のオーバーロードが活用されている点に注意を払おう。たとえば、次の行はURIが含まれている文字列をXNamespace型オブジェクト(XML名前空間を保持するオブジェクト)に変換するXNamespaceクラスのImplicit演算子によって動作する。

XNamespace ex = "http://www.piedey.co.jp/example/linqtoxml200811";

 この記述のメリットは、XPath式を使った例で、これに相当する次のような行と比較するとわかるだろう。

nsmgr.AddNamespace(
    "ex", "http://www.piedey.co.jp/example/linqtoxml200811");

 そう。ここでexというキーワードは、文字列ではなく変数名になったのである。ということは、名前の不整合はコンパイル時にチェック可能となったのである。

 もう1つ、リスト18.5の「ex + "name"」という部分にも注目してほしい。これは足し算を行っているわけではなく、XNamespaceオブジェクトとローカル名を結合して、XName(XMLで使われる名前を保持するオブジェクト)を作成するXNamespaceクラスのAddition演算子を実行させているものである。つまり、「名前空間URI+ローカル名」を持つ名前オブジェクトを作り出しているわけである。

 これを見て、「加算を意味する『+』をそれ以外の目的で使うのはソースコードの直感的なわかりやすさを損なうからイカン」と怒り心頭の読者も多いと思うが、そのように脊髄反射的に怒るのは早計というものだ。

 なぜなら、この「名前の生成」という処理は大量に書き込む可能性が高いからだ。それゆえに次のことが要求される。

  • 出現回数が多いので、書く手間が少なく、簡潔な表現が望ましい
  • 扱う回数が多いので、プログラマーはすぐに表記に慣れる。予備知識なしで見たときのわかりやすさよりも、簡潔さが優先される
  • 頻出する表記なので書き間違いが入り込む可能性も高い。より的確なエラーチェックができるとよい
  • 名前の生成はたいていの場合、本質的に重要な処理ではない。したがって、重要な処理が目立つよう、シンプルな表記が好ましい

 具体的に次ページのリスト18.6の例を見ると、このような説明の趣旨がなんとなくわかるのではないかと思う。これは、LINQ to XMLのAPIを用いてXML文書を生成するサンプルである。

using System;
using System.Xml.Linq;

class Program
{
  static void Main(string[] args)
  {
    XNamespace ex =
        "http://www.piedey.co.jp/example/linqtoxml200811";

    XElement doc =new XElement(ex + "person",
      new XElement(ex + "name", "Wong Fei Fong"),
      new XElement(ex + "age", "18"),
      new XElement(ex + "address", "Village of Lahan")
    );
    Console.WriteLine(doc.ToString());
  }
}
リスト18.6 名前を生成する「+」の多用

<person xmlns="http://www.piedey.co.jp/example/linqtoxml200811">
  <name>Wong Fei Fong</name>
  <age>18</age>
  <address>Village of Lahan</address>
</person>
リスト18.6の実行結果

 名前を生成する「+」は4回出現しているが、いずれも異なる結果を得るためのもので、生成した値を使い回すことはできない。つまり、名前の生成処理の回数そのものは減らすことはできない。逆に、これらの名前生成は、あくまで主役ではなく「new XElement」で生成される要素の名前指定としての役割を果たすだけである。簡潔に目立たず、書き間違えれば確実にエラーを出力する役割を担って記述されている。

 このように、実際に手を動かしてコードを記述し続けるという状況から見れば、+演算子で名前を生成する仕掛けは使い勝手が良いのである。

 しかし、このサンプルコードにはもう1つ別の見どころがある。おそらく、DOMに精通したプログラマーであれば、このコードはかなり意外性があると思う。この結果を得るにしては、あまりにも簡潔すぎるのである。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter18 LINQ to XML
    1.18.1 LINQプロバイダーを導入する別の理由
    2.18.2 XML最大の災厄
    3.18.3 DOMの憂鬱
    4.18.4 E4XのXMLサポート
  5.18.5 LINQ to XMLというブレークスルー
    6.18.6 単純化されたXML文書生成
    7.18.7 まとめ―ストレスレスなXMLの扱い/練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間