目次


XMLの論考: オブジェクト・モデルとしてのXML-RPC

大衆のためのデータ・バンドルか

Comments

XML-RPCは、他のすべての競合物より悪いというすばらしい"美徳"を持つ、リモート関数呼び出しプロトコルです。Java RMI、CORBA、またはCOMと比較すると、XML-RPCは伝送可能なデータのタイプについては貧弱で、メッセージ・サイズについては肥満です。XML-RPCは、HTTPプロトコルを悪用して、存在理由が十分にあるファイアウォールを迂回します。その結果として、メッセージが伝送に状態を持つことができず、チャネルのボトルネックが引き起こされます。SOAPと比較すると、XML-RPCには重要なセキュリティー機構と堅固なオブジェクト・モデルの両方が欠けています。データ表現としてのXML-RPCは、ネイティブのプログラム言語の機構 (たとえば、Javaのserialize、Pythonのpickle、PerlのData::Dumper、または、Ruby、Lisp、PHP、および他の多くの言語における類似のモジュール) と比較すると、低速で、煩雑で、不完全です。

言い換えると、XML-RPCは、Richard Gabrielのソフトウェア設計哲学 (「悪いほうが良い」) を完ぺきに体現したものです (参考文献を参照)。前の段落の内容以上にXML-RPCについて強烈に記述することはできません。このプロトコルは多種多様な作業に最適だと思います。その理由を理解するため、Gabrielの「悪いほうが良い」哲学の教義を引用したいと思います。

  • 単純性: 設計は、インプリメンテーションとインターフェースの両方において、単純でなければなりません。さらに重要なのは、インプリメンテーションがインターフェースより単純であることです。単純性は、設計において最も重要な考慮事項です。
  • 正確さ: 設計は、観測可能なすべての局面において、正確でなければなりません。正確であるよりも単純であるほうが少しは良いでしょう。
  • 整合性: 設計は、極端に矛盾していてはなりません。ある場合には、単純性のために整合性を犠牲にできますが、インプリメンテーションを複雑にしたり矛盾させたりするよりも、それほど一般的でない状況を扱う該当の部分を除去するほうが良いでしょう。
  • 完全性: 設計は、実際的な数の重要な状況を扱うものでなければなりません。道理にかなった範囲で予期されるすべてのケースを扱う必要があります。完全性は、他の要素のために犠牲にすることができます。実際、インプリメンテーションの単純性が危険にさらされる場合には、完全性を犠牲にする必要があります。単純性が保存されるのであれば (特に、インターフェースの整合性が無価値であれば)、整合性を犠牲にすることによって完全性を実現することができます。

特定のテクノロジーが存在する何年も前に書いたにもかかわらず、GabrielはXML-RPCの長所を完ぺきに識別しました。

xml_pickleとXML-RPCのどちらがよいか

わたしは、Python用の比較的人気のあるモジュールであるxml_pickle を作成しました。このモジュール (このコラムで以前に説明しました。参考文献を参照) の目的は、標準のcPickle およびpickle モジュールのインターフェースとほとんど同じインターフェースを使用して、Pythonオブジェクトをシリアライズすることです。唯一の違いとして、わたしのモジュールではXMLで表現が行われます。わたしがxml_pickle を通じて意図していたことは、他のプログラム言語からも (また、異なるPythonバージョン間でも) 読み取れる非常に軽量な形式を作成することです。このモジュールにはXMLピックルを妥当性検査したいと思うユーザーのためのDTDが付属していますが、ユーザーからのフィードバックによると、形式的な妥当性検査が重要になることはめったにないようです。

xml_pickle のユーザーから繰り返し受ける質問の1つは、より広範囲の用途と多くのプログラム言語におけるインプリメンテーションの存在を考えたときに、XML-RPCのほうが優れているのかどうか、ということです。この限定的な質問に対する答えではおそらくxml_pickle に軍配が上がりますが、両者を比較することは有益であり、データ型の豊富さについていくつかの点が分かります。

最初のパスにおいて、XML-RPCはxml_pickle とは異なる処理を行うように見えます。XML-RPCはリモート・プロシージャーを呼び出し、結果を受け取ります。リスト1の典型的な使用例は、XML-RPC WebサイトとProgramming Web Services with XML-RPC に出てきます (参考文献を参照)。

リスト1. PythonシェルでのXML-RPCの使用例
>>>import xmlrpclib 
>>> betty = xmlrpclib.Server("http://betty.userland.com")
>>>print betty.examples.getStateName(41)
South Dakota

対照的に、xml_pickle は、ローカルのメモリー内オブジェクトのストリング表現を作成します。これらの処理は同じに見えないかもしれませんが、XML-RPCでは、リモート・プロシージャーを呼び出すのに、引数を適切なXML表現に変換する (別の言葉で言うと、パラメーターをピックル/シリアライズする) 必要があります。同様に、XML-RPC呼び出しからの戻り値には、ネストされたデータ構造が含まれることがあります。さらに、xmlrpclib.dumps() メソッドは、1つのxml_pickle モジュールと名前を共用しており (両方ともいくつかの標準モジュールから発案された)、同じ処理 (実際の呼び出しを実行しないでXMLシリアライゼーションを作成する) を行います。

一見すると、少なくともシリアライゼーションの局面にしか関心がない場合に、xml_picklexmlrpclib は機能的に交換可能に見えます。しかし、これから見るように、よく調べるといくらかの違いが明らかになります。

オブジェクトの表現

オブジェクトを作成し、2つの異なるアプローチを使用してシリアライズしてみましょう。いくつかの対比点が明らかになります。

リスト2. PythonシェルでのXML-RPCのシリアライズ例
>>>import xmlrpclib
>>>class C:pass
..
>>> c = C()
>>> c.bool, c.int, c.tup = (xmlrpclib.True,37, (11.2,'spam') )
>>>print xmlrpclib.dumps((c,),'PyObject')
<?xml version='1.0'?>
<methodCall>
<methodName>PyObject</methodName>
<params>
<param>
<value><struct>
<member>
<name>tup</name>
<value><array><data>
<value><double>11.2</double></value>
<value><string>spam</string></value>
</data></array></value>
</member>
<member>
<name>bool</name>
<value><boolean>1</boolean></value>
</member>
<member>
<name>int</name>
<value><int>37</int></value>
</member>
</struct></value>
</param>
</params>
</methodCall>

すでにいくつかのことに気が付いたはずです。まず、このXML文書全体には、現在の目的とは無関係なルート<methodCall> エレメントがあります。余分な数バイトを要することを除き、この付加的な囲みエレメントは重要ではありません。同様に、<methodName> は不要ですが、この例は、文書の役割を示す名前を提供しています。さらに、xmlrpclib.dumps() の呼び出しでは、オブジェクトのタプルが受け入れられますが、そのうちの1つを「ピックル」することだけが重要です (他のオブジェクトが存在する場合、そのオブジェクトは独自の<param> エレメントを持ちます)。ラッピングの他に、オブジェクトの属性は、<struct> エレメントの<member> エレメント内に適切に含められています。

それでは、xml_pickle が行う処理を見てみましょう (オブジェクトは上記のものと同じです)。

リスト3. Pythonシェルでのxml_pickleのシリアライゼーション例
>>>from xml_pickleimport XML_Pickler
>>>print XML_Pickler(c).dumps()
<?xml version="1.0"?>
<!DOCTYPE PyObject SYSTEM"PyObjects.dtd">
<PyObjectclass="C" id="1840428">
<attr name="bool" type="PyObject"class="Boolean" id="1320396">
  <attr name="value" type="numeric" value="1" />
</attr>
<attr name="int" type="numeric" value="37" />
<attr name="tup" type="tuple" id="1130924">
  <item type="numeric" value="11.199999999999999" />
  <item type="string" value="spam" />
</attr>
</PyObject>

xml_pickle バージョンでは、少なくなっている部分と多くなっている部分の両方があります (実際のサイズは同等です)。Pythonに組み込みブール型はありませんが、クラスを使用して新しい型を表すと、xml_pickle は即座に調整されます (ただし、冗長になります)。対照的に、XML-RPCは、8つのデータ型しかシリアライズできないように制限されています。もちろん、そのうちの2つの型 (<array><struct>) は、それ自体がコレクションであり、複合型にできます。さらに、xml_pickle は、複数のコレクション・メンバーを同じ基礎オブジェクトに結び付けることができます。設計上、XML-RPCにこれはありません (xml_pickle でも最近のバージョンで導入されました)。小さな点として、xml_pickle には単一のnumeric 型属性だけが含まれていますが、value 属性の実際のパターンは、整数、浮動小数点数、複素数などへのデコードを行えるようになっています。これらの戦略によって現実の一般性が失われたり得られたりするわけではありませんが、静的な型の言語を処理するプログラマーにはXML-RPCのスタイルが美的に訴えるでしょう。

XML-RPCの弱点

オブジェクト・シリアライゼーション形式としてのXML-RPCの問題点は、これが単なるプレーン形式であり、ほとんどの高水準プログラム言語のオブジェクトを処理するのに十分な型を備えていないことです。リスト4はこの欠点を示しています。

リスト4. PythonシェルでのXML-RPCの多重定義の例
>>> c = C()
>>> c.foo ='bar'
>>> d = {'foo':'bar'}
>>>print xmlrpclib.dumps((c,d),'PyObjects')
<?xml version='1.0'?>
<methodCall>
<methodName>PyObjects</methodName>
<params>
<param>
<value><struct>
<member>
<name>foo</name>
<value><string>bar</string></value>
</member>
</struct></value>
</param>
<param>
<value><struct>
<member>
<name>foo</name>
<value><string>bar</string></value>
</member>
</struct></value>
</param>
</params>
</methodCall>

リスト4では、2つのもの (オブジェクト・インスタンスと辞書) がシリアライズされています。Pythonオブジェクトは特に辞書に似ていると言えますが、辞書とオブジェクトをまったく 同じように表すと、多くの情報が失われます。さらに、XML-RPCの<struct> が持つ極端に汎用的な意味は、任意のOOP言語 (少なくとも、ネイティブのハッシュ/辞書構成を持つ任意の言語) にかなり影響します。これは、Pythonの特徴ではありません。一方、XML-RPCの<array> 型の内部でPythonのタプルやリストを区別できないことは、まったくPython固有の制限です。

xml_pickle は、すべてのPython型 (前述のように、ユーザー・クラスによって定義されたデータ型を含む) をより適切に処理します。実は、xml_pickle で辞書を直接ピックルすることはできません。これは基本的に、だれもこの機能を要求しなかったためです (追加することは容易です)。しかし、リスト5で示されているような、オブジェクト属性である辞書はピックルされます。

リスト5. Pythonシェルでのxml_pickle辞書の例
>>> c, c2 = C(), C()
>>> c2.foo ='bar'
>>> d = {'foo':'bar'}
>>> c.c, c.d = c2, d
>>>print XML_Pickler(c).dumps()
<?xml version="1.0"?>
<!DOCTYPE PyObject SYSTEM"PyObjects.dtd">
<PyObjectclass="C" id="1917836">
<attr name="c" type="PyObject"class="C" id="1981484">
  <attr name="foo" type="string" value="bar" />
</attr>
<attr name="d" type="dict" id="1917900">
  <entry>
    <key type="string" value="foo" />
    <val type="string" value="bar" />
  </entry>
</attr>
</PyObject>

この例で暗示されているxml_pickle アプローチのもう1つの長所は、辞書キーがストリングでなくても良いことです。XML-RPCの<struct> エレメントでは、<name> キーは常にストリングです。ただし、Perl、PHP、およびほとんどの言語は、この点でXML-RPCモデルに似ています。

xml_pickleの弱点

残念ながら、xml_pickle には、多くのプログラム言語に存在するいくつかの型が欠けています。目的がPython オブジェクトを単に保管して復元することではなく、異なる言語間でオブジェクトを交換することである場合、xml_pickle は現時点でまったく不適切です。原則として、浮動小数点数と整数の問題は重要ではありません。しかし、value 属性の形式が分析されるのを待たなくても、必要な型をXMLパーサーが判別できれば、(たとえばJava用の)「アンピックラー」を設計することは容易になるでしょう。

異種言語間でのピックルに関してさらに重大な問題は、XML-RPCに存在するもののPythonの組み込み型ではない、<boolean> および<dateTime.iso8601> タグです。先程、xml_pickle は、カスタム・データ型を定義するユーザー・クラスを容易かつ適切に処理すると述べましたが、異種言語間でのケースになると、これはあまり当てはまりません。たとえば、リスト6にあるxml_pickle 表現のフラグメントは、iso8601 Date/Timeを記述しています。

リスト6. xml_pickleバージョンのiso8601 Date/Time
<attr name="dte" type="PyObject" class="DateTime" id="1984076">
  <attr name="value" type="string" value="20011122T17:28:55" />
</attr>

2つの問題により、このデータをPerlやREBOLやPHPで使用することは難しくなっています。1つ目の問題は、復元されたクラスのネーム・スペースです。Pythonでは、復元されたxmlrpclib.DateTime のネーム・スペースは、デフォルトでxml_pickle.DateTime になります (ただし、このネーム・スペースは、アンピックルの前に手動で操作できます)。Pythonのインスタンス化とネーム・スペースが機能する仕方は、少なくともインスタンスのメソッドよりも属性が重要なのでない限り、この事実にはほとんど依存しません。しかし、さまざまな言語では、有効範囲指定の問題が非常に異なる仕方で処理されます。

2番目の、ずっと重要な問題は、このカスタム・クラスが、ネイティブ型であるはずの言語で容易にそのように認識できないという事実です。PerlとPHPにはネイティブのDateTime 型は存在しないので、これらの言語では、アンピックラーによってvalue インスタンス属性が復元される限り、何も失われません。対照的に、REBOLには、日付だけでなくEメール・アドレスやURLなどの風変わりな型も含む、より多くのネイティブ・データ型が存在します。これらは、xml_pickle プロセスで失われます。もちろん、XML-RPCでも、これらのデータ型は失われます。いずれにしても、わたしたちは、プレーンなストリング型によってより具体的なものを表す必要があります (または、XML-RPCで<base64> を使用します。xml_pickle はこれを、高位のビット値をエスケープ処理することによって処理します (たとえば、"\xff"))。

結論: これからどうするか

XML-RPCとxml_pickle のいずれも、一般的なプログラム言語のオブジェクト・インスタンスを表現する手段として満足のいくものではありません。しかし、これらの両方は非常に近づいてきています。ここでは、これらのプロトコルの間にある小さなみぞを埋めるいくつかのアプローチを提案し、一般的なオブジェクト・シリアライゼーション形式を提供したいと思います。

xml_pickle を「修正」することは、実は驚くほど単純で、より多くの型をこの形式に追加するだけです。たとえば、xml_pickle が最初に開発された後、UnicodeType がPythonに追加されました。この型の完全なサポートを追加するには、たった4行の新しいコードしか必要ではありませんでした (ただし、これが単純になったのは、XMLがもともとUnicodeであるという事実もいくらか関係していました)。また、ユーザーの要求に応え、numeric モジュールのArrayType がさらに少しの作業で追加されました。たとえある型がPythonに存在していなくても、xml_pickle 内でカスタム・クラスを定義することにより、その型の動作を追加できます。たとえば、REBOLの "e-mail address" 型は、次のようなフラグメントによってサポートできます。

<attr name="my_address" type="email" value="mertz@gnosis.cx" />

いったんアンピックルされれば、xml_pickle は "email" を "string" の同義語として扱えます。または、いくつかの役立つ動作とともにEmailAddress クラスをインプリメントすることもできます。後者の方法を使用する場合は、そのような動作の1つとして、上記のxml_pickle フラグメントへのピックルを含めることができます。

XML-RPCを「修正」することは、もっと難しい作業です。大量の新しいデータ型の追加を提案することは容易であり、純粋に技術的な視点からは、特に問題ありません。しかし、社会的な問題としては、XML-RPCの成功により、互換性のない変更を導入することは難しくなっています。仮説的な「データ拡張型」XML-RPCは、既存のインプリメンテーションやインストール・システムでうまく機能しないでしょう。実際、一部のインプリメント担当者は、Javaのnull、PythonのNone、Perlのundef、SQLのNONE などに対応する非標準 (よく言って半標準) の型として自分たちが追加した "nil" 型が欠けていることに、十分悩まされています。しかし、一部のプログラム言語だけで使用される型をより多く追加しても、うまくいかないでしょう。

オブジェクト・シリアライザーとしてXML-RPCを拡張する1つのアプローチは、2重の仕事を行うように<struct> エレメントを利用することです。標準のXML-RPCで不完全に型指定されるすべての型は、単一の<member> を持つ<struct>にラッピングできます。ここで、<name> はその特殊型を示します。既存のXML-RPCライブラリーではこれは行われませんが、XML-RPCのプロトコルとDTDは非常に単純なので、この動作の追加はまったく小さなことです (しかし、ほとんどの場合は、ただラッピングするだけでなく、ライブラリーを変更する必要があります)。

たとえば、XML-RPCでは、本来、Pythonのリストとタプルの間にある違いを記述できません。したがって、リスト7のフラグメントは、Pythonオブジェクトの記述としては不完全です。

リスト7. リストまたはタプルを表すXML-RPCフラグメント
<array>
  <data>
    <value>
      <double>11.2</double>
    </value>
    <value>
      <string>spam</string>
    </value>
  </data>
</array>

これは、次の表現に置き換えることができます。これは有効なXML-RPCであり、適切なインプリメンテーションによって特定のPythonオブジェクトに復元できます。

リスト8. タプルを表すXML-RPCフラグメント
<struct>
  <member>
    <name>NEWTYPE:tuple</name>
    <value>
      <array>
        <data>
          <value>
            <double>11.2</double>
          </value>
          <value>
            <string>spam</string>
          </value>
        </data>
      </array>
    </value>
  </member>
</struct>

真の<struct> は、2つ (またはそれ以上) の方法で表せます。まず、すべての<struct> を、別の<struct> にラッピングすることができます (おそらく、<name> OLDTYPE:struct などを使用します)。Pythonの場合は、辞書とオブジェクト・インスタンスが両方ともNEWTYPE なので、これがおそらく最善です。2番目の方法として、ネーム・スペースに似た接頭部NEWTYPE: を、この特殊な用途のために予約することができます (偶然の衝突が起きることは考えられません)。

設計上、xml_pickle はXML-RPCよりも、新しいデータ型を表すために拡張しやすくなっています。さらに、xml_pickle の拡張では、バージョン間での優れた逆方向互換性が維持されます。わたしは設計者として、xml_pickle のためにこの柔軟性を含めてよかったと思います。ただし、実際には、XML-RPCのほうがはるかに広く使用およびインプリメントされています。幸いなことに、XML-RPCも、薄い層を追加することにより、基礎DTDを壊さなくても、任意のデータ型を表すように調整できます。この機構はそれほど優雅ではありませんが、XML-RPCは、このような調整の後も既存のインプリメンテーションとの互換性が可能になるように、よく考えられています。


ダウンロード可能なリソース


関連トピック

  • UserlandのXML-RPCホーム・ページ (http://xmlrpc.com) は、もともと、XML-RPCについての調査を始めるための場所です。ここでは、多くの役立つ参考資料が見付かります。
  • XML-RPCホーム・ページでは、チュートリアルや記事のリンク (http://www.xmlrpc.com/directory/1568/tutorialspress)を調べることが特に役立ちます。
  • Kate Rhodesは、「XML-RPC vs. SOAP」という主題の優れた比較記事 (http://weblog.masukomi.org/writings/xml-rpc_vs_soap.htm)を書きました。この記事で、彼女はSOAPを「軽量」なプロトコルとして誤り伝えている記述に関するいくつかの詳細を指摘しています。
  • Richard P. Gabrielは、「Lisp: Good News, Bad News, How to Win Big」という主題のかなり有名な論文 (http://www.ai.mit.edu/docs/articles//good-news/good-news.htm)を書きました。だれもが読んで参照しているのは、「The Rise of 'Worse is Better'」というセクションです。
  • O'ReillyのProgramming Web Services with XML-RPC (http://www.oreilly.com/catalog/progxmlrpc/。Simon St. Laurent、Joe Johnston、Edd Dumbill共著) は、非常にすばらしい本です。この本の精神は、XML-RPC自体の精神と一致しています。
  • Secret Labのxmlrpc Pythonモジュールは、http://www.pythonware.com/products/xmlrpc/index.htmにあります。
  • IBMのWebSphere Application Server (WAS) がXML開発をサポートする方法については、WAS Advanced Edition 3.5オンライン・ヘルプにある、XMLに関する背景情報を参照してください。
  • WebSphere Studio Application Developer には、XMLおよびJava用の統合ビジュアル開発環境が含まれています。詳しくは、WebSphere Studioゾーンを参照してください。
  • XMLの論考の過去のコラムでは、次のようなさまざまなトピックを扱っています。
    • XMLの論考 第1回では、Pythonのxml_pickleオブジェクトを紹介しています。
    • XMLの論考 第2回では、Pythonのxml_objectifyの使い方について説明しています。
    • XMLの論考 第3回では、DocBookを紹介しています。
    • XMLの論考 第4回では、引き続き、DocBookでレガシー文書アーカイブを構築する方法について説明しています。
    • XMLの論考 第5回では、XSLTを介してXML文書をHTMLに変換する方法について説明しています。
    • XMLの論考 第6回では、いくつかのXMLエディターを比較し、特に、テキスト中心の文書に適しているものを取り上げています。
    • XMLの論考 第7回では、DTDとXML Schemaを比較検討し、開発者がどのような場合に、成熟しつつあるW3C XML Schemaに背を向けてDTDにこだわりたくなるのかを示しています。
    • XMLの論考 第8回では、コンピューター科学者たちによって概念化されたデータ・モデル という抽象的な理論が、特定の複数表現データ・フローを開発するために、どのように役立っているのかを説明しています。
    • XMLの論考 第9回では、RDBMSから独立したXML結果セットを生成できる、パブリック・ドメインのsql2dtdおよびsql2xmlユーティリティーについて説明しています。
    • XMLの論考 第10回では、Davidの「魅力的なPython」第15回のコラムで紹介された汎用全文検索システムを拡張して、XML特有の検索および索引付け機能を組み込みます。また、どのようにindexerがXMLの階層ノード構造を活用できるかを説明します。
    • XMLの論考 第11回では、このシリーズの第1回で紹介したモジュールに再び立ち戻ります。
    • XMLの論考 第12回では、SQLステートメントを生成するパブリック・ドメインのユーティリティーについて説明します。そのSQLステートメントを使えば、XML文書を開始するときに、一貫性のある、反転可能な方法でデータベースを作成して、それにデータを読み込むことができます。
    • XMLの論考 第13回では、XML文書のいくつかの圧縮方法を調べます。
    • XMLの論考 第14回は、XMLデータの処理にHaskell (およびライブラリーHaXml) を使用する利点について明らかにしています。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=240802
ArticleTitle=XMLの論考: オブジェクト・モデルとしてのXML-RPC
publish-date=12012001