XMLマスターポイントレッスン ~ プロフェッショナル(データベース)編 ~
最終回 XMLデータの構造変更
インフォテリア株式会社 野中康弘 NONAKA, Yasuhiro
本連載もいよいよ最終回となりました。今回は、システムの仕様変更などに伴うXMLデータの構造変更について、簡単な例と共にポイントなどを解説します。システムの仕様変更が頻繁に行なわれることはないと思いますが、仕様変更があった際にXMLデータの構造も変更できなければ優れた技術者とは言えません。今回解説する内容もXML-DBの運用/管理必須知識となりますので、この機会にしっかりと理解しておきましょう。
データ構造の変更
皆さんが携わっているさまざまなシステムで、仕様変更やシステム統合などの理由によりデータ構造の変更を余儀なくされた経験があるかと思います。RDBを利用したシステムでは、対象となるテーブルの構造を変更することは、そのテーブルが関係するGUIやアプリケーションなどにも影響が出てきます。特にテーブル構造を変更することは、状況にもよりますが容易に行なえる作業ではないと思います。
XMLDBを利用したシステムでもRDBを利用したシステムと同様に、対象となるXMLデータの構造を変更しますので、そのXMLデータが関係するGUIやアプリケーションにも影響が出てきます。既存データのことを考慮しなければならない部分はRDBと同じですが、XMLデータの構造変更はRDBに比べると容易に行なうことができます。
今回はXML Schemaで定義されたスキーマを使用して、既存データを変更せずにXML Schemaの変更だけで対応する「データ構造の拡張」と「データ構造の制限」の2種類のデータ構造の変更について、図1のような構造を持つXMLデータ(LIST1)を例に解説していきます。
LIST1:顧客情報XMLデータ(1)
<?xml version="1.0" encoding="Shift_JIS" ?>
<顧客情報>
<顧客 ID="C0000001" 氏名="山田太郎">
<住所>東京都品川区・・・</住所>
<電話番号>
<自宅>03-1111-2222</自宅>
<携帯>090-3333-4444</携帯>
</電話番号>
</顧客>
</顧客情報>
なお、LIST1のXMLデータをXML Schemaで定義したスキーマはLIST2のようになります。
LIST2:顧客情報XMLデータのスキーマ定義(dbmag200802_01.xsd)
<?xml version="1.0" encoding="Shift_JIS"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="顧客情報" type="customerInfoType" />
<xs:complexType name="customerInfoType" >
<xs:sequence>
<xs:element ref="顧客" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<xs:element name="顧客" type="customerType" />
<xs:complexType name="customerType">
<xs:sequence>
<xs:element ref="住所" />
<xs:element ref="電話番号" />
</xs:sequence>
<xs:attribute name="ID" type="xs:string" use="required" />
<xs:attribute name="氏名" type="xs:string" use="required" />
</xs:complexType>
<xs:element name="住所" type="xs:string" />
<xs:element name="電話番号" type="phoneNumberType" />
<xs:complexType name="phoneNumberType">
<xs:sequence>
<xs:element ref="自宅" minOccurs="0" />
<xs:element ref="携帯" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:element name="自宅" type="xs:string" />
<xs:element name="携帯" type="xs:string" />
</xs:schema>
データ構造の拡張
データ構造の拡張とは、既存のデータ構造に対して新たに要素や属性を追加することを指します。ここでは「顧客」要素に「趣味リスト」要素と「来店記録」要素を追加することにします。「趣味リスト」要素、「来店記録」要素の追加条件は次のとおりです。
- 「顧客」要素の子要素として「趣味リスト」要素、「来店記録」要素を追加
- 「趣味リスト」要素の出現回数は0回または1回
- 「趣味リスト」要素は、子要素として、「趣味」要素を持つ
- 「趣味」要素は文字型とし、出現回数は0回以上、上限なし
- 「来店記録」要素の出現回数は0回以上、上限なし
- 「来店記録」要素は、子要素として「来店日」要素と「備考」要素を持つ
- 「来店日」要素は日付型とし、出現回数は1回
- 「備考」要素は文字型とし、出現回数は0回または1回
LIST2:顧客情報XMLデータ(2)
<?xml version="1.0" encoding="Shift_JIS" ?>
<顧客情報>
<顧客 ID="C0000002" 氏名="高橋次郎">
<住所>東京都渋谷区・・・</住所>
<電話番号>
<自宅>03-5555-6666</自宅>
<携帯>090-7777-8888</携帯>
</電話番号>
<趣味リスト>
<趣味>ドライブ</趣味>
<趣味>料理</趣味>
<趣味>スノーボード</趣味>
</趣味リスト>
<来店記録>
<来店日>2007-11-14</来店日>
<備考>初回来店</備考>
</来店記録>
<来店記録>
<来店日>2007-12-24</来店日>
</来店記録>
</顧客>
</顧客情報>
ここで注意しなければならないのは追加される要素の出現回数です。本来、来店記録は各顧客に対して1回以上存在する項目ですが、既存のデータには存在していませんので、「来店記録」要素の出現回数を1回以上にできません。「趣味リスト」要素についても同様です。ただし、データ構造の拡張を行なう前に追加される要素を既存のデータに追加するような機能などを提供するのであれば、「趣味リスト」要素の出現回数を0回または1回ではなく1回に、「来店記録」要素の出現回数を0回以上ではなく1回以上にすることができます。それではXML Schemaにはどのような変更を加えれば良いか見ていきましょう。
extension要素による拡張
XML Schemaの複合型の拡張ではどのようなXML Schemaの要素を使うか覚えていますか。データ型を拡張するには complexType要素、complexContent要素、extension要素を使用します。まず、complexType要素で新しい型の名前を定義し、complexContent要素で新しい型が複合型であることを定義します。そして、extension要素のbase属性に指定された型を元にextension要素の子要素に追加する要素を記述します(図3)。
さらにデータ構造を拡張した型を利用するには、9行目にある「顧客」要素のtype属性を新しい型の名前に変更する必要があります。
要素の再定義
前述の説明では既存のスキーマ定義ファイルを変更しましたが、このファイルを変更せずにデータ型の拡張部分のスキーマ定義を別ファイルに記述する場合について考えてみましょう。XML Schemaには複数のスキーマ定義ファイルを組み合わせて1つのスキーマ定義ファイルにするための要素が提供されています。それはinclude要素とimport要素です。include要素の基本的な構文を図4に、import要素の基本的な構文を図5に示します。
include要素、import要素ともにschema要素の子要素として記述します。include要素を使って既存のスキーマ定義を取り込み、データ拡張部分のスキーマ定義ファイルを作成して、complexType要素のname属性に「customerType」を指定します(LIST5)。
この状態で妥当性検証を行なうと、スキーマ定義ファイル側で「1つのスキーマ定義に同じ名前を持つグローバル宣言されたコンポーネントを持つことはできない」「循環定義の階層構造に対するエラー」が出力されます(LIST6)。
LIST6:include要素を使ったスキーマ定義の検証結果(1)
[error] file:///D:/dbmag200802_01.xsd:10行39桁sch-props-correct.2:
A schema cannot contain two global components with the same name; ⇒
this schema contains two occurrences of ',customerType'.
[error] file:///D:/dbmag200802_02-inc.xsd:6行41桁ct-props-correct.3:
Circular definitions detected for complex type ':customerType'. ⇒
This means that ':customerType' is contained in its own type hierarchy,
which is an error.
( 掲載の都合上、⇒ で折り返しています。 )
ここでcomplexType要素のname属性をほかの名前に変更しても「顧客」要素のデータ型には反映されませんので、今度はXMLデータ側でのエラーが出力されます(LIST7)。LIST7:include要素を使ったスキーマ定義の検証結果(2)
[error] file:///D:/dbmag200802_02.xml:9行12桁cvc-complex-type.2.4.d: ⇒
Invalid content was found starting with element
'趣味リスト'. No child element is expected at this point.
( 掲載の都合上、⇒ で折り返しています。 )
include要素をimport要素に変更し妥当性検証を行なっても、この例では名前空間に対応していないので、LIST8のようなエラーが出力されます。LIST8:import要素を使ったスキーマ定義の検証結果
[error] file:///D:/dbmag200802_02-imp.xsd:3行53桁src-import.1.2:
If the namespace attribute is not present on an element information item ⇒
then the enclosing schema must have a targetNamespace.
[error] file:///D:/dbmag200802_02-imp.xsd:6行41桁ct-props-correct.3:
Circular definitions detected for complex type ':customerType'. This means that ⇒
':customerType' is contained in its own type hierarchy,
which is an error.
[error] file:///D:/dbmag200802_02.xml:2行7桁cvc-elt.1: Cannot find the declaration ⇒
of element '顧客情報'.
( 掲載の都合上、⇒ で折り返しています。 )
このような場合、include要素、import要素は使用できず、代わりにredefine要素を使用します。redefine要素の基本的な構文は図6のようになります。redefine要素は、schemaLocation属性に指定された元となるスキーマファイルで定義されている内容を部分的に再定義できます。redefine要素を使って既存のスキーマ定義を参照し、データ拡張部分のスキーマ定義ファイル(LIST9)を作成して妥当性検証を行なうと、redefine要素を使用したスキーマ定義ファイルに対してLIST1、LIST3のXMLデータは妥当となり、問題なくXML-DBに格納できます。
このように、XMLデータの構造を拡張するには、既存のスキーマ定義ファイルに拡張する部分の記述を追加する方法と、拡張する部分を別のスキーマ定義ファイルとし、redefine要素を使って元となるスキーマ定義ファイルを参照し再定義する方法の2種類があります。
データ構造の制限
データ構造の制限とは、要素の出現回数を変更したり、要素や属性の値の範囲を狭めたり、属性の出現制約(入力できる文字列のパターンなど)を変更したりする場合のことを指します。通常、運用中のシステムのデータに対して構造の制限をかけることは考えにくいですが、開発中のシステムであれば、データ構造の制限をかけることはありますので、その方法について解説していきます。ここでは、LIST3の顧客情報XMLデータについて、「顧客」要素の「ID」属性と先ほど拡張した「趣味」要素に対して制限をかけます。
「ID」属性と「趣味」要素にかける制限は次のとおりです。
- 「ID」属性に入力できる文字数を8文字とする
- 「ID」属性に入力できる文字列のパターンは"C"+数字7桁とする
- 「趣味」要素の出現回数を最大10回とする
データ構造の制限後はLIST1、LIST3のXMLデータが格納できることはもちろん、LIST10のようなデータも格納できます。
LIST10:顧客情報XMLデータ(3)
<?xml version="1.0" encoding="Shift_JIS" ?>
<顧客情報>
<顧客 ID="C0000003" 氏名="鈴木花子">
<住所>東京都新宿区・・・</住所>
<電話番号>
<自宅>03-9999-0000</自宅>
<携帯>090-1234-5678</携帯>
</電話番号>
<趣味リスト>
<趣味>音楽鑑賞</趣味>
<趣味>料理</趣味>
<趣味>生け花</趣味>
<趣味>海外旅行</趣味>
<趣味>映画鑑賞</趣味>
<趣味>絵画</趣味>
<趣味>陶芸</趣味>
<趣味>トレッキング</趣味>
<趣味>スキューバダイビング</趣味>
<趣味>ヨガ</趣味>
</趣味リスト>
<来店記録>
<来店日>2007-11-15</来店日>
<備考>初回来店</備考>
</来店記録>
<来店記録>
<来店日>2007-11-30</来店日>
</来店記録>
</顧客>
</顧客情報>
その代わりLIST11のようなデータはエラーとなり、格納できなくなります。ここで注意しなければならないのは制限による既存データへの影響です。もともとLIST11のようなデータが格納されていたのに、上記の制限をかけることで無効なデータとなってしまいます。すでにデータが存在している場合には既存データを精査し、既存データに影響を与えないように制限をかけなければなりません。
それでは、どのようにXML Schemaを変更すれば良いか考えてみましょう。
XML Schemaの制限
XML Schemaの複合型の制限は、complexType要素、complexContent要素、restriction要素を使用します。まず、complexType要素で新しい型の名前を定義し、complexContent要素で新しい型が複合型であることを定義します。そして、restriction要素のbase属性に指定された型を元にrestriction要素の子要素に使用するすべての要素、属性を記述します(図8)。
さらに「趣味」要素の最大出現回数を10回に制限します(図10)。
制限を付加した後のXML SchemaのリストをLIST12にまとめますので参照してください(LIST4の続き)。
データ構造に制限をかけた型を利用するには、LIST4の9行目にある「顧客」要素のtype属性を「newCustomerType」から「NewCustomerType」に、38行目にある「趣味リスト」要素のtype属性を「hobbyListType」から「newHobbyListType」に変更する必要があります。このようにXML Schemaファイルを変更することで、「ID」属性に入力できる文字数を8文字、入力できる文字列のパターンを"C"+数字7桁とし、「趣味」要素の出現回数を最大10回にできます。
データ型の制限においても、前述の「要素の再定義」で解説したredefine要素を使って制限部分を別のスキーマファイルとして定義することもできます(LIST13)。
ここで作成するスキーマファイルのredefine要素のschemaLocation属性には、LIST9のdbmag200802_02-red.xsdを指定します。そして「customerType」型と「hobbyListType」型はスキーマファイルdbmag200802_02-red.xsdで定義されていますので、この2つの型に制限をかけた記述をredefine要素の子要素として記述します。最後に「ID」属性への制約を付加します。これで既存のスキーマファイルを変更した場合と同じ結果を得ることができます。
XML Schemaで定義されたスキーマを使用したデータ構造の拡張にはextension要素を、データ構造の制限にはrestriction要素を使ってスキーマ定義を行ないます。さらにそれらを反映するために既存のスキーマ定義ファイルを修正するか、もしくは新しくスキーマ定義ファイルを作成し、その中でredefine要素を使ってデータ構造を再定義することもできます。構築中のシステムであれば、既存のスキーマ定義ファイルを修正しても良いと思いますが、運用中のシステムであれば新たにスキーマ定義ファイルを作成し、その中でredefine要素を使ってデータ構造を再定義したほうがデータ構造の変更による影響を最小限に抑えることができると思います。
今月の確認問題
ここまで、XMLデータの構造変更を解説してきました。今回解説した内容について、しっかりと理解できているかどうかを確認問題でチェックしてみましょう。
問題
次の[ユーザー情報XML文書]に対して、Login要素の子要素として日付型の「LastDate」要素を追加します。
[ユーザー情報XML Schema文書]をどのように変更すれば妥当となるか、正しいものを選択してください。
[ ユーザー情報XML文書 ]
<User>
<Login>
<Id>yamada</Id>
<Pw>taro1234</Pw>
</Login>
</User>
[ ユーザー情報XML Schema文書 ]
<xs:schema xmlns:xs= "http://www.w3.org/2001/XMLSchema">
<xs:element name="User" type="userType"/>
<xs:complexType name="userType">
<xs:sequence>
<xs:element ref="Login"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Login" type="loginType"/>
<xs:complexType name="loginType">
<xs:sequence>
<xs:element name="Id" type="xs:string"/>
<xs:element name="Pw" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
- 既存のXML SchemaファイルのLogin要素のtype属性を変更し、新しい型を、extension要素を使って定義する
- 既存のXML SchemaファイルのLogin要素のtype属性を変更し、新しい型を、restriction要素を使って定義する
- 新しく別のXML Schemaファイルを作成し、redefine要素、extension要素を使って再定義する
- 新しく別のXML Schemaファイルを作成し、redefine要素、restriction要素を使って再定義する
<xs:schema xmlns:xs= "http://www.w3.org/2001/XMLSchema">
<xs:element name="User" type="userType"/>
<xs:complexType name="userType">
<xs:sequence>
<xs:element ref="Login"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Login" type="newLoginType"/>
<xs:complexType name="loginType">
<xs:sequence>
<xs:element name="Id" type="xs:string"/>
<xs:element name="Pw" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="newLoginType">
<xs:complexContent>
<xs:extension base="loginType">
<xs:sequence>
<xs:element name="LastDate" type="xs:date"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
<xs:schema xmlns:xs= "http://www.w3.org/2001/XMLSchema">
<xs:element name="User" type="userType"/>
<xs:complexType name="userType">
<xs:sequence>
<xs:element ref="Login"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Login" type="newLoginType"/>
<xs:complexType name="loginType">
<xs:sequence>
<xs:element name="Id" type="xs:string"/>
<xs:element name="Pw" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="newLoginType">
<xs:complexContent>
<xs:restriction base="loginType">
<xs:sequence>
<xs:element name="Id" type="xs:string"/>
<xs:element name="Pw" type="xs:string"/>
<xs:element name="LastDate" type="xs:date"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:schema>
<xs:schema xmlns:xs= "http://www.w3.org/2001/XMLSchema">
<xs:redefine schemaLocation="ex01.xsd">
<xs:complexType name="loginType">
<xs:complexContent>
<xs:extension base="loginType">
<xs:sequence>
<xs:element name="LastDate" type="xs:date"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:redefine>
</xs:schema>
<xs:schema xmlns:xs= ⇒ "http://www.w3.org/2001/XMLSchema">
<xs:redefine schemaLocation="ex01.xsd">
<xs:complexType name="loginType">
<xs:complexContent>
<xs:restriction base="loginType">
<xs:sequence>
<xs:element name="LastDate" type="xs:date"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:redefine>
</xs:schema>
解説
ここでは「LastDate」要素を追加しますので、複合型を拡張するための要素であるextensionを使用します。また、XML Schema文書への反映は既存のXML Schemaファイルを変更する方法と新しく別のXML Schemaファイルを作成し、redefine要素を使用して既存のXML Schema文書で定義されているデータ型を再定義する方法の2種類があります。
Aは既存のXML Schemaファイルを変更し、extension要素を使ってデータ型を拡張しています。Bはrestriction要素を使っているので誤りです。restriction要素はデータ型に制限をかける場合に使用します。Cは新しく別のXML Schemaファイルを作成し、redefine要素、extension要素を使ってlogintype型を再定義しています。DもBと同様にrestriction要素を使用しているので誤りです。よって、正解はA、Cです。
* * *
今回はXMLデータの構造変更について解説しました。XML Schemaを使用したデータ構造の変更にはextension要素、restriction要素を使うこと、既存のXML Schema文書を変更せずにデータ型を再定義するにはredefine要素を使うこと、XML Schema文書に別のXML Schema文書を取り込むための要素、include要素、import要素についてもしっかりと理解しておきましょう。また誌面の都合上、解説できなかったXML Schemaを使ったXMLデータの一意性制約を定義するための要素、unique要素、key要素とkeyref要素についても理解を深めておきましょう。
本連載も今回で最終回となりました。技術解説を中心にベーシックV2試験対応編を全15回、本連載を全10回にわたって解説しましたが、XMLやXML-DBに興味を持ち、技術を習得したいと思っていただけたでしょうか。本連載をお読みいただいた方はXMLやXML-DBのプロとしての第一歩を踏み出したわけですから、しっかりと知識/技術を磨いていただければと思います。今までありがとうございました。
野中康弘(のなかやすひろ)
2004年よりインフォテリア株式会社 教育部に勤務。主にインフォテリア認定トレーニングセンターで開催されているXML教育のテキスト開発/改訂に従事。今年も慌しい1年だったように思います。掃除があまり好きではない私にとって、年末の大掃除が悩みの種になっています。
<掲載> P.250-257 DB Magazine 2008 February