レベル: 初級 David Mertz, Ph.D (mertz@gnosis.cx), Author, Gnosis Software, Inc.
2002年 2月 02日 XMLのフォーマットは非常にシンプルです。XMLはバイナリー・エンコードではなくプレーンなUnicodeテキストを使用し、構造はすべて推測しやすいタグで宣言されます。にもかかわらず、XML文書を処理するにはパーサーで注意深くデバッグする必要があるほど、XML文法には多くの規則が存在します。しかも、パーサーはそれぞれ独自のプログラミング・スタイルを強制します。それに代わるのは、XMLをシンプルにすることです。オープン・ソースであるPYX形式は、XML文書を表現する純粋に行指向の形式です。この形式を使うことによって、grep、sed、awk、wc、および一般的なUNIXコレクションを使用して、XML文書の内容を簡単に処理できるようになります。
このコラムの常連読者は、XML文書を扱う一般的な方法に関して私が不満を持っていることを良くご存じでしょう。Python xml_objectifyの変更に関して扱った以前の記事では、DOMの複雑さを取り上げましたが、大きな反響がありました。以前Haskell HaXmlライブラリーを紹介する記事を執筆したのは、いくつかの点でXSLTが不便であると感じている事が理由でした。同様に今回は、SAXが様々な問題を解決するものの、必要以上に「重い」ということが分かりました。
SAX APIはDOMやXSLTと比べるならずっと軽いと言えます。それは、コンピューター・リソースの面だけなく、プログラマーがそれを習得するために払う努力やラーニング・カーブというさらに重要な面においてもです。それでも、SAXを使用するには、XMLプログラマーはパーサー・ライブラリーを使う必要がありますし、コールバックAPIに従う必要があります。しかし、XML文書内のデータは、こうした事が必要なほど複雑では決してありません。私の考えでは、XML文書を処理するもっと簡単な方法があるべきです。そして特に、XMLを扱うときに、一般に普及しているツールや技法の多くを、もっと自由に使えてしかるべきです。
PYX形式
PYX形式は、SGML ESIS形式から派生したもので、XML文書を行指向で表現します。PYXは実際にはXMLではありませんが、XML文書内のすべての情報を表現でき、一般的なテキスト処理ツールを使ってより簡単に処理できます。それらに加えて、PYX文書は必要なら変換してXML文書に戻すことができます。PYX文書は、対応するXML文書とほとんど同じ大きさ (若干大きかったり、小さかったりする場合もある) であるため、XMLとPYXとではストレージや伝送上の考慮事項はほとんど同じです。
PYX形式は、記述する点においても理解する点においても非常にシンプルです。各行の先頭文字が、その行の内容のタイプを示します。連続する行に同じ内容タイプが含まれる場合はありますが、内容が複数の行にまたがることはありません。タグ属性の場合、属性名と値は単にスペースによって区切られるだけです。引用符は使用されません。プレフィックスの文字は以下のとおりです。
(開始タグ)終了タグ
A属性
-文字データ (内容)
?処理命令
|
PYX形式ではいくつかの文字はエスケープ文字として表されます。文字データの中の改行文字は、\n エスケープ・コードを使用して常に別個の内容行で示されます。タブは\t エスケープ・コードとして表されます。そして、元のXML文書でタブが使われている場合、その通常の内容行 (新規の行ではなく)エスケープのために使用されるバックスラッシュは、それら自身\\ と表記します。スペースはエスケープ文字として表さないことに注意してください。通常いくつかの内容行は全体がスペースで構成されます (テキスト・ビューアーでは見ても分かりにくい)。行は、プラットフォーム固有の改行文字で終了します。
PYXを使う理由としては、幅広い用途と、便利さ、そして行指向のテキスト処理ツールや技法とのなじみが良いという点を挙げることができます。たとえば、wc、tail、head、およびuniqなどのツールが組み込まれているGNU textutilを使用することができます。他にもgrep、sed、awkなどのテキスト処理ツールや、perlおよび他のスクリプト言語など、良く知られている優れたツールを使用できます。こうした種類のツールを使用するには通常、改行文字で区切られたレコードである必要があり、テキストの各部を識別するために正規表現を使用する必要があります。偶然にも、こうした必要条件はどれも、XMLとは全く相いれないものです。
PYXの使用
では実際にPYXについて見てみましょう。PYXライブラリーはさまざまなプログラミング言語に対応していますが、ほとんどの場合、コマンド行ツールxmlnおよびxmlvを使用するのが最も効果的です。xmlnは検証を行わない変換ツールで、xmlvはDTDに対して検証を追加したものです。expatおよびrxpパーサーはこれらのツールにコンパイルされていますが、ユーザーはこれらのパーサーのAPIに関して心配する必要はありません。
リスト1. XML文書とPYX文書
[PYX]# cat test.xml
<?xml version="1.0"?>
<!DOCTYPE Spam SYSTEM "spam.dtd" >
<!-- Document Comment -->
<?xml-stylesheet href="test.css" type="text/css"?>
<Spam flavor="pork" size="8oz">
<Eggs>Some text about eggs.</Eggs>
<MoreSpam>Ode to Spam (spam="smoked-pork")</MoreSpam>
</Spam>
[PYX]# ./xmln test.xml
?xml-stylesheet href="test.css" type="text/css"
(Spam
Aflavor pork
Asize 8oz
-\n
-
(Eggs
-Some text about eggs.
)Eggs
-\n
-
(MoreSpam
-Ode to Spam (spam="smoked-pork")
)MoreSpam
-\n
)Spam
|
これらを比較すると、元のXML文書からDOCTYPE宣言とコメントが削除されていることが分かります。ほとんどの場合、この部分は重要ではありません (パーサーも多くの場合、この情報を破棄します)。PYX形式は、XML形式とは対照的に、文書に関してさまざまな質問を随時簡単に提起することができます。例えば、このサンプル文書の中で使われている属性値すべてには何があるか ? という質問があるとします。その場合、PYXを使えば、次のように簡単に照会できます。
リスト2. PYX形式を使用した照会 (属性)
[PYX]# ./xmln test.xml | grep "^A" | awk '{print $2}'
pork
8oz
|
元のXML文書でこの質問に対する答えを得ることは、かなり難しい問題となります。パーサーを呼び出してタグ属性辞書を探し出すプログラムを新たに作成するか、必要な情報を見つけられるように複雑な正規表現を加えることが必要になるでしょう。複雑なのは、<MoreSpam> 要素の内容です。これには、属性に非常に良く似ているものの、実はそうではないものが含まれています。
次に、PYXによってシンプルになる別のタスクについて考えるために、XML文書の空でない内容行をダンプしましょう。ある人はSAXを使ってこのタスクを行うかもしれませんが、そのためにはcharacters() ヘッダーと、いくつかの他のハンドラーのための空のスケルトンを含んだ、小さなアプリケーションを作成する必要があります。私たちが好むのはHTMLファイルに適用されるlynx -dump のようなものかもしれません。言い換えれば、1行で済むも
リスト3. PYX形式を使用した照会 (内容)
[PYX]# ./xmln test.xml | grep '^-[^\n ]' | sed s/^-//
Some text about eggs.
Ode to Spam (spam="smoked-pork")
|
Sean McGrath氏のコラム (参考文献 を参照) には、いくつかの例が紹介されています。
XMLに戻す
PYX形式は非常にシンプルであるため、有能なプログラマーであれば1時間ほどでPYX2XMLツールを作成できます。そして各行が、出力のために何が必要か、またその行がタグ、PI、内容のどれであるかを正確に示します。
PYX2XML変換にはごくわずかな記述しかありません。特に、開始タグがある場合、それに続く行 (無制限) にはそのタグの属性が含まれています。属性 (存在する場合) の出力の後は、終了タグである不等号括弧が必要です。開始タグが検出された時点では、変換ユーティリティーは属性 (存在する場合) の数について検知していません。そのため、"looking-for-attributes" 状態を真または偽に設定する必要があります。
残念なことに、PYX2XML変換は非常にシンプルであるにもかかわらず、pyx2xml.pyツール (SourceforgeのPyxie Webサイトで入手可能。参考文献を参照。) は驚くほどさまざまな点で問題があります。生成されたXML文書は整形式ですが、奇妙なスペースが加えられます。しかしさらに悪いことに、このツールの短いスクリプトをクラッシュさせるプログラミング・エラーがあります。ではここで、読者の皆さんのために実際のインプリメンテーションを紹介しましょう。
リスト4. PYX-to-XML変換のためのPythonスクリプト
import sys, os, xreadlines
unescape = lambda s: s.replace(r'\t','\t').replace(r'\\','\\')
write = sys.stdout.write
get_attrs = 0
for line in xreadlines.xreadlines(sys.stdin):
if get_attrs and line[0] <> 'A':
get_attrs = 0 # End of tag attribues
write('>')
if line[0] == '?': # Proc Instr
write('<?%s?>\n' % line[1:-1])
elif line[0] == '(': # Open tag
write('<%s' % line[1:-1])
get_attrs = 1
elif line[0] == 'A': # Tag attrib
name,val = line[1:].split(None, 1)
write(' %s="%s"' % (name, unescape(val)[:-1]))
elif line[:3] == r'-\n': # Newline
write(os.linesep)
elif line[0] == '-': # Misc content
write(unescape(line[1:-1]))
elif line[0] == ')': # Close tag
write('</%s>' % line[1:-1])
|
その他の考慮事項
Pyxieプロジェクトのページでは、pyxieと呼ばれるPythonモジュールが紹介されています。そこでは、ツリー・ベースまたはイベント・ベースのスタイルでPYXコード化文書を処理する、多くのクラスが用意されています。PYX形式をさまざまな用途に適用させる場合 (そしてPythonを使用する場合)、これらのクラスは使用する価値があるかもしれません。しかしある意味で、これらのクラスはいくらか的はずれのようにも感じます。PYX形式の長所はそのシンプルさであり、行指向ツールによるアクセシビリティーなのです。
XML文書のメモリー内木表現が必要なら、DOMはすでにそれを行うことができます。これらのDOMよりいくらか単純なAPIを望む場合、Pythonのxml_objectify、PerlのXML::Parser、RubyのREXML、そしてJavaのJDOMなどのモジュールを使用して木構造を取得することもできます。pyxieも同じ目的で使用できますが、実質利点はありません。同じように、XML文書を一般的なイベント指向で処理したい場合、SAXまたはexpatを使用できます。pyxieを使っても特別な利点はありません。
データの階層構造に多少こだわる方法でPYX文書を処理しなければならない場合もあります。この場合、ある時点でSAXまたはDOMで生じるような複雑さの問題に後戻りして、PYXの意義が失われてしまいます。しかし、あまり複雑でない初期の段階で、PYXを階層的に処理するために実際に必要なデータ構造とは、タグ・スタックです。これは、非常にシンプルなデータ構造です。
例えば、上記のサンプル文書を順次処理する場合には、以下のスタック操作を実行します。
1. Push "Spam"
2. Push "Eggs"
3. Pop ("Eggs")
4. Push "MoreSpam"
5. Pop ("MoreSpam")
6. Pop ("Spam")
|
このような単純な例では、スタックが2項目以上の深さになることはありません。しかし、一般的にはさまざまな深さになります。pushするときとpopするときを知っていると非常にシンプルになります。開始タグの行が検出されるときにPushし、終了タグの行が検出されるときに Popします。Pop操作は、常にスタックによってpopされる最後のものであるため、終了タグのストリングを認識する必要さえありません。実際に、PYX形式で終了タグ・ストリングがないとしても、何か情報が失われることはありません (しかし、スタック・カウントを必要 としない、終了タグの自動識別という便利さは失われるかもしれません。例えば、PYX2XMLは書き込みが難しくなる場合があります)。
PYXファイルの行ごとの処理の各ポイントでは、1つのスタックがすべてを示し、現在の行の階層的なコンテキストについて知ることができます。ある場合、スタック内を単に覗くことによって、XPathスタイルの修飾子を構成することもできます。内容または属性に関するある操作は、このテキストに依存している場合もあるかもしれません。この種の処理は、基本的なテキスト・ユーティリティーで通常行える処理を少し超えることになりますが、それでもなおXMLパーサー/インターフェースの完全なAPIよりも限定的で、柔軟で、そしてシンプルなのです。
結論
XML APIの設計者たちは、残念ながらKISS原則 ("Keep It Simple, Stupid" (こら短くしとけ)) をすっかり忘れてしまっています。DOM、SAX、またはXSLTのフルパワーを引き出し、必要とするアプリケーションは、確かに存在します。しかし、非常に多くのXMLアプリケーションでは、一般的なAPIによって、その日暮らしのプログラマーが入って来られないように不必要なバリアが作り出されているのです。幸いなことに、このような無駄な複雑さを避ける方法があります。このコラムでは、本来シンプルなものをシンプルにするために行える情報を紹介しました。こうした試みはこれからも続けていきたいと思います。
参考文献
- Sean McGrath氏のintro to the PYX format の記事を参照してください。
- McGrath氏は、Pythonと組み合わせるPYXの幅広い使用法に関しても、自著XML Processing with Python (Prentice Hall, 2000) で紹介しています。この書籍は、私が「魅力的なPython」コラムのPython関連書籍の最新情報で書評を述べているものの1つです。
- PYXを処理する (変換する) ためのPerlライブラリーを参照してください。
著者について  | 
|  | David Mertz氏は多くの分野で活躍しています。ソフトウェア開発や、それについて著述もしています。その他、学術政策理念について分野を問わず、関係する雑誌に記事も書いています。かなり以前には、超限集合論、ロジック、モデル理論などを研究していました。その後、労働組合組織者として活動していました。そして、David Mertz氏自身は人生の半ばにもまだ達していないと思っているので、これから何かほかの仕事をするかもしれません。 |
記事の評価
|