連載

.NETで簡単XML

第16回 川俣流XMLプログラミングの定石(2)

株式会社ピーデー 川俣 晶
2004/03/24
Page1 Page2 Page3 Page4


Back Issue
1
XML超入門
2
プログラムでXML文書を作成する
3
XML文書を読み書きするプログラムの作成
4
DOM(Document Object Model)

5

DOMとXPath

6

.NETプログラムでXSLTスクリプトを使う

7

VS.NETでXML Schemaを活用する(作成編)
8
VS.NETでXML Schemaを活用する(動作編)
9
オブジェクトをXMLでシリアライズ(1)
10
オブジェクトをXMLでシリアライズ(2)
11
オブジェクトをXMLでシリアライズ(3)
12 オブジェクトをXMLでシリアライズ(4)
13 オブジェクトをXMLでシリアライズ(5)
14 オブジェクトをXMLでシリアライズ(6)
15 川俣流XMLプログラミングの定石(1)

 今回も前回に引き続きXML文書の読み書きに関する筆者なりの定石を述べる。

データを処理しやすい形に変換するタイミング

 XML文書に含まれる内容をプログラムから参照することは、単純な作業とは限らない。例えば、DOMを用いて整数が書き込まれた属性値を取得しても、取得される値はあくまで文字列である。よって、その文字列値を整数値に変換する必要があるが、その手順はプログラマ自身が記述しなければならない。また、整数値ではなく文字列値の情報を参照する場合でも、どのノードに目的の文字列が入っているかを調べる手順が発生し、単にデータを参照するコードを記述するだけでは不足である。つまり、XML文書の内容を参照するプログラムでは、DOMツリーから取得した内容をプログラムが扱いやすい形に加工する手順が必要となる。それらをまとめると、以下の3つの手順となる。

  1. 解析
  2. 処理しやすい形に変換
  3. 参照

 ここで問題になるのは、「処理しやすい形に変換」をどのタイミングで行うかである。「解析」と「参照」を行うタイミングは連続しておらず、時間が離れていることが多い。ということは、その間のどのタイミングで「処理しやすい形に変換」を行ってもよいことになる。とはいえ、任意の時点で行うことはあまり多くなく、「解析直後に行う」と「参照直前に行う」のどちらかの場合が多いのではないかと思う。この2つのケースを記述したサンプル・プログラムを以下に示す。

 まず、解析直後に「処理しやすい形に変換」を行う場合のサンプル・プログラムを示す。

Public Class Item
  Private m_code As String
  Public Property Code() As String
    Get
      Return m_code
    End Get
    Set(ByVal Value As String)
      m_code = Value
    End Set
  End Property

  Public Sub New()
  End Sub

  Public Sub New(ByVal filename As String)
    Dim doc As XmlDocument = New XmlDocument
    doc.Load(filename)
    Code = doc.SelectSingleNode("/item/code").InnerText
  End Sub

  Public Sub Save(ByVal filename As String)
    Dim doc As XmlDocument = New XmlDocument
    Dim itemElement As XmlElement = doc.CreateElement("item")
    doc.AppendChild(itemElement)
    Dim codeElement As XmlElement = doc.CreateElement("code")
    itemElement.AppendChild(codeElement)
    codeElement.InnerText = Code
    doc.Save(filename)
  End Sub
End Class
「解析」直後に「処理しやすい形に変換」を行う場合のサンプル・プログラム(VB.NET版C#版

 このサンプル・プログラムは、パラメータのある方のコンストラクタの内部でXML文書をファイルから読み込み、読み込んだ直後にitem要素の子であるcode要素の値を取り出し、これをメンバ変数に格納している(これが「処理しやすい形に変換」の部分)。Codeプロパティは、この変数へのアクセサとして機能しているにすぎない。ファイルへの保存はSaveメソッドで行うが、これは必要なXML文書をDOMツリーとして組み立ててからファイルに保存している。

 なお、パラメータのないコンストラクタも用意している理由は、ファイルが存在しない状態からでもインスタンスを作成できるように、という配慮のためである。

 次に、参照直前に「処理しやすい形に変換」を行う場合のサンプル・プログラムを示す。

Public Class Item
  Private doc As XmlDocument = New XmlDocument

  Public Property Code() As String
    Get
      Return doc.SelectSingleNode("/item/code").InnerText
    End Get
    Set(ByVal Value As String)
      doc.SelectSingleNode("/item/code").InnerText = Value
    End Set
  End Property

  Public Sub New()
    Dim itemElement As XmlElement = doc.CreateElement("item")
    doc.AppendChild(itemElement)
    Dim codeElement As XmlElement = doc.CreateElement("code")
    itemElement.AppendChild(codeElement)
  End Sub

  Public Sub New(ByVal filename As String)
    doc.Load(filename)
  End Sub

  Public Sub Save(ByVal filename As String)
    doc.Save(filename)
  End Sub
End Class
「参照」直前に「処理しやすい形に変換」を行う場合のサンプル・プログラム(VB.NET版C#版

 このサンプル・プログラムは、Codeプロパティ内部で、DOMツリーから値を取り出したり、そこに値を格納したりするように記述されている(これが「処理しやすい形に変換」の部分)。それに伴い、ほかのコードも大幅に変化していることが分かるだろう。パラメータのある方のコンストラクタは、DOMオブジェクトにXML文書を読み込む機能だけを持たされている。Saveメソッドは、ただ単にDOMオブジェクトをファイルに保存するだけの機能を持たされている。パラメータのないコンストラクタは、必要なノードを持つDOMツリーを構築する機能を与えられている。ここで作成するDOMツリーがなければ、プロパティへの書き込みが失敗してしまうのである。

 さて、この2つの方法は、どちらも定石の一種といえる。これらは、それぞれ異なる特徴を持っていて、どちらか一方が正しいとはいえない。では、この2つの方法の特徴を比較してみよう。

 「解析直後に行う」方法にはどのような特徴があるのだろうか。それは、DOMを操作する処理が、ファイルを読み書きする場合にのみ発生するということである。その間、何回プロパティにアクセスしようと、XML固有のライブラリは呼び出されず、シンプルかつ高速に処理が行われる。プログラミングも、XMLとは関係のない普通のVB.NET(C#)プログラミングとして行うことができる。しかし、未知の要素や属性を含むXML文書を読み込んだ場合、それをSaveメソッドで書き込むと、未知の要素や属性はすべて失われてしまう。

 もう1つの「参照直前に行う」方法にはどのような特徴があるのだろうか。こちらは、プロパティにアクセスするごとにXPath式からノードを探索する重い処理が発生するため、速度的なメリットはない。しかし、プロパティ経由で書き換えられないノードも含めて、すべてのノードが保存されるため、未知の要素や属性を含むXML文書を扱う場合でも、すべての未知の要素や属性が保存され、Saveメソッドで書き込む場合にそれらも同時に書き込まれる。

 これらの特徴から、速度を重視するか、それとも未知の要素や属性が保存されることが必要か、といった条件から、どちらの定石を使うかを判断することができるだろう。速度を重視するなら「解析直後に行う」方法がベターであり、未知の要素や属性が保存されることを重視するなら「参照直前に行う」方法がベターであるといえる。このほかに、知っている要素や属性であっても、そのプログラムで扱う必要がないものは処理しないとして、「参照直前に行う」方法を採用するケースもある。

 なお、これはDOMを使った例だが、XmlReader/XmlWriterを使う場合は、「解析直後に行う」方法のみが使え、「参照直前に行う」方法の使用は困難であることに注意が必要である。XmlReader系クラスで読み取った情報は、読み取った後、すぐに消えていくので、後からすべて保存可能にするには、読み取った情報をすべて取っておくようにプログラムを組まねばならない。これは面倒な手間になるので、特に何か大きな理由がない限り、行われることはないだろう。

 ちなみに、この2つの方法を組み合わせて、高速にアクセスしつつ未知の要素や属性も保存するようにコーディングすることは不可能ではない。しかし、筆者の経験上、そこまで複雑なことを行う必要が発生することは多くなく、たいていは「解析直後に行う」方法と「参照直前に行う」方法の2種類の定石を持っているだけで事足りるように思う。複雑な方法は、それが必要になってから考えても遅くないだろう。


 INDEX
  .NETで簡単XML
  第16回 川俣流XMLプログラミングの定石(2)
  1.データを処理しやすい形に変換するタイミング
    2.DOMツリーからデータを抜き取る方法
    3.XML文書とクラスの1対1対応およびXML文書のキャッシュ
    4.文字列定数にXML文書を書き込む場合の改行とインデント
 
更新履歴
【2005/9/22】本記事の一部に以下のような誤りがありました。お詫びして訂正させていただきます。

 速度を重視するなら「参照直前に行う」方法がベターであり、未知の要素や属性が保存されることを重視するなら「参照直前に行う」方法がベターであるといえる。
 速度を重視するなら「解析直後に行う」方法がベターであり、未知の要素や属性が保存されることを重視するなら「参照直前に行う」方法がベターであるといえる。

 XmlReader/XmlWriterを使う場合は、「解析直後に行う」方法のみが使え、「解析直後に行う」方法の使用は困難であることに注意が必要である。
 XmlReader/XmlWriterを使う場合は、「解析直後に行う」方法のみが使え、「参照直前に行う」方法の使用は困難であることに注意が必要である。

 たいていは「解析直後に行う」方法と「解析直後に行う」方法の2種類の定石を持っているだけで事足りるように思う。
 たいていは「解析直後に行う」方法と「参照直前に行う」方法の2種類の定石を持っているだけで事足りるように思う。

インデックス・ページヘ  「連載 :.NETで簡単XML」


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 記事ランキング

本日 月間