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

developerWorks Japan  >  XML  >

XMLの論考 第11回: xml_pickleおよびxml_objectifyの再考

オープン・ソースのレッスンと常識

developerWorks
ページオプション

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

原文はこちら

原文はこちら


レベル: 初級

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

2001年 6月 01日

著者のDavid Mertzが、高水準言語Pythonによる、XML文書処理に便利なユーティリティーを初めて紹介して以来、ユーザーや読者から非常に有用な拡張や提案が数多く寄せられました。この記事では、彼のモジュール・スイートに対して行われた変更の一部をご紹介するほか、これらのモジュールの高度な使用とカスタマイズに関するヒントも提示します。コード・サンプルでは、py_obj._XML 属性、オブジェクトやリストとして扱われるノード属性、py_obj マジック属性の振る舞いなどのデモンストレーションを行います。

私がこれまで書いてきたIBMのコラム、チュートリアル、および記事には2つ (3つかもしれませんが) の目的がありました。第1の目的は、私が持っている知識を他のプログラマー / デベロッパーの人たちに伝え、そして、読者の作業をいくらかでも簡単にできるかもしれない機会を大事にしたいということです。(これらの記事の執筆料をいただけるのも、非常にありがたいことです。)

新しい可能性

私の記事のもう1つの目的は、プログラミング・コードをパブリック・ドメインにリリースすることです。このコードを作成するときの私の目標は、一般的なプログラミング概念を具体的に示すことにありましたので、この概念に沿ってコードを調整しました。しかし同時に、個々のデベロッパーがそれぞれの目的のために直接利用できるようなコードをプログラミング・コミュニティーに提供するつもりです。

コードをリリースしているときに、私は、これらのモジュールのユーザーから、多くの貴重な提案や拡張パッチを受け取りました。ユーザーから寄せられた改善案の多くは、私がそれまで考えたこともないようなものであり、いくつかの改善案は、ユーザーの洞察力の深さを示す衝撃的と言ってもいいくらいのものでした。この記事では、xml_picklexml_objectify の使用例をいくつか示します。これらは、私が「XMLの論考: 第1回」と 「XMLの論考: 第2回」(これらのモジュールを最初に検討した記事) を書いたときは、まだ使用できませんでした。




上に戻る


xml_objectifyの拡張

特に、1つの変更が今も苦労の種になっています。多分、タイミングが少し良くなかったのだと思います。私が2000年8月にxml_objectifyxml_pickle を初めて作成した後、すぐにPyXML配布で何回か非互換バージョンが出ました。間もなく、XMLサポートが完全互換ではないPython 2.0が出ました。ユーザーは当時最新であったPython XMLサポートに合わせるためにいくつかのパッチを当てましたが、現在では、xml_objectifyxml_pickle の両方にPython 2.0+ およびその組み込みPyXMLパッケージが必要になっています。XMLパッケージに関してPython 2.0の要件が出てきたので、私はほかにもいくつかの変更をPython 2.0構文に加えました。Python 1.5に逆方向の互換性がないことは残念な限りですが、この場合それを維持するのは荷が重過ぎます。

私が「XMLの論考: 第2回」で紹介したxml_objectify フィーチャーの1つは、完全なエレメント・コンテンツ (文字データのサブエレメント・マークアップを含む) を保持する特殊_XML 属性でした。デフォルトの振る舞いは、今も、ネストされたオブジェクトに文字レベルのマークアップが含まれている場合だけ、その_XML 属性を作成します。しかし現在では、keep_containers() 関数や、ALWAYSMAYBENEVER などの値を使用してこの振る舞いを変更できるようになっています。たとえば、


リスト1: デフォルトのpy_obj._XML属性の作成
                
>>> xml_str ='''<doc><p>Spam and eggs <b>are</b> tasty</p>
..                  <p>The Spanish Inquisition</p>
..                  <foot>Our weapon is fear</foot></doc>'''
>>> open('test.xml','w').write(xml_str)
>>>from xml_objectifyimport *
>>> py_obj = XML_Objectify('test.xml').make_instance()
>>> py_obj.p[0].PCDATA
u'Spam and eggs  tasty'
>>> py_obj.p[0]._XML# 一つ目の <p> は <b> マークアップを含む
u'Spam and eggs <b>are</b> tasty'
>>> py_obj.p[1].PCDATA
u'The Spanish Inquisition'
>>> py_obj.p[1]._XML# 二つ目の <p> はマークアップを含まない
Traceback (most recent call last):
File"<stdin>", line 1,in ?
AttributeError:'_XO_p' instance has no attribute'_XML'


リスト2: py_obj._XML属性作成の変更
                
>>> _=keep_containers(ALWAYS)
>>> py_obj = XML_Objectify('test.xml').make_instance()
>>> py_obj.p[1]._XML
u'The Spanish Inquisition'
>>> _=keep_containers(NEVER)
>>> py_obj = XML_Objectify('test.xml').make_instance()
>>> py_obj.p[0]._XML
Traceback (most recent call last):
File"<stdin>", line 1,in ?
AttributeError:'_XO_p' instance has no attribute'_XML'

xml_objectify の最も強力なフィーチャーは、理解しがたいフィーチャーのようです。多分、多くのユーザーは、クラス・マジックの振る舞いを必要と感じたことはないでしょう (いや、それに気付いたことさえないでしょう)。しかし、"オブジェクト化された" XMLノードの振る舞いを決める特殊クラスを用意しておくことはできます。(このことについては初回の記事で説明していますが、実行過程を見る価値はあります。)

例を出す前に、いくつか細かい点に触れておく必要があります。最初のモジュール・バージョンでずさんな矛盾が起こらないようにするために、xml_objectify では、XMLノードのクラス・テンプレート名を "細分化" します。"抽象"ノード・クラス、つまり_XO_ は、それ自体でいくつかの "マジック" 振る舞いを持っています。動的な作成であれ、プログラマーによる作成であれ、作成された具体的なノード・クラスは、_XO_tagname 形式になっています (この場合、<tagname> は、具体化されたXML文書に現れるタグです)。

_XO_ そのものが提供する "マジック" は、__getitem__() および__len__() メソッドです。これらのメソッドを使用すれば、各ノード属性をリストのように振る舞わせるのが好ましいコンテキストでは、ノード属性をリストであるかのように扱うことができます。しかし同時に、添え字なしで "一人っ子" ノードを参照することもできます。たとえば、


リスト3: オブジェクトおよびオブジェクトのリストとしてのノード属性
                
>>> print type(py_obj.p), type(py_obj.foot)
<type 'list'> <type 'instance'>
>>> print py_obj.p[1].PCDATA, '...', py_obj.foot.PCDATA
The Spanish Inquisition ... Our weapon is fear
>>> for line in py_obj.p: print line.PCDATA,
..
Spam and eggs  tasty The Spanish Inquisition
>>> for line in py_obj.foot: print line.PCDATA,
..
Our weapon is fear
>>> map(lambda line: len(line.PCDATA), py_obj.foot)
[18]
>>> map(lambda line: len(line.PCDATA), py_obj.p)
[20, 23]

ユーザーがプログラム内で独自のノード・クラスを作成したい場合は、さらに多くのマジックが可能になります。基本的には、以下に示されているように、ユーザーが望む任意の 方法で属性ノードを振る舞わせることができます。


リスト4: py_objのマジック・ノード振る舞いの作成
                
>>>import xml_objectify
>>> xml_str ='''<buffet>
.. <plate><food>Steak</food><food>Potatoes</food></plate>
.. <plate><food>Corn</food><food>Broccoli</food></plate>
.. <buffet>'''
>>> open('buffet.xml','w').write(xml_str)
>>>class
                
                     plate
                (xml_objectify._XO_):
..def
                
                     eat
                (self):
..for foodin self.food:
..if food.PCDATA =='Broccoli':
..return"If I liked Broccoli, I might have to eat it!"
..return"Yum!"
..
>>> xml_objectify._XO_plate = plate
>>> py_obj = XML_Objectify('buffet.xml').make_instance()
>>>print py_obj.plate[1].eat()
If I liked Broccoli, I might have to eat it!
>>>print py_obj.plate[0].eat()
Yum!

この場合、xml_objectify._XO_plate 割り当てによるトリックが重要です。正しいマジック振る舞いを行うには、適切なマジックおよび細分化されたクラスがそのネームスペースに入っていなければなりません。

一群のデータをXMLファイルから取得し、独自のメソッドを使用して完全に自然なPythonオブジェクトにそのデータを処理させるのは実にすばらしいことだと私は思っています。

EXPAT技法

Costas Malamasは、大型XML文書の処理について非常に貴重な拡張を行いました。最近まで、xml_objectify の唯一の機能方法は、DOMツリーを作成し、そのツリーをたどって "Python風の" オブジェクトを生成することでした。この方法は小さなXML文書には効果的に働きましたが、50k~100k規模のファイルになると、話にならないほど遅くなり始めたものです。順列組み合わせで複雑さが爆発して、xml_objectify が大型文書で使用不可能になっているように思われます。

運良くMalamasが、PythonEXPAT バインディング (EXPATは、CのハイパフォーマンスXMLライブラリーです) に基づいてXML文書を構文解析する代替メソッドを提供しました。ExpatFactory クラスにはまだいくつか欠点 (処理命令による一部の文書の失敗) がありますが、たいていの場合、新規の技法が大型XML文書も迅速に処理します。

EXPAT技法は、設計上の2つの制限も課します。このため、xml_obj_dom 属性が失われるのは明らかです (xml_obj を最初の場所に保持していた場合)。また、_XML 属性が使用できなくなります。しかし、後者の制限は将来解決される可能性があります。

使用する構文解析技法の選択は簡単です。


リスト5: 構文解析メソッドの選択
                
>>> xml_obj = XML_Objectify('buffet.xml',EXPAT)
>>> xml_obj = XML_Objectify('buffet.xml',parser=DOM)

オプションの指定がない場合は、レガシーDOM技法がデフォルトになりますが、このデフォルトが変わった場合は、将来のコードで明示的に指定しなければなりません。EXPATDOM は、一致するストリング値だけが含まれているxml_objectify 内の定数です。





上に戻る


xml_pickleの拡張

"非pickle" オブジェクトのインスタンス・メソッドを保存したい場合は、xml_objectify と同様の方法でxml_pickle ネームスペースを移植する必要があります。このやり方は紛らわしいように見えますが、以下のようなコーディングで単純にすることができます。


リスト6: 非pickle Pythonオブジェクトが活動していることの確認
                
>>>import xml_pickle
>>>classMyClass:
..defDoIt
                (self):
..print"Done!"
..
>>> o1 = MyClass()
>>> o1.attr1 ='spam'
>>> xml_str = xml_pickle.XML_Pickler(o1).dumps()
>>> o2 = xml_pickle.XML_Pickler().loads(xml_str)
>>> o2.DoIt()
Traceback (most recent call last):
File"<stdin>", line 1,in ?
AttributeError:'MyClass' instance has no attribute'DoIt'
>>> xml_pickle.MyClass = MyClass
>>> o2 = xml_pickle.XML_Pickler().loads(xml_str)
>>> o2.DoIt()
Done!

すべてのpickle / 非pickleを開始する前に、pickleにしたいクラスをxml_pickle ネームスペースに入れる場合は、基本的には、すべてのオブジェクト振る舞いを復元することができます。ただし、picklecPickle の場合と同様に、これらのメソッド自体はpickleにされません (単に属性だけがpickleされます)。ユーザーは実行時に存在しているメソッドのクラスを使用します (このクラスは、最後のpickle以降のより新しいクラスです)。




上に戻る


循環参照およびディープ・コピー

Joshua Macy (Joe Kraskaの助けを借りて) は、私が最初の記事で指摘したxml_pickle の制限を取り除きました。初期バージョンでは、xml_pickle は、pickleしたオブジェクトの循環参照を検査しませんでした。さらに (および同じ理由で)、初期バージョンは、すべての属性をその実際のPythonオブジェクトのディープ・コピーとしてpickleしました。同一オブジェクトの参照が入っている多くのサブストラクチャーがPythonオブジェクトに含まれている場合は、pickleされたサイズが急速に膨れ上がることがあります。その上、非pickleにされたオブジェクトには、pickleにされたオリジナルと等しい (a == a) 可能性はあるが、同じ (a is a) ではない複数のオブジェクトが含まれることになります。

しかし、Macyのアプローチに利益はあるものの、DEEPCOPYオプションをモジュールに導入することが望ましいです。refid/id スキーム (非常に優れています) の主な問題点は、汎用ツールで使用するには相当無理があるように思われることです。ことによると、Python以外の言語のユーザーは、xml_pickle されたオブジェクトを手軽に使用したいと思っているのかもしれません (多分、完全に動的なオブジェクトとしてではなく、階層データ・ストアとして。しかし、それはそれとして結構なことです)。あるいは、ことによると、pickleされたオブジェクトのXSLT変換は特定の目的に有効かもしれません。以下のpickleされた抜粋からこの難しさがわかります。


リスト7: XMLとしてのpickleにされたPythonオブジェクト
                
<?xml version="1.0"?>
<!DOCTYPE PyObject SYSTEM"PyObjects.dtd">
<PyObjectclass="XML_Pickler" id="1383532">
<attr name="lst" type="list" id="1391340">
<item type="numeric" value="1" />
<item type="numeric" value="3.5" />
<item type="numeric" value="2" />
<item type="numeric" value="(4+7j)" />
</attr>
<attr name="lst2" type="ref" refid="1391340" />
<attr name="num" type="numeric" value="37" />
..
</PyObject>

lst2 属性を一般的な方法 (デベロッパーの目) で解釈するのはちょっと骨が折れることが分かります。refid から手を引いて、対応するid を検索し直す必要があります。実際、type="ref" XML属性の使用は良くない選択だったかもしれません。refid XML属性を持って いたとしたら、lst2 参照lst の場合と同様に、単にtype="list" を記録することで、もっと分かりやすいものになったかもしれません。しかしもちろん、いったん何かを行えば、逆方向互換性を切断しない限り、それを改善するのは困難です。

参照についてのちょっとした注意をすると、鋭いハッカーには役立つかもしれません。id/refid 値は、関係のあるオブジェクトのPythonid() から生まれます。これらの値は固有な意味は持っていませんが、実行中の任意の時点では固有であるという優れた特性をもっています。xml_pickle は、異なる実行の "同じ" オブジェクトをpickleした場合に、まったく同じXMLファイルが作成されるということを保証するものではありません (id 値が、まず確実に変わります)。一般に、随時のid 値はプログラムには関係しませんが、暗号ハッシュやCRCなどをプロセスの一部として使用する場合は、"不具合" になる可能性があります。

機能強化にあまり説明は必要ありませんが、ユーザー要求に応答する場合は、"pickle可能" タイプのセットにNumeric 配列が追加されます。数理科学関係のPythonユーザーの場合は、これらのタイプは、オブジェクトの重要な属性を形成します。xml_pickle は、Numeric をサポートしているときに、それが存在していることをインテリジェントに確認します。確認できなければ、array モジュールへ戻ります。




上に戻る


Python公理

私がこれらのモジュールを開発しているとき、というより単にモジュールの開発の世話をしているときに学んだ1つの教訓は、Python公理「まず、それを正しく理解し、次に、それを素早く実行する!」の価値でした。

総体的に言って、後者についてはかなり達成しました。xml_pickle に対して何回か最適化を施したことで、pickleにされたオブジェクト・サイズに関するその振る舞いがO(N^2) から管理可能なO(N) に変わりました。ここでの注意点は、それを何度も行っていると、str = str + "more stuff" の効率がびっくりするほど悪くなる場合があることです。EXPAT技法を使うと、xml_objectify も同様に速くなります。私が当初から最適化を気にしすぎていたら、何かについての研究を迅速に世間に発表することもなかっただろうし、貴重な力添えを得ることもなかったと思います。

私がこの記事で検討してきたようなツールやライブラリーを作成できる間、オープン・ソース・ソフトウェア開発の実際的な社会動的性についてもっと学びたいと思います。それは興味をそそる過程ですが、それがどこへ続いているのかは分かりません。



参考文献

  • 著者David MertzのXMLモジュールを、xml_objectify.py およびxml_pickle.py で見つけてください。

  • 古いバージョン番号またはプレリリースのモジュールに関心がある場合は、gnosis.cx/download ディレクトリーをブラウズしてください。ここには、各種のバージョン (名前にバージョン番号が含まれている)が含まれています。バージョン番号のないモジュールは、通常、最新の "安定した"バージョンです。さらに、このディレクトリー (すべてパブリック・ドメイン)には、多くの他の製品も含まれています。

  • David Mertzによるxml_pickle およびxml_objectify に関する初期記事 (2000年8月) をIBM developerWorksで見つけてください :XMLの論考: 第1回 およびXMLの論考: 第2回

  • David Mertzによる以前の「XMLの論考」コラムの他の記事を、次の参考文献から見つけてください。
    • XMLの論考: 第3回 はDocBookを紹介しています。
    • XMLの論考: 第4回 は、引き続き、DocBookによるレガシー文書アーカイブの作成方法を検討しています。
    • XMLの論考: 第5回 は、XML文書をHTMLへ、さらにXSLTへ変換する方法について説明しています。
    • XMLの論考: 第6回 は、テキスト主体の文書に適したXMLエディターに焦点を当てて、いくつかのXMLエディターを比較しています。
    • XMLの論考: 第7回 は、DTDとXML Schemaを比較検討し、W3C XML Schema仕様が完成しているにもかかわらず、デベロッパーがDTDを使用したほうがよい場合を提案しています。
    • XMLの論考: 第8回 は、コンピューター科学者によって概念化されたデータ・モデル の抽象理論が、いかに特定の多重表記データ・フローの開発に役立っているかを説明しています。
    • XMLの論考: 第9回 は、RDBMSから独立してポータブルXML結果セットを生成できるようにするパブリック・ドメインsql2dtd およびsql2xml ユーティリティーについて検討しています。
    • XMLの論考: 第10回 は、インデクサーがどのようにしてXMLの階層ノード構造を利用できるかを検討しています。

  • WebSphere Application Serverアドバンスト版3.5オンライン文書の参照セクションに記載されているXML DOMについて勉強してください


著者について

Photo of David Mertz

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




記事の評価


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



 


 


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


この記事を共有する

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について プライバシー お問い合わせ