連載

.NETで簡単XML

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

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

文字列定数にXML文書を書き込む場合の改行とインデント

 ソース・コード中にXML文書を文字列定数として書き込んでしまいたいケースに、しばしば遭遇する。例えば、筆者が作成したプログラムの中には、XML文書として読み込む設定ファイルがなかった場合に、デフォルトで設定される値をソース・コード中にXML文書として記述しているものがある。XML文書はStringReaderクラスなどを使えば、文字列からでも読み込ませるのは簡単なので、けっこう便利である。このほかに、類似の方法として、リソースにXML文書を埋め込む方法もある。

 その場合、注意しなければならないのは、空白文字である。XML 1.0の空白文字 はスペース文字(U+0020:Unicodeにおける文字コード表記)、タブ(U+0009)、復帰(U+000D)、改行(U+000A)の4種類 であり、改行やタブを含むことに注意しよう。実例を以下に示そう。

Private Const document1 As String = "<?xml version='1.0' ?>" _
  & "<document attribute='value'>" _
  & " <item>text</item>" _
  & "</document>"

Private Const document2 As String = "<?xml version='1.0' ?>" & vbCrLf _
  & "<document attribute='value'>" & vbCrLf _
  & " <item>text</item>" & vbCrLf _
  & "</document>" & vbCrLf
ソース・コード中にXML文書を文字列定数として埋め込んだサンプル・プログラム(VB.NET版C#版
文字列定数document1は改行文字を含まないのに対し、文字列定数document2は改行文字を含んでいる。

 ここで、文字列定数のdocument1とdocument2を見比べていただきたい。一見、同じXML文書を記述しているかのように見えるが、改行文字を明示的に入れている/入れていないという差がある。document1は一見、複数の行に分かれているように見えるが、文字列の内容に改行文字は含まれない。それに対して、document2は明示的にvbCrLf(つまり、Chr(13) & Chr(10))(C#では「\r\n」)を記述することで、改行文字を含むXML文書を記述している。この2つはイコールではない。それを確認するために以下のようなサンプル・コードを記述してみた。

Private Sub dumpFromString(ByVal src As String)
  Dim reader As XmlTextReader = New XmlTextReader(New StringReader(src))
  reader.Read()
  While Not reader.EOF
    System.Diagnostics.Trace.Write(reader.NodeType.ToString())
    If reader.NodeType = XmlNodeType.Whitespace Then
      System.Diagnostics.Trace.Write(" value: ")
      For Each ch As Char In reader.Value
        System.Diagnostics.Trace.Write(AscW(ch))
        System.Diagnostics.Trace.Write(" "c)
      Next
    End If
    System.Diagnostics.Trace.WriteLine("")
    reader.Read()
  End While
End Sub

Private Sub getItemText(ByVal src As String)
  Dim doc As XmlDocument = New XmlDocument
  doc.LoadXml(src)
  System.Diagnostics.Trace.WriteLine("text in item is: " _
    & doc.SelectSingleNode("//item").InnerText)
End Sub

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
  System.Diagnostics.Trace.WriteLine("*** document1")
  dumpFromString(document1)
  getItemText(document1)
  System.Diagnostics.Trace.WriteLine("*** document2")
  dumpFromString(document2)
  getItemText(document2)
End Sub
改行文字を含まないXML文書と含むXML文書の違いを確認するためのサンプル・プログラム(VB.NET版C#版

 これを実行すると以下のようになる(ただし、「*** document3」以下の行は、C#版でのみ出力される)。

*** document1
XmlDeclaration
Element
Whitespace value: 9
Element
Text
EndElement
EndElement
text in item is: text
*** document2
XmlDeclaration
Whitespace value: 13 10
Element
Whitespace value: 13 10 9
Element
Text
EndElement
Whitespace value: 13 10
EndElement
Whitespace value: 13 10
text in item is: text
*** document3
XmlDeclaration
Whitespace value: 13 10 9 9 9
Element
Whitespace value: 13 10 9 9 9 9
Element
Text
EndElement
Whitespace value: 13 10 9 9 9
EndElement
text in item is: text
「改行文字を含まないXML文書と含むXML文書の違いを確認するためのサンプル・プログラム」の出力結果
「*** document3」以下の行は、C#版でのみ出力される。

 このサンプル・プログラムでは、空白ノードに含まれる文字のコードを出力している。この出力の中で、13と10の組み合わせが改行文字(改行+復帰)、9がタブ文字を示す。これを見れば、XMLパーサを通して受け取る結果が完全に一致しないことが分かるだろう。つまり、外部のXML文書を読み込むのと等価なXML文書をソース・コードに埋め込む場合は、改行文字を忘れずに入れるようにしなければならない。

 しかし、話はそれで終わりではない。getItemTextメソッドが出力している「text in item is: ……」以下の文字列を見ると、両者は何ら変わりない結果を出力している。文字データの一部を構成しない空白文字は、あってもなくても結果を左右しないというのも事実である。その事実を活用することにより、あえて改行を含まないXML文書をソース・コードに埋め込んでしまうという定石もある。改行文字を含めない方が、ソース・コードがすっきりして見やすい場合もあるのである。ただし、文字データの一部を構成する改行文字を取り除くと、取得される文字データが変わってしまうので、その場合は改行文字を省略してはならない。

 さて、以下はC#プログラマだけに関係する話題である。上記のサンプル・プログラムは、C#の場合に限ってのみ、3つめのXML文書が書き込まれている。これは、「逐語的リテラル文字列」と呼ばれるものである(ちなみに「逐語的」とは、「記述されているとおりに文字列を解釈する」という意味)。二重引用符で囲まれたリテラル文字列の先頭に@記号を書き加えるだけで、引用符に囲まれた範囲内のリテラル文字列が、記述されているとおりの文字列のままの形で扱われる。そのまま扱われるということは、「\r\n」のようなエスケープ・シーケンスが改行文字に置き換えたりされない、ということである。では逐語的リテラル文字列には改行を入れることができないのかというと、そんなことはない。リテラル文字列の中で改行すれば、それが改行として認識されるのである。つまり、複数行に渡る文字列をいちいち引用符を閉じることなく記述できる。

 もちろん、便利さばかりではない。引用符「"(ダブル・クォーテーション)」 だけは、そのまま書き込むことができず、文字の"は、同じ文字を2回重ねて""と 記述しなければならない。逐語的リテラルは、複数行に渡るテキストデータをそ のままソースコード中に書き込めるのだが、引用符「"(ダブル・クォーテーシ ョン)」だけは加工が必要とされ、不便である。

 この逐語的リテラル文字列は、XML文書を扱う上で極めて便利である。そのため、XML文書をソース・コードに埋め込む際に逐語的リテラル文字列を使うのは、C#での1つの定石であるといえる。では、なぜ極めて便利といえるのか。その理由は、XML文書で使われるほとんどすべての引用符は、「"」と「'(シングル・クォーテーション)」のどちらを使ってもよいとされている点にある。例えば、属性を記述するときにattribute='value'と記述しても、attribute="value"と記述しても、結果は同じである。通常、応用ソフトは使用された引用符がどちらであったかの情報を取得することもできないぐらい、まったくの等価となる。ならば、「"」を一切使わないようにXML文書を記述してしまえば、1つの逐語的リテラル文字列に、元の文字列を加工することなく、XML文書全体を 容易に収めてしまうことができる。

 多くの言語において、引用符の役割は固定されていて、容易に入れ替えて記述することはできない。引用符の役割がほとんど固定されていないXMLだからこそ定石として成立する使い方といえる。

 このようにメリットのある逐語的リテラル文字列だが、1つだけ問題がある。それはソース・コードに読みやすくインデントを付けるためにスペース文字やタ ブ(注:文字名表記は、JIS X 4159準拠。空白文字、スペース文字、タブ、復帰、改行)を入れた場合、それらの文字も文字列の一部として取り込まれてしまうということである。そのため、空白文字(スペース文字、タブ、復帰、改行) の1文字まで厳密に書き込む必要がある場合、改行に続く文字は行頭に寄り気味になってソース・コードが見にくくなる可能性もあり得る。しかし、文字データの一部として認識されない空白文字(スペース文字、タブ、 復帰、改行) であれば大きな影響を及ぼさないので、インデントを入れることができる。この点でも、逐語的リテラル文字列という機能はXMLと親和性が高い。

終わりに

 前回および今回述べた定石は、あくまで筆者が定石としてよく使うものであって、ほかの考え方、方法、実現方法による定石もあり得るだろう。それは、読者の皆さんが、個々の状況、立場から最善のものを考えてみてほしい。

 さて、以上でこの連載も終わりである。XMLは簡単であるが同時に奥が深い。ちょっと試してみるのも簡単で面白いが、いざ本格的に使おうとすると簡単に終わらない部分も残る。前々回までの解説は簡単な部分についての説明だったが、前回および今回は少しだけ簡単ではない部分に踏み込んでいる。もちろん、簡単ではない部分とは、実用に使われる部分である。

 そのような実用領域は奥が深いが、決して恐れる必要はない。ほかの技術がそうであったように、定石を確立して使いこなしていけば、それで多くの場合に対処できるはずである。しかし、読者の皆さんそれぞれの立場によって、同じ定石がそのまま使えないこともあるだろう。そういう場合には、定石は定石として固定されたものを考えないで、各自の立場で必要とされる形に定石を変化させていくことが必要になる。難しそうだと思うかもしれないが、そういう作業でこそ、XMLは真価を発揮する。定石の開拓には試行錯誤を必要とするかもしれないが、常にテキスト・データとして参照できるXML文書が、試行錯誤をやりやすくしてくれる。生成したデータをちょっとメモ帳で開けば、すぐに内容を確認できるのだ。そこにこそ、XMLを使う本当の意義の1つが見えるように思われる。

 以上でこの連載は終わるが、長い連載にお付き合いいただいた読者の皆さんに感謝を申し上げたい。End of Article

 

 INDEX
  .NETで簡単XML
  第16回 川俣流XMLプログラミングの定石(2)
    1.データを処理しやすい形に変換するタイミング
    2.DOMツリーからデータを抜き取る方法
    3.XML文書とクラスの1対1対応およびXML文書のキャッシュ
  4.文字列定数にXML文書を書き込む場合の改行とインデント
 
インデックス・ページヘ  「連載 :.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 記事ランキング

本日 月間