目次


保守が容易で拡張性を持った XML フォーマットを作成する

新しい要件に対応できるよう XML フォーマットを十分アジャイルに設計し、変更を削減する

Comments

過去 10 年の間に、XML は組織内、あるいは組織間でのデータの保存や交換のための標準として、十分に受け入れられた一般的なものになりました。XML それ自体は抽象概念にすぎないため、フォーマットが成功するかどうかは、組織、あるいは組織グループが設計する XML フォーマットに完全に依存します。すべてのソフトウェア製品と同様に、これらの XML フォーマットはビジネスの要件が変化するにつれ、保守という課題に直面します。また、一般的な意味で変更の必要性が生じるだけではなく、競争上の理由やマーケット上の理由から、複数の組織のために XML フォーマットを同時に更新しなければならない場合が頻繁にあります

XML スキーマが 1 つだけであれば、保守は比較的簡単です。しかし数百もの組織に影響する変更を行うと、その影響は甚大です。XML スキーマの単純な変更に対応するだけでも多大な時間と費用を費やすことになりますが、事前に行った設計が適切であれば、変更情報を少し詳細に調べる以外にほとんど作業は必要ないはずなのです。この記事では 次の 2 点について説明します。

  • こうした影響に対応するための方法
  • こうした影響を最小限にとどめる方法

ここでは、車、タイヤ、フロントガラス、そしてそれらに関係する会社や再販業者に関する、非常に単純な例をいくつか使います。これらの例はあまり現実的な例ではありませんが、XML フォーマットの保守性を改善するための提言を伝えるには十分です。

単純ではあっても問題が多い

最初に、タイヤの情報を共有するために作成される XML ファイルの例として、Michelin 社のタイヤを付けた Volvo C30 の例を使いましょう。リスト 1 はこの例を示しています。

リスト 1. タイヤの情報を共有するための単純な XML ファイルの例
<car>
   <brand>Volvo</brand>
   <type>C30</type>
   <kind>Small family car</kind>
   <tires>
      <tire>
         <brand>Michelin</brand>
         <type>Winter</type>
         <count>4</count>
      </tire>
      <tire>
         <brand>Michelin</brand>
         <type>Spare</type>
         <count>1</count>
      </tire>
   </tires>
   <windscreen count="1">
      <brand>Car glass</brand>
   </windscreen>
</car>

この XML ファイルは非常に単純に見えるのではないでしょうか。一見、何も問題がなさそうですが、よく見てください。実は、問題は XML スキーマにあります。この XML スキーマは、非常に大きくてすべてが一体構造になっていることがわかります。そして、この XML フォーマットには、ほんの数個しか要素タイプがないことを考えてみてください。本当に現実的な例では、この XML スキーマがどれほどの大きさになるかを想像してみてください (リスト 2)。

リスト 2. 単純な XML フォーマットを記述する XML スキーマ
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="car">
    <xs:complexType>
      <xs:complexContent>
        <xs:extension base="brand">
          <xs:sequence>
            <xs:element ref="type"/>
            <xs:element ref="kind"/>
            <xs:element ref="tires"/>
            <xs:element ref="windscreen"/>
          </xs:sequence>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="kind" type="xs:string"/>
  <xs:element name="tires">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" ref="tire"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="tire">
    <xs:complexType>
      <xs:complexContent>
        <xs:extension base="brand">
          <xs:sequence>
            <xs:element ref="type"/>
            <xs:element ref="count"/>
          </xs:sequence>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="count" type="xs:integer"/>
  <xs:element name="windscreen">
    <xs:complexType>
      <xs:complexContent>
        <xs:extension base="brand">
          <xs:attribute name="count" use="required" type="xs:integer"/>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="brand">
    <xs:sequence>
      <xs:element ref="brand"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="brand" type="xs:string"/>
  <xs:element name="type" type="xs:NCName"/>
</xs:schema>

さて、「何が問題だと言うのか?誰も XSD ファイルなど見やしない」と言う前に、ビジネスを進める上では変更が必要になる可能性があることを考えてみてください。例えば次のように、タイヤのサイズを表示するようにタイヤの XML を変更する必要が出てきたらどうするのでしょう。

[...]
<tire>
   <brand>Michelin</brand>
   <type>Winter</type>
   <count>4</count>
   <size>20"</size>
</tire>
[...]

この簡単な変更からわかるように、フロントガラスの会社は、タイヤのフォーマットが少しでも変更されると新しい XSD を取得します。しかもフロントガラスの会社は、その XSD を理解するように自社のソフトウェアを変更する必要があります。これは適切ではありません。これではフロントガラスの会社に余分な作業と費用が発生してしまいます。またタイヤの会社も新しい XSD を受信し、(その会社でのソフトウェアの作成方法によっては) その XSD を処理するためにソフトウェアを更新する必要があるかもしれません。XSD を読む人は誰もいないかもしれませんが、実は XSD は多くの人にとって大きな頭痛の種となるのです。

簡単な対策: モジュール化

巨大な XSD ファイルを避けるための解決策として、タイヤとフロントガラスにそれぞれ独自の名前空間と別々の XSD ファイルを与えます。リスト 3 はその方法を示しています。

リスト 3. XML ファイルの例を変更し、名前空間を含める
[...]
   <tr:tires>
      <tr:tire count="4">
         <tr:brand>Michelin</tr:brand>
         <tr:type>Winter</tr:type>
      </tr:tire>
      <tr:tire count="1">
         <tr:brand>Michelin</tr:brand>
         <tr:type>Spare</tr:type>
      </tr:tire>
   </tr:tires>
   <wnd:windscreen count="1">
      <wnd:brand>Car glass</wnd:brand>
   </wnd:windscreen>
[...]

このように変更し、車の XML の例の他の部分をそのままにすれば、XSD ファイルはずっと小さくて処理が容易なものになります。関係する情報の大部分は、この XSD の先頭でインポートされる他の XSD に移動します。さて、この XSD ファイルを見てみると、モジュール構成になっています (リスト 4)。

リスト 4. 車の XML スキーマを変更し、タイヤとフロントガラス用に別々の XSD をインポートする
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      elementFormDefault="qualified" 
      targetNamespace="http://car.org/car" 			
      xmlns:tr="http://car.org/tire" 
      xmlns:wnd="http://car.org/windscreen" 
      xmlns:car="http://car.org/car">
 <xs:import namespace="http://car.org/tire" schemaLocation="tr.xsd"/>
 <xs:import namespace="http://car.org/windscreen" schemaLocation="wnd.xsd"/>
 <xs:element name="car">
   <xs:complexType>
         <xs:sequence> 
               <xs:element ref="car:brand"/>
               <xs:element ref="car:type"/>
               <xs:element ref="car:kind"/> 
               <xs:element ref="tr:tires"/>
               <xs:element ref="wnd:windscreen"/>
         </xs:sequence>
   </xs:complexType>
 </xs:element>
 <xs:element name="brand" type="xs:NCName"/>
 <xs:element name="type" type="xs:NCName"/>
 <xs:element name="kind" type="xs:string"/>
</xs:schema>

この XSD ファイルは XSD の 1 つのモジュールにすぎなくなります。この場合には XSD ファイルはそれほど大幅に縮小されていませんが、もっと重要な点は、これらの XSD が今やモジュール構成であり、ずっと扱いが容易になっていることです。その結果、タイヤのフォーマットの中で何かを変更することができ、またその変更をすべてのタイヤ会社に配布することができ、無関係な変更によってフロントガラスの会社に迷惑をかけることがありません

モジュール化がもたらす相乗効果

モジュール化は分散環境での保守管理の問題に役立つだけではありません。モジュール化によって要素の再利用を促進することができます。例えば、タイヤ会社が自動車のタイヤだけではなく自転車のタイヤも製造しているとします。このタイヤ会社は、タイヤ用の同じ XSD ファイルを使って自社の自転車タイヤを記述したいと思うかもしれません。しかしこの会社のタイヤを購入する自転車会社は、自動車を記述するための XML スキーマの大部分の要素は自転車会社に無関係なため、その XML スキーマを使うことを望みません。例えば普通の自転車にはフロントガラスはありません。自転車会社は、単にタイヤ用の XSD をインポートする、彼ら専用の自転車用 XSD を望みます。

こうしたシナリオでは、XML はリスト 5 のようになります。

リスト 5. タイヤの XML フォーマットを再利用して自転車を記述する XML ファイルの例
<bicycle>
[...]
   <tr:tire count="2">
      <tr:brand>Gazelle</tr:brand>
      <tr:type>Race</tr:type>
      <tr:size>25"</tr:size>
   </tr:tire>
[...]
</bicycle>

これはタイヤをインポートした自動車用の XSD と似ていますが、これは自転車専用です。では、次の例では自動車の例に戻ります。

実際のモジュールを扱う

車の XSD は最終的に、フロントガラスの XSD とタイヤの XSD をインポートするだけではなく、モーターの XSD、ハンドルの XSD、座席の XSD、塗料の XSD 等々もインポートします。そうすると管理が困難になります。この問題に対する解決方法は、車の全部品をインポートする、parts.xsd という別の XSD をインクルードすることです。こうすることによって、インポート・リストの中のどれが変更された場合にも、こうした依存関係の変更管理専用の parts.xsd ファイルのみを変更すればよくなります。リスト 6 はこの例の全体を示しています。

リスト 6. 車の XML スキーマで、インポートのリストを 1 つの include で置き換える
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      elementFormDefault="qualified" 
      targetNamespace="http://car.org/car" 
      xmlns:tr="http://car.org/tire" 
      xmlns:wnd="http://car.org/windscreen" 
      xmlns:car="http://car.org/car">
 <xs:include schemaLocation="parts.xsd"/>
 <xs:element name="car">
   <xs:complexType>
         <xs:sequence> 
               <xs:element ref="car:brand"/>
               <xs:element ref="car:type"/>
               <xs:element ref="car:kind"/> 
               <xs:element ref="tr:tires"/>
               <xs:element ref="wnd:windscreen"/>
         </xs:sequence>
   </xs:complexType>
 </xs:element>
 <xs:element name="brand" type="xs:NCName"/>
 <xs:element name="type" type="xs:NCName"/>
 <xs:element name="kind" type="xs:string"/>
</xs:schema>

ただし parts.xsd ファイルはリスト 7 のようなものです。

リスト 7. parts.xsd: インクルードする必要のあるインポートのリスト
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      elementFormDefault="qualified" 
      targetNamespace="http://car.org/car" 
      xmlns:tr="http://car.org/tire" 
      xmlns:wnd="http://car.org/windscreen" 
      xmlns:car="http://car.org/car">
  <xs:import namespace="http://car.org/tire" schemaLocation="tr.xsd"/>
  <xs:import namespace="http://car.org/windscreen" schemaLocation="wnd.xsd"/>
</xs:schema>

parts.xsd を使うことによる実際のメリットは、各部品の XSD をインポートするためのリストを、そうした部品を参照する必要があるすべての XSD ファイルに対してコピーする必要はないという点です。1 つの include で十分なのです。

現実の世界でのリスク

現実の世界の問題では、要素や部品の数が増えるにつれ、要素タイプの定義にも細心の注意を払う必要があります。例えば、修飾されていない属性 (つまり特にどの名前空間にも属さない属性) を XML フォーマットの中で使うことがよくありますが、これは危険です (リスト 8)。

リスト 8. 修飾されていない count 属性を持つ XML の例
<tr:tires>
   <tr:tire count="4">
      <tr:brand>Michelin</tr:brand>
      <tr:type>Winter</tr:type>
   </tr:tire>
   <tr:tire count="1">
      <tr:brand>Michelin</tr:brand>
      <tr:type>Spare</tr:type>
   </tr:tire>
</tr:tires>
<wnd:windscreen count="1">
   <wnd:brand>Car glass</wnd:brand>
</wnd:windscreen>

もし、修飾されていない属性に基づいて要素を選択すると、その結果を予測することは困難になります。例えば、値が 1 である count 属性を含むすべての要素を以下の XPath クエリーを使って選択すると、その結果がどの名前空間に属するかを予測することはできません。

//[@count = 1]

仮に予測できたとしても、実際に名前空間を予測できたのは、この例が非常に単純であるためです。重要な点は、こうしたちょっとした問題であっても、すぐに手が付けられないほどの問題になる可能性があるということです。そのための解決方法は、それぞれの属性を専用の名前空間で修飾することです。リスト 9 に示す XML の例が、先ほどの例とほんの少し異なっていることに注意してください。

リスト 9. 修飾された count 属性を持つ XML の例
[...]
   <tr:tires>
      <tr:tire tr:count="4">
            <tr:brand>Michelin</tr:brand>
            <tr:type>Winter</tr:type>
      </tr:tire>
      <tr:tire tr:count="1">
            <tr:brand>Michelin</tr:brand>
            <tr:type>Spare</tr:type>
      </tr:tire>
   </tr:tires>
   <wnd:windscreen wnd:count="1">
      <wnd:brand>Car glass</wnd:brand>
   </wnd:windscreen>
[...]

幸い、この XML がほんの少し異なっているように、XML スキーマの違いも非常にわずかです (リスト 10)。

リスト 10. 修飾された count 属性用にタイヤの XML スキーマを変更する
[...]
  <xs:element name="tire"> 
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="tr:brand"/>
        <xs:element ref="tr:type"/>
      </xs:sequence>
      <xs:attribute name="count" use="required" form="qualified" type="xs:integer"/>
    </xs:complexType>
  </xs:element>
[...]

この効果は劇的ではないかもしれませんが、十分に効果を認めることができます。できる限り、競合が起こる可能性をなくす習慣をつけることが大切です。

より一般的なフォーマットを設計する

ストレージのバックエンドとしてリレーショナル・データベースを使用する場合には、データベースのテーブルやフィールドを 1 対 1 で XML の構成体に変換してしまいがちです。そうすることでモデリングの自由度を制限することができますが、そのデータベースに接続される他のものにも (たとえそれらがこのデータベースを使わない場合でも)、そのデータベースの制約が悪影響を与えます。リスト 1 はリレーショナル・データベースから 1 対 1 で変換した場合の好例です。

しかし XML 文書は、リスト 1 とは異なる形でモデリングする必要があります。例えば、すべての部品を自動的に描画するために、車のフロントからリアまでの全体のレイアウトが必要かもしれません。そのためにはリスト 11 に示す XML の例のように記述します (これを SVG (Scalable Vector Graphics) への XSLT 変換の中で使うことができます)。これは一見すると非常に困難に思えるかもしれませんが、実際にはそれほど困難ではありません。この XML ファイルを作成するための方法は次のとおりです。

リスト 11. 別の構造を持つ XML の例
<car>
   <brand>Volvo</brand>
   <type>C30</type>
   <kind>Small family car</kind>
   <tr:tire tr:count="2">
      <tr:brand>Michelin</tr:brand>
      <tr:type>Winter</tr:type>
   </tr:tire>
   <wnd:windscreen wnd:count="1">
      <wnd:brand>Car glass</wnd:brand>
   </wnd:windscreen>
   <tr:tire tr:count="2">
      <tr:brand>Michelin</tr:brand>
      <tr:type>Winter</tr:type>
   </tr:tire>
   <tr:tire tr:count="1">
      <tr:brand>Michelin</tr:brand>
      <tr:type>Spare</tr:type>
   </tr:tire>
</car>

すると車の XSD はリスト 12 のようになります (car 要素の complexType に特に注目してください)。

リスト 12. 車の XML スキーマで別の構造を記述する
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      elementFormDefault="qualified" 
      targetNamespace="http://car.org/car" 
      xmlns:tr="http://car.org/tire" 
      xmlns:wnd="http://car.org/windscreen" 
      xmlns:car="http://car.org/car">
  <xs:import namespace="http://car.org/tire" schemaLocation="tr.xsd"/>
  <xs:import namespace="http://car.org/windscreen" schemaLocation="wnd.xsd"/>
  <xs:element name="car">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="car:brand"/>
        <xs:element ref="car:type"/>
        <xs:element ref="car:kind"/>
        <xs:choice maxOccurs="unbounded">
          <xs:element ref="tr:tire"/>
          <xs:element ref="wnd:windscreen"/>
        </xs:choice>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="brand" type="xs:NCName"/>
  <xs:element name="type" type="xs:NCName"/>
  <xs:element name="kind" type="xs:string"/>
</xs:schema>

このファイルをリレーショナル・データベースに保存し、後で同じ XML 文書を取得するための方法は、もっと複雑です。そうした場合には NXD が便利です。Exist DB は、シンプルなオープンソースの NXD の例です。IBM の DB2 Express-C は無料の選択肢として、リレーショナル・データベースと XML データベースの統合ソリューションを提供します。DB2 Express-C では、SQL を使って、あるいは XQuery のようにピュア XML 技術を使ってデータベースにアクセスすることができます。

XML スキーマのバージョンとドキュメントを扱う

多くの場合、何社かのグループが同じ XML スキーマの複数のバージョンを同時に使用することはまったく問題ありません。自分がどのバージョンを使用しているのか、またそのバージョンではどの要素が変更されているのかを理解している限り、問題はありません。望ましい習慣としては、XSD の annotation 要素を使ってバージョンと要素の情報を記述することです。annotation 要素には documentationappinfo という 2 つの要素を含むことができます。

documentation 要素は自明で、通常は文書の内容を記述します。この要素に関して迷うことはまずありません。そして documentation よりも興味深いのは、何でも好きなものを含むことができる appinfo 要素です。例えば、ある特定要素のバージョンを含むように version というカスタム要素を定義する場合、XSD はリスト 13 のようになります。

リスト 13. カスタマイズされた annotation を含む XML スキーマの例
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      elementFormDefault="qualified" 
      targetNamespace="http://car.org/tire" 
      xmlns:tr="http://car.org/tire" 
      xmlns:custom="http://car.org/custom">
 <xs:element name="tires">
   <xs:annotation>
         <xs:appinfo>
                <custom:version>0.91</custom:version>
             </xs:appinfo>
             <xs:documentation>
                Describes a set of tires.
         </xs:documentation>
   </xs:annotation>
   <xs:complexType>

XSD パーサーにはカスタム・バージョンの処理方法はわかりませんが、こうしたカスタム属性や類似のカスタム属性は、大規模な組織グループの中で XSD を扱う上で非常に役立ちます。結局のところ、XML ファイルと XML スキーマはコンピューターが読み取るだけではなく、人間が読むためのものでもあるため、それを考慮して XSD を扱うことが適切な習慣なのです。

extension 要素について

XML スキーマの機能に関する最後として、extension 要素に触れておく必要があります。この前のセクションで、XSD の appinfo 要素の拡張性について説明しました。デフォルトで、appinfo 要素は任意の内容に対して使うことができます。要素タイプを拡張するための方法としては、extension 要素を追加する方法の方が制限は厳しくなります。リスト 14 のXSD ファイルは size 要素を含むように basicTire タイプを拡張する方法を示しています。

リスト 14. タイヤの XML スキーマに size 要素の extension を含める
<xs:element name="tire" type="tr:sizedTire"/>
  <xs:complexType name="basicTire">
    <xs:sequence>
      <xs:element ref="tr:brand"/>
      <xs:element ref="tr:type"/>
    </xs:sequence>
    <xs:attribute name="count" use="required" form="qualified" type="xs:integer"/>
  </xs:complexType>
  <xs:complexType name="sizedTire">
    <xs:complexContent>
      <xs:extension base="tr:basicTire">
        <xs:sequence>
          <xs:element ref="tr:size"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

また XML スキーマによって、必要な場合には basicType の拡張を明確に禁止することもできます。XSD の仕様についてさらに詳しく調べたい場合には、「参考文献」を参照してください。この記事では説明しきれませんが、XML スキーマには興味深い機能がもっとたくさんあるのです。

まとめ

この記事で説明したように、大企業やそれらに関連する環境で XML フォーマットを保守しやすいものに保つためには、自動生成される専門ファイルとして XML スキーマを扱うべきではありません。XML スキーマの保守性を改善するための方法は、この記事で説明した以外にも豊富にあります。さらには、XML フォーマットを他の言語 (Schematron や RELAX NG など) で記述することさえできます。どのフォーマットを使うにせよ、適切な XML フォーマットをあらかじめ設計することによって、その XML フォーマットを使った通信に関わる全員の要求を満足させることができます。


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


関連トピック

  • XMLスキーマの使用法の基本: エレメントの定義」(Ashvin Radiya と Vibha Dixit の共著、 developerWorks、2000年8月) を読んで XML スキーマについて学んでください。
  • ヒント: XML Schema Standard Type Library を使って楽をする」(Nicholas Chase 著、developerWorks、2007年7月) は XML Schema Standard Type Library を使うためのヒント記事です。
  • developerWorks の Index of XML standards には最も重要な XML 標準がリストアップされています。
  • XML および関連技術において IBM 認定技術者になる方法については、IBM XML certification を参照してください。
  • オープンソースのデータベース管理システム、Exist DB をダウンロードし、試してみてください。Exist DB は完全に XML の上に構築されており、XML データ・モデルに従って XML データを保存することができ、そして効率的で索引ベースの XQuery 処理を特徴としています。

コメント

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Information Management
ArticleID=339727
ArticleTitle=保守が容易で拡張性を持った XML フォーマットを作成する
publish-date=08122008