IBM®
本文へジャンプ
    Japan [変更]    ご利用条件
 
 
検索範囲検索:    
    ホーム    製品    サービス & ソリューション    サポート & ダウンロード    マイアカウント    
skip to main content

developerWorks Japan  >  XML  >

XMLの論考: 第2回

XML文書をオブジェクトとして "Python 風" に処理する方法 (II)

developerWorks
ページオプション

JavaScript を要するドキュメントオプションは表示されません

原文はこちら

原文はこちら


レベル: 中級

David Mertz, Ph.D (mertz@gnosis.cx), Author, Gnosis Software, Inc.

2000年 8月 01日

この記事は、『XML Matters』コラムの第 2 回目となりますが、XML と Python の間のよりシームレスな統合を実現する絶え間ない探求の一環として、David Mertz がxml_objectify モジュールを紹介します。David は、xml_objectify の使用方法と、XML 文書をオブジェクトとして処理する際にこの "Python 風" モジュールを使用する利点について説明します。

プロジェクトの紹介

XML の論考 では、XML と Python の間のよりシームレスで自然な統合を作成するという私の携わっているプロジェクトを紹介しました。これに関連して『参考文献』のセクションには、一般的な Python のプログラミング手法やその他の XML/Python トピックについて説明している他のdeveloperWorks 記事へのリンクが含まれています。

互換性のある XML-SIG
アップデートの入手

XML-SIG ディストリビューションは、ベータ版でかなり頻繁に変更されます。これらの変更点は、xml_objectify が機能する方法に影響を与える可能性があります。したがって、『参考文献』から、xml_objectify と互換性のあることが知られている XML-SIG バージョンをダウンロードすることができます。

XML-SIG ディストリビューションが公式にリリースされるときや、XML パッケージが公式の Python リリースの一部であるときは、現在のxml_objectify が、公式リリースで機能するように更新されます。最新のxml_objectify については、『参考文献』を参照してください。

XML と Python の間に完全な対応関係があるわけではないので、このプロジェクトには、(少なくとも初めは) 2 つの異なるモジュールが含まれています。xml_pickle は、XML で任意の Python オブジェクトを表すためのものであり、xml_objectify は、XML 文書を Python オブジェクトとしてネイティブに表すためのものです。この記事では、xml_objectify について扱っています。

Python では、xmllibxml.saxpyxie、およびxml.dom などのモジュールやパッケージにより、XML コミュニティーで一般的な XML 文書処理法を提供します。読者は他のプログラム言語で使用可能な同様のモジュールやライブラリーについてご存じかもしれません。実際、これらのモジュールの多くは、言語に関して中立な XML 標準に基づいており、XML を中心として文書やオブジェクトを処理する方法を共通にインプリメントしています。

Python には一般的な XML プロトコルがインプリメントされているため、プログラミングをさまざまな方法で柔軟に行うことができます。たとえば、DOM などの移植可能な標準を使用すれば、1 つの言語を使用するプログラマーが、他の言語で作成された DOM 指向のコードを容易に処理することができます。しかし、Python のプログラマーの中は、"通常の" Python にもっとよく似た方法でコーディングすることを好む人がいます。多くの場合、XML 概念フレームワークは Python の統合されているものというよりも、Python に後から貼り付けたかのように見えます。それで私は、XML 文書を処理する "Python 風" モジュールのセットを開発しました。




上に戻る


本論: xml_objectify の使用方法

xml_objectify の使用は単純であり、モジュールの文書ストリング・コメントでよく説明されています。いくつかのサンプル・コードを少しだけ見てみましょう。


XML 文書からの Python オブジェクトの作成
                
                
from xml_objectifyimport XML_Objectify
xml_obj = XML_Objectify('address.xml')
py_obj = xml_obj.make_instance()

見て分かるように、汎用の XML 文書からオブジェクトの Python オブジェクトを作成するには 2 つのステップが関係します。まず、DOM に似た中間的なファクトリー・オブジェクト (つまり、他のオブジェクトの作成に使用するオブジェクト) を作成します。2 番目に、XML_Objectify インスタンスから 1 つまたは複数の Python オブジェクトを生成します。特殊なPyObjects.dtd 形式の文書を処理するには、xml_pickler を使用します。(xml_pickle については、『XML の論考』を参照してください。)

両方のステップを同じ行で行うこともできます。以下に例を示します。


インラインでの XML/Python オブジェクトの作成
                
py_obj = XML_Objectify('address.xml').make_instance()

もちろん、後者の場合、ファクトリー・オブジェクトを保存してネイティブのオブジェクトをさらに生成することはできず、完全な DOM インスタンスを含む._dom データ・メンバーもクリアされます。

比較のために、以下の例では、DOM オブジェクトの作成が Python の場合と同じように単純であることを示しています。


XML 文書からの DOM オブジェクトの作成
                
from xml.dom.utils import FileReader
dom_obj = FileReader().readXml(open('address.xml'))

FileReader().readXml() には実際のファイル・オブジェクトが必要ですが、XML_Objectify() ではファイル・オブジェクトかプレーン・ファイル名を使用できます。いずれの場合でも、オブジェクトの作成には 2 つの行を使用します。

xml_objectify モジュールを使用する場合とxml.dom パッケージを使用する場合で違うところは、関連付けるオブジェクトのタイプです。Python DOM オブジェクトは完全な Python オブジェクトですが、その属性とメソッドは、XML_Objectify オブジェクトの属性とメソッドほどには、元の XML 文書のデータと構造に対応していません。一般に、Python DOM オブジェクトの属性はネストされた.children リストですが、これはあまり意味のあるものではありません。サンプル文書にある同じ XML 属性にアクセスするには、最初の行でxml_objectify を使用するか、次の 4 つの行で DOM を使用するかを選択することができます。これについて以下に示します。


[xml.dom] の使用と [xml_objectify] Python オブジェクトの使用の比較
                
                
print py_obj.person[1].address.city
print dom_obj.get_childNodes()[1].get_childNodes()[3].\
      get_childNodes()[3].get_attributes()['city'].value
print dom_obj._node.children[1].children[3].children[3].\
      attributes['city'].children[0].value

DOM ツリーは、厳密に配列されたノードのツリーとして編成されます。これらのノードを順番に参照するのは難しくありませんが、特定のノードを参照するのはかなりめんどうです。問題をさらに悪くしているのは、空白テキスト・ノードや処理命令ノード (めったに考慮する必要はない) が混在しているということです。サブタグをノード・リストで見付ける作業は、ほとんど試行錯誤となります。上記の例では、ネイティブの属性 (たとえば、.children) と DOM スタイルのメソッド (たとえば、.get_childNodes()) へのアクセスが、異なるprint ステートメントで使用されています。いずれにしても、XML 文書内で参照されているデータを見付けるのは簡単ではありません。

対照的に、上記の例の最初のprint ステートメントは、分かりやすい説明となっています。注意点は、Python のゼロから始まるリスト索引付けを使用しなければならないという小さな点だけです。したがって、この行は単に、「住所録中の 2 番目の人物の住む都市名を印刷する」と述べています。(各ステートメントでは "New York" が印刷されます。) さらに、py_obj.__class__ が、XML 文書のルート要素に対応する "住所録" になっています。また、単純なテキスト以上のものを含む各属性は、該当するものを定義する XML タグに対応する名前のついたクラスのインスタンスになっています。

見て分かるように、xml.dom は一般に使いにくく、その構文は不明瞭です。これに対しネイティブの Python オブジェクトはずっと使いやすいものです。 xml_objectify は DOM を内部で広く使用することに注意してください。実際、各XML_Objectify インスタンスは、開いている XML 文書の DOM ツリーである._dom 属性を含んでいます。ただし、作成されたインスタンス.make_instance は、DOM を含まず、ルート・タグのクラス・タイプになっています。




上に戻る


設計に関する考慮事項、制限、および警告

コードの内観

xml_objectify を使用すれば、既存の汎用関数をすべて利用することができます。 pyobj_printer() は、xml_objectify モジュールに付属するサンプルの汎用関数です。この関数は、あらゆる Python オブジェクトを読み取り可能な再帰的表現として生成します。XML 文書をネイティブの Python 文書として表すことにより、抽象的な仕方で Python オブジェクトを処理する既存の関数を再利用することができます。もちろん、DOM オブジェクトは一種の Python オブジェクトですが、有用な仕方でこれらのオブジェクトに汎用関数を使用するのは困難です。たとえば、DOM オブジェクトの属性はネストされた.children リストなので、pyobj_printer() などの汎用関数を使用しても、それほど役立つ出力は生成されません。

クラスの動作に含まれるトリック

xml_objectify は、ちょっとしたトリックを使って、まだ定義されていない属性値のクラスだけを動的に定義します。これにより、複雑な動作や属性を持つクラスを定義し、特定の XML 文書内容をその中に入れることができます。たとえば、クラスperson が定義されており、さまざまなメソッド (必要に応じて.__init__() メソッドを含む) を持っているとします。上記の例でインポートされた XML 住所録内の各 "person" は、インスタンスに入れられたデータを操作するメソッドを含め、指定されたすべての動作を持ちます。もちろん、XML_Objectify() を文書に対して実行する前にクラスを事前定義していなければ、クラスは実際の XML で定義された属性のコンテナーにすぎません。

文字マークアップの処理

通常、XML タグはブロック・レベルですが、文字レベルのものもあります。私の意見では、自然な Python 表現は場合によって異なります。ブロック・レベルのサブタグは、そのサブタグにちなんだ名前を持つ親タグの属性によって容易に表されます。このサブタグ属性の値は新しい Python オブジェクトです。このオブジェクトのタイプも、サブタグにちなんだ名前を持っています。たとえば、階層的に見て、personaddressmisc-info を持ちます。Python では、これらをperson.address およびperson.misc_info として参照することができます。

タグの内容がテキスト・データとそのデータのマークアップ (活版印刷用のマークアップであることが多い) で構成される、文字レベルのタグの場合、サブタグは親タグが現実に階層内に持っているものではありません。たとえば、misc_info オブジェクトは、現実にital 属性を持っているわけではありません。では、以下のタイプの XML はどのように表すべきでしょうか。

 

<misc-info>One of the <ital>most</ital> talented actresses on TV.</misc-info>

xml_objectify は、マークアップされた文字データと考えられるオブジェクト/タグに、._XML という特殊な属性を追加します。この属性は、タグ内にリテラル XML を含んでいます。たとえば、pyobj_printer() 関数は、特定のネストされたオブジェクトに関して._XML が存在する場合に、再帰的属性の代わりにこのリテラル XML を表示します。ただし、依然として標準の再帰的サブタグ・オブジェクト作成は実行されるので、最も関係が深い属性と構造を知ることができます。

ネイティブの Python オブジェクトにはルート文書だけが含まれる

多くの XML 文書には、タグや文字データ内容とともに処理命令やコメントが含まれています。しかし、XML_Objectify オブジェクトの.make_instance() メソッドで作成された Python オブジェクトには、文書ルート・タグの内容しか含まれていません。さらに、XML コメントは無視されます。タグ属性と文字データだけが表現されます。

上記の『XML 文書からの Python オブジェクトの作成』の例では、元のXML_Objectify オブジェクト (xml_obj) を保存しておけば、.processing_instruction 属性や、._dom 属性にさえもアクセスし、ネイティブの Python オブジェクトから残された情報を知ることができます。

属性タイプの単純化

すべての XML 属性は、ストリング・タイプの Python オブジェクト属性に変換されます。現在、Python は、属性を XML 列挙タイプまたは数値タイプで表しません。このような機能は将来のバージョンで追加される可能性がありますが、一般には DTD が必要です。xml_objectify は DTD を想定していません。

サブタグ属性

XML サブタグは、同じタイプのサブタグが 1 つであるか複数であるかに応じて、オブジェクト・タイプの Python 属性、またはそのようなオブジェクトのリストによって表されます。これは、特定のタグに同じタイプである複数のサブタグが含まれているかどうかによって決まります。たとえば、上記の最初のaddress.xml 例では、1 人の連絡情報に 1 つの自宅電話番号が含まれ、他の人の連絡情報に 0 または複数の自宅電話番号が含まれる可能性があります。これに対応して、contact_info オブジェクトには、.home_phone 属性がないものと、1 つのhome_phone オブジェクトを含む.home_phone 属性があるものと、home_phone オブジェクトのリストを含む.home_phone 属性があるものがあります。DTD を使用すれば、さらに順序を多くすることができますが、私の意見では、Python アプリケーションにはこの種類の動的な機能が必要です。

Python のネーム・スペースに関する制約

Python のネーム・スペースは、XML のネーム・スペースよりも小さくなっています。したがって、タグや属性の XML 名が変更される場合があります。 xml_objectify は、ダッシュ、コロン、およびポンド/ハッシュ記号をアンダースコアに変換します。このモジュールは、これ以外のネーム・スペースの衝突を処理しません。たとえば、XML 文書にタグ<spam-eggs><spam_eggs><spam:eggs> および<spam#eggs> がある場合、xml_objectify が生成する Python オブジェクトに XML 文書を正しく表現したものにはなりません。ほとんどの場合、この種の競合文字を使ったタグを含む XML 文書を使用する可能性は少ないので、これは問題とならないでしょう。




上に戻る


xml_objectify の将来

現時点では、ネイティブの Python オブジェクトを、読み取られた元の構造と同じ XML 文書に戻すことはできません。この問題は、より分かりやすい Python オブジェクトを生成するためにxml_objectify が XML 文書内の順序に関する情報を意図的に除去するために起こります。Python 属性は事前に決定された順序を持ちませんが、特定の順序で XML タグや属性が必要になることがあります。XML タグが特定の順序で出現する必要がない場合であっても、意味的に順序が重要であることがあります。(共通するサブタグが繰り返される場合、Python のリストは順序を保持します。) XML に戻すには、任意の順序を受け入れるか、ネイティブの Python オブジェクト内に順序情報を保存して Python らしくないオブジェクトにするかを選択する必要があります。

Python オブジェクト内で除去された情報を再構成する 1 つのオプションは、XML に戻す際に DTD で検証を行うことです。私はこのオプションを追求しましたが、Python の実行時に追加、削除、または変更された属性をどのように処理するかという疑問が依然として存在しています。Python オブジェクトを変更すると、元の XML 文書の DTD に従わない情報が生成される可能性があります。しかし、ユーザーが特定の必要があると考えるのであればxml_objectify に機能を追加するつもりです。



参考文献



著者について

Photo of David Mertz

David Mertz氏は多くの分野で活躍しています。ソフトウェア開発や、それについて著述もしています。その他、学術政策理念について分野を問わず、関係する雑誌に記事も書いています。かなり以前には、超限集合論、ロジック、モデル理論などを研究していました。その後、労働組合組織者として活動していました。そして、David Mertz氏自身は人生の半ばにもまだ達していないと思っているので、これから何かほかの仕事をするかもしれません。David Mertz氏の連絡先はmertz@gnosis.cxです。




記事の評価


サイト改善のため、ご意見をお寄せください。こちらのフォームからお願いいたします。



はいいいえわからない
 


 


12345
不充分・不完全である大変素晴らしい
 


この記事を共有する

del.icio.us del.icio.us newsing newsing FC2ブックマーク FC2ブックマーク
Choix! Choix! ニフティクリップ ニフティクリップ Yahoo!ブックマーク Yahoo!ブックマーク
MM/memo MM/memo CZブックマーク CZブックマーク livedoorクリップ livedoorクリップ
はてなブックマーク はてなブックマーク Buzzurl(バザール) Buzzurl(バザール)




上に戻る


    日本IBMについて プライバシー お問い合わせ