ObectSquare

[技術講座]


UML による XML 設計ガイド (改訂版)

株式会社 オージス総研
オブジェクトテクノロジー・ソリューション部
伊藤 喜一
初版: 2001/05/24
第2版: 2002/01/24

0. はじめに

 ここ最近、XML がデータの交換形式やソフトウェアの設定ファイル等で使用されるケースが多くなってきました。XML はオブジェクト指向と親和性が高い技術だといわれます。そこで、UML を使って分析されたドメインのモデルから、XML のスキーマ設計を行うためのノウハウをまとめてみました。なお、UML と XML の基礎知識は既にあるものとして話を進めますので、不明な点がありましたら 参考文献 等をご参照ください。XML のスキーマ定義については、初版で使用した DTD に加え、RELAX と XML Schema での記述も追加しました。 XML Schema も W3C 勧告となり、徐々に使われ始めていますので、参考にしていただければと思います。

 念のためお断りしておきますが、目下各種団体により XML のスキーマ定義の標準化が進んでいます。独自に XML の設計をする前に、まずは使用できる標準規格がないか調べてみることをお勧めします。その上で、利用できそうな標準規格がない、あるいはローカルに使用するため、独自の XML ファイルの形式を作成する必要があるような場合に、このドキュメントを参考にして設計を行っていただければと思います。

目次

  1. 例題: 注文伝票
  2. クラス
  3. 属性
  4. 関連
  5. ロール名
  6. 多重度
  7. 汎化
  8. まとめ

1. 例題: 注文伝票

 注文管理システムの「注文伝票」を例題として、以後の説明を行いたいと思います。ドメインを分析した結果、以下のモデルが作成されました。注文伝票 (OrderSlip) には複数の注文 (Order) が含まれます。1つの注文で複数の商品 (Product) を注文することができ、各商品に対する注文明細 (OrderLineItem) が作られます。また、注文は顧客 (Customer) との間に関連があります。顧客は個人顧客 (PersonalCustomer) と法人顧客 (CorporateCustomer) に分類できます。

分析モデル

2. クラス

 まず最初はクラスの設計です。UML の各クラスに対して、基本的に XML の要素を1つ対応させます。例えば、顧客クラス Customer でしたら次のようになります。スキーマの定義は属性が入ったところで説明したいと思います。

<customer/>

 少し補足しておくと、XML の要素となるのはクラス自身ではなく、そのインスタンスの変数であると考えるのが最近の傾向のようです。したがって、タグ名の先頭は大文字ではなく小文字で始めることにします。また、複合語の場合の2番目以降の各単語の区切りは大文字とします。これらは Java でよく使われる命名規則にしたがったものです。


3. 属性

 続いて属性の設計です。大きく分けて XML の属性で表す方法と子要素で表す方法があります。顧客クラス Customer を例に見ていきます。

3.1. クラスに対応する要素の属性で表す方法

 一番簡単な方法です。XML の属性を用いると、ID 型や IDREF 型、NMTOKEN 型などを指定することができますが、3.2 の形式のように複雑な構造を持たせることはできません。また、SAX パーサを使用した場合、3.2 の形式よりアクセスが簡単にできます。一意キーや、バージョン情報などのメタ情報はこの形式を用いるとよいでしょう。

XML の記述例
<customer 
    id="C001"
    name="山田 太郎"
    address="東京都大田区XXXX-XX"
    phone="03-1234-XXXX"/>
DTD による定義
<!ELEMENT customer EMPTY>
<!ATTLIST customer id      ID    #REQUIRED
                   name    CDATA #REQUIRED
                   address CDATA #REQUIRED
                   phone   CDATA #REQUIRED>
RELAX による定義
<tag name="customer">
  <attribute name="id"      required="true" type="ID"/>
  <attribute name="name"    required="true" type="string"/>
  <attribute name="address" required="true" type="string"/>
  <attribute name="phone"   required="true" type="string"/>
</tag>
<elementRule role="customer" type="emptyString"/>
XML Schema による定義
<xsd:element name="customer" type="Customer"/>
<xsd:complexType name="Customer">
  <xsd:attribute name="id"      type="xsd:ID"     use="required"/>
  <xsd:attribute name="name"    type="xsd:string" use="required"/>
  <xsd:attribute name="address" type="xsd:string" use="required"/>
  <xsd:attribute name="phone"   type="xsd:string" use="required"/>
</xsd:complexType>

 RELAX は DTD の構造をほぼそのまま XML で置き換えた感じですが、XML Schema では、まず Customer という型を定義してから、それを要素 customer に割り当てるという方式をとっています。このあたりはオブジェクト指向的な考え方を取り入れているようです。

 なお、XML Schema においてプレフィクス xsd は名前空間 "https://www.w3.org/2001/XMLSchema" に割り当てられているとします (以下同じ)。

3.2. 子要素のコンテンツで表す方法

 属性をクラスに対応する要素の子要素とし、その値をコンテンツで表す方法です。例のように、属性自体にさらに複雑な構造を持たせることができます。DOM パーサや XSLT などからの使い勝手は、3.1 の形式でも 3.2 の形式でもそれほど変わりありません。一般の属性値は 3.1、3.2 どちらの方法で表すこともできますが、個人的には汎用性が高いこちらをお勧めします。

XML の記述例
<customer>
  <id>C001</id>
  <name>
    <familyName>山田</familyName>
    <givenName>太郎</givenName>
  </name>
  <address>東京都大田区XXXX-XX</address>
  <phone>03-1234-XXXX</phone>
</customer>
DTD による定義
<!ELEMENT customer   (id, name, address, phone)>
<!ELEMENT id         (#PCDATA)>
<!ELEMENT name       (familyName, givenName)>
<!ELEMENT familyName (#PCDATA)>
<!ELEMENT givenName  (#PCDATA)>
<!ELEMENT address    (#PCDATA)>
<!ELEMENT phone      (#PCDATA)>
RELAX による定義
<tag name="customer"/>
<elementRule role="customer">
  <sequence>
    <element name="id"      type="string"/>
    <ref label="name"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</elementRule>

<tag name="name"/>
<elementRule role="name">
  <sequence>
    <element name="familyName" type="string"/>
    <element name="givenName"  type="string"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:element name="customer" type="Customer"/>
<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="id"      type="xsd:string"/>
    <xsd:element name="name"    type="Name"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Name">
  <xsd:sequence>
    <xsd:element name="familyName" type="xsd:string"/>
    <xsd:element name="givenName"  type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

3.3. 子要素の属性で表す方法

 3.1 と 3.2 の中間的な方法です。後で関連の設計に似た形式を使用しています。

XML の記述例
<customer>
  <id      value="C001"/>
  <name    value="山田 太郎"/>
  <address value="東京都大田区XXXX-XX"/>
  <phone   value="03-1234-XXXX"/>
</customer>
DTD による定義
<!ELEMENT customer (id, name, address, phone)>
<!ELEMENT id       EMPTY>
<!ATTLIST id       value NMTOKEN #REQUIRED>
<!ELEMENT name     EMPTY>
<!ATTLIST name     value CDATA   #REQUIRED>
<!ELEMENT address  EMPTY>
<!ATTLIST address  value CDATA   #REQUIRED>
<!ELEMENT phone    EMPTY>
<!ATTLIST phone    value CDATA   #REQUIRED>
RELAX による定義
<tag name="customer"/>
<elementRule role="customer">
  <sequence>
    <ref label="id"/>
    <ref label="name"/>
    <ref label="address"/>
    <ref label="phone"/>
  </sequence>
</elementRule>

<tag name="id">
  <attribute name="value" required="true" type="NMTOKEN"/>
</tag>
<elementRule role="id" type="emptyString"/>

<tag name="name">
  <attribute name="value" required="true" type="string"/>
</tag>
<elementRule role="name" type="emptyString"/>

<tag name="address">
  <attribute name="value" required="true" type="string"/>
</tag>
<elementRule role="address" type="emptyString"/>

<tag name="phone">
  <attribute name="value" required="true" type="string"/>
</tag>
<elementRule role="phone" type="emptyString"/>
XML Schema による定義
<xsd:element name="customer" type="Customer"/>
<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="id"      type="CustomerID"/>
    <xsd:element name="name"    type="Name"/>
    <xsd:element name="address" type="Address"/>
    <xsd:element name="phone"   type="Phone"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="CustomerID">
  <xsd:attribute name="value" type="xsd:NMTOKEN" use="required"/>
</xsd:complexType>

<xsd:complexType name="Name">
  <xsd:attribute name="value" type="xsd:string" use="required"/>
</xsd:complexType>

<xsd:complexType name="Address">
  <xsd:attribute name="value" type="xsd:string" use="required"/>
</xsd:complexType>

<xsd:complexType name="Phone">
  <xsd:attribute name="value" type="xsd:string" use="required"/>
</xsd:complexType>

3.4. 属性のまとめ

 以上を整理すると、顧客クラス Customer のお勧めの設計は以下のようになります。id は属性にしましたが、ドメイン的な要素が強い場合は子要素にしてもよいでしょう。

XML の記述例
<customer id="C001">
  <name>山田 太郎</name>
  <address>東京都大田区XXXX-XX</address>
  <phone>03-1234-XXXX</phone>
</customer>
DTD による定義
<!ELEMENT customer (name, address, phone)>
<!ATTLIST customer id ID #REQUIRED>
<!ELEMENT name     (#PCDATA)>
<!ELEMENT address  (#PCDATA)>
<!ELEMENT phone    (#PCDATA)>
RELAX による定義
<tag name="customer">
  <attribute name="id" required="true" type="ID"/>
</tag>
<elementRule role="customer">
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:element name="customer" type="Customer"/>
<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
  <xsd:attribute name="id" type="xsd:ID" use="required"/>
</xsd:complexType>

4. 関連

 次は関連の設計です。ネットワーク構造の UML と、ツリー構造の XML の違いが最も顕著に表れる部分です。

4.1. XML の要素の階層構造で表す方法

 一方のクラスに対応する XML の要素が他方のクラスに対応する XML の要素を含む形 (階層構造) にします。含まれる側 (子要素) は値オブジェクトになるので、複数のオブジェクト間で共有することはできません。モデル上では次のように集約 (あるいはコンポジション) で表すとよいでしょう。

XML の記述例
<order>
  <product>ノート</product>
  <quantity>20</quantity>
  <customer>
    <name>山田 太郎</name>
    <address>東京都大田区XXXX-XX</address>
    <phone>03-1234-XXXX</phone>
  </customer>
</order>
DTD による定義
<!ELEMENT order    (product, quantity, customer)>
<!ELEMENT customer (name, address, phone)>
...
RELAX による定義
<tag name="order"/>
<elementRule role="order">
  <sequence>
    <element name="product"  type="string"/>
    <element name="quantity" type="decimal"/>
    <ref label="customer"/>
  </sequence>
</elementRule>

<tag name="customer"/>
<elementRule role="customer">
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:element name="order" type="Order"/>
<xsd:complexType name="Order">
  <xsd:sequence>
    <xsd:element name="product"  type="xsd:string"/>
    <xsd:element name="quantity" type="xsd:decimal"/>
    <xsd:element name="customer" type="Customer"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

4.2. ID と参照を用いる方法

 一方のクラスに ID 型の属性を設け、他方のクラスにそれへの参照を持たせます。どちらのオブジェクトも複数の他のオブジェクトから参照することができます。オブジェクトを一意に識別できれば、必ずしも ID 型を使わなくてもかまいませんが、XML の ID 型を使用することで、ファイル内での一意性が保証され、XPath による指定が簡単になります。

XML の記述例
<order>
  <customerRef ref="C001"/>
  <product>ノート</product>
  <quantity>20</quantity>
</order>

<customer id="C001">
  <name>山田 太郎</name>
  <address>東京都大田区XXXX-XX</address>
  <phone>03-1234-XXXX</phone>
</customer>
DTD による定義
<!ELEMENT order       (customerRef, product, quantity)>
<!ELEMENT customerRef EMPTY>
<!ATTLIST customerRef ref IDREF #REQUIRED>

<!ELEMENT customer    (name, address, phone)>
<!ATTLIST customer    id  ID    #REQUIRED>
...
RELAX による定義
<tag name="order"/>
<elementRule role="order">
  <sequence>
    <ref label="customerRef"/>
    <element name="product"  type="string"/>
    <element name="quantity" type="decimal"/>
  </sequence>
</elementRule>

<tag name="customerRef">
  <attribute name="ref" required="true" type="IDREF"/>
</tag>
<elementRule role="customerRef" type="emptyString"/>

<tag name="customer">
  <attribute name="id" required="true" type="ID"/>
</tag>
<elementRule role="customer">
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:element name="order" type="Order"/>
<xsd:complexType name="Order">
  <xsd:sequence>
    <xsd:element name="customerRef" type="CustomerRef"/>
    <xsd:element name="product"     type="xsd:string"/>
    <xsd:element name="quantity"    type="xsd:decimal"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="CustomerRef">
  <xsd:attribute name="ref" type="xsd:IDREF" use="required"/>
</xsd:complexType>

<xsd:element name="customer" type="Customer"/>
<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
  <xsd:attribute name="id" type="xsd:ID" use="required"/>
</xsd:complexType>

 DTD ではファイル内での一意性しか指定できませんが、XML Schema ではある範囲内での一意性や、参照の制約を記述することもできます。このあたりはデータベース技術からの影響を感じます。

<xsd:key name="customer.key">
  <xsd:selector xpath=".//customer"/>
  <xsd:field xpath="@id"/>
</xsd:key>

<xsd:keyref name="customer.ref" refer="customer.key">
  <xsd:selector xpath=".//customerRef"/>
  <xsd:field xpath="@ref"/>
</xsd:keyref>

 なお、order から customer への参照は、子要素の属性ではなく、直接 order の属性にしてしまう方法もあります。

XML の記述例
<order customer="C001">
  <product>ノート</product>
  <quantity>20</quantity>
</order>

5. ロール名

 関連のロール名の扱い方です。

5.1. ロール名のみ XML の要素とする方法

 ロール名のみを XML の要素とします。クラス名が必要ならば XML の属性として追加することもできます。1つのクラスを複数の役割で使用する場合、例のようにパラメータエンティティを用いると DTD の記述が簡単になります。

XML の記述例
<order>
  <product>ノート</product>
  <quantity>20</quantity>
  <customer type="Person">
    <name>山田 太郎</name>
    <address>東京都大田区XXXX-XX</address>
    <phone>03-1234-XXXX</phone>
  </customer>
</order>
DTD による定義
<!ENTITY % Person.conts "(name, address, phone)">
<!ENTITY % Person.attrs "type NMTOKEN #FIXED 'Person'">

<!ELEMENT order    (product, quantity, customer)>
<!ELEMENT customer %Person.conts;>
<!ATTLIST customer %Person.attrs;>
...

 RELAX でパラメータエンティティに相当するのは attPoolhedgeRule です。

RELAX による定義
<attPool role="Person.attrs">
  <attribute name="type" type="NMTOKEN">
    <enumeration value="Person"/>
  </attribute>
</attPool>
<hedgeRule label="Person.conts">
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</hedgeRule>

<tag name="order"/>
<elementRule role="order">
  <sequence>
    <element name="product"  type="string"/>
    <element name="quantity" type="decimal"/>
    <ref label="customer"/>
  </sequence>
</elementRule>

<tag name="customer">
  <ref role="Person.attrs"/>
</tag>
<elementRule role="customer"/>
  <hedgeRef label="Person.conts"/>
</elementRule>

 XML Schema では型定義と要素定義を分けて書くことができますので、この方法を自然な形で使うことができます。

XML Schema による定義
<xsd:complexType name="Person">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
  <xsd:attribute name="type" type="xsd:NMTOKEN" fixed="Person"/>
</xsd:complexType>

<xsd:element name="order" type="Order"/>
<xsd:complexType name="Order">
  <xsd:sequence>
    <xsd:element name="product"  type="xsd:string"/>
    <xsd:element name="quantity" type="xsd:decimal"/>
    <xsd:element name="customer" type="Person"/>
  </xsd:sequence>
</xsd:complexType>

5.2. クラス名のみ XML の要素とする方法

 クラス名のみを XML の要素とし、ロール名はその属性で表します。1つのクラスが複数の役割を持っており、その構造を再利用したい場合に便利な方法ですが、意味的に若干不適切な気がしますし、他の方法で代用可能なので、あまりお勧めしません。

XML の記述例
<order>
  <product>ノート</product>
  <quantity>20</quantity>
  <person role="customer">
    <name>山田 太郎</name>
    <address>東京都大田区XXXX-XX</address>
    <phone>03-1234-XXXX</phone>
  </person>
</order>
DTD による定義
<!ELEMENT order  (product, quantity, person)>
<!ELEMENT person (name, address, phone)>
<!ATTLIST person role NMTOKEN #IMPLIED>
...
RELAX による定義
<tag name="order"/>
<elementRule role="order">
  <sequence>
    <element name="product"  type="string"/>
    <element name="quantity" type="decimal"/>
    <ref label="person"/>
  </sequence>
</elementRule>

<tag name="customer">
  <attribute name="role" type="NMTOKEN"/>
</tag>
<elementRule role="person"/>
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:element name="order" type="Order"/>
<xsd:complexType name="Order">
  <xsd:sequence>
    <xsd:element name="product"  type="xsd:string"/>
    <xsd:element name="quantity" type="xsd:decimal"/>
    <xsd:element name="person"   type="Person"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Person">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
  <xsd:attribute name="role" type="xsd:NMTOKEN"/>
</xsd:complexType>

5.3. ロール名、クラス名とも XML の要素とする方法

 ロール名、クラス名とも XML の要素で表し、クラスに対応する要素をロール名に対応する要素の子要素とします。クラスに対応する要素の再利用が可能ですが、構造が若干複雑になります。

XML の記述例
<order>
  <product>ノート</product>
  <quantity>20</quantity>
  <customer>
    <person>
      <name>山田 太郎</name>
      <address>東京都大田区XXXX-XX</address>
      <phone>03-1234-XXXX</phone>
    </person>
  </customer>
</order>
DTD による定義
<!ELEMENT order    (product, quantity, customer)>
<!ELEMENT customer (person)>
<!ELEMENT person   (name, address, phone)>
...
RELAX による定義
<tag name="order"/>
<elementRule role="order">
  <sequence>
    <element name="product"  type="string"/>
    <element name="quantity" type="decimal"/>
    <ref label="customer"/>
  </sequence>
</elementRule>

<tag name="customer"/>
<elementRule role="customer">
  <ref label="person"/>
</elementRule>

<tag name="person"/>
<elementRule role="person"/>
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:element name="order" type="Order"/>
<xsd:complexType name="Order">
  <xsd:sequence>
    <xsd:element name="product"  type="xsd:string"/>
    <xsd:element name="quantity" type="xsd:decimal"/>
    <xsd:element name="customer">
      <xsd:complexType>
        <xsd:sequence>
          <xsd:element name="person" type="Person"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:element>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Person">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

6. 多重度

 関連の多重度が n の場合の表し方を考えます。

6.1. リスト自体を1つの要素とする方法

 リスト自体を1つの要素にします。まとまりとして意識することができ、他に属性や関連がある場合区別しやすいので、個人的にはこの方法をお勧めします。

XML の記述例
<order>
  <customer>山田 太郎</customer>
  <date>2001-05-01</date>
  <time>10:05:00</time>
  <orderLineItemList>
    <orderLineItem>
      <product>ノート</product>
      <quantity>20</quantity>
    </orderLineItem>
    <orderLineItem>
      <product>鉛筆</product>
      <quantity>10</quantity>
    </orderLineItem>
  </orderLineItemList>
</order>
DTD による定義
<!ELEMENT order (customer, date, time, orderLineItemList)>
<!ELEMENT orderLineItemList (orderLineItem)+>
<!ELEMENT orderLineItem     (product, quantity)>
...
RELAX による定義
<tag name="order"/>
<elementRule role="order">
  <sequence>
    <element name="customer" type="string"/>
    <element name="date"     type="date"/>
    <element name="time"     type="time"/>
    <ref label="orderLineItemList"/>
  </sequence>
</elementRule>

<tag name="orderLineItemList"/>
<elementRule role="orderLineItemList">
  <ref label="orderLineItem" occurs="+"/>
</elementRule>

<tag name="orderLineItem"/>
<elementRule role="orderLineItem"/>
  <sequence>
    <element name="product"  type="string"/>
    <element name="quantity" type="decimal"/>
  </sequence>
</elementRule>

 DTD で (orderLineItem)+ の + の部分は下限値が0の場合 * を指定します。RELAX も occurs="+" の + は下限値が0のときは * にします。

XML Schema による定義
<xsd:element name="order" type="Order"/>
<xsd:complexType name="Order">
  <xsd:sequence>
    <xsd:element name="customer" type="xsd:string"/>
    <xsd:element name="date"     type="xsd:date"/>
    <xsd:element name="time"     type="xsd:time"/>
    <xsd:element name="orderLineItemList">
      <xsd:complexType>
        <xsd:sequence>
          <xsd:element name="orderLineItem" type="OrderLineItem"
              minOccurs="1" maxOccurs="unbounded"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:element>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="OrderLineItem">
  <xsd:sequence>
    <xsd:element name="product"  type="xsd:string"/>
    <xsd:element name="quantity" type="xsd:decimal"/>
  </xsd:sequence>
</xsd:complexType>

6.2. 各要素を単純に並べる方法

 リスト自体は XML の要素にせず、各要素を単純に並べます。階層構造は単純になりますが、他に属性や関連がある場合区別しにくく、リスト全体をまとまりとして扱うことはできません。

XML の記述例
<order>
  <customer>山田 太郎</customer>
  <date>2001-05-01</date>
  <time>10:05:00</time>
  <orderLineItem>
    <product>ノート</product>
    <quantity>20</quantity>
  </orderLineItem>
  <orderLineItem>
    <product>鉛筆</product>
    <quantity>10</quantity>
  </orderLineItem>
</order>
DTD による定義
<!ELEMENT order (customer, date, time, orderLineItem+)>
<!ELEMENT orderLineItem (product, quantity)>
...
RELAX による定義
<tag name="order"/>
<elementRule role="order">
  <sequence>
    <element name="customer" type="string"/>
    <element name="date"     type="date"/>
    <element name="time"     type="time"/>
    <ref label="orderLineItem" occurs="+"/>
  </sequence>
</elementRule>

<tag name="orderLineItem"/>
<elementRule role="orderLineItem"/>
  <sequence>
    <element name="product"  type="string"/>
    <element name="quantity" type="decimal"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:element name="order" type="Order"/>
<xsd:complexType name="Order">
  <xsd:sequence>
    <xsd:element name="customer" type="xsd:string"/>
    <xsd:element name="date"     type="xsd:date"/>
    <xsd:element name="time"     type="xsd:time"/>
    <xsd:element name="orderLineItem" type="OrderLineItem"
        minOccurs="1" maxOccurs="unbounded"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="OrderLineItem">
  <xsd:sequence>
    <xsd:element name="product"  type="xsd:string"/>
    <xsd:element name="quantity" type="xsd:decimal"/>
  </xsd:sequence>
</xsd:complexType>

7. 汎化

 最後に汎化 (継承) の表し方です。

7.1. 汎化を集約 (コンポジション) に変更する方法

 スーパークラスに対応する要素をサブクラスに対応する要素が保有する形にします。スーパークラスの構造が再利用できますが、階層が深くなるとスーパークラスの属性へのアクセスが面倒になります。

XML の記述例
<personalCustomer>
  <customer id="C001">
    <name>山田 太郎</name>
    <address>東京都大田区XXXX-XX</address>
    <phone>03-1234-XXXX</phone>
  </customer>
  <birthday>1969-01-23</birthday>
</personalCustomer>

<corporateCustomer>
  <customer id="C002">
    <name>○×商事</name>
    <address>東京都港区XXXX-XX</address>
    <phone>03-5678-XXXX"/</phone>
  </customer>
  <charge>佐藤 花子</charge>
</corporateCustomer>
DTD による定義
<!ELEMENT customer (name, address, phone)>
<!ATTLIST customer id ID #REQUIRED>

<!ELEMENT personalCustomer  (customer, birthday)>
<!ELEMENT corporateCustomer (customer, charge)>
...
RELAX による定義
<tag name="customer">
  <attribute name="id" required="true" type="ID"
</tag>
<elementRule role="customer">
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</elementRule>

<tag name="personalCustomer"/>
<elementRule role="personalCustomer">
  <sequence>
    <ref label="customer"/>
    <element name="birthday" type="date"/>
  </sequence>
</elementRule>

<tag name="corporateCustomer"/>
<elementRule role="corporateCustomer">
  <sequence>
    <ref label="customer"/>
    <element name="charge" type="string"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
  <xsd:attribute name="id" type="xsd:ID" use="required"/>
</xsd:complexType>

<xsd:element name="personalCustomer" type="PersonalCustomer"/>
<xsd:complexType name="PersonalCustomer">
  <xsd:sequence>
    <xsd:element name="customer" type="Customer"/>
    <xsd:element name="birthday" type="xsd:date"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:element name="corporateCustomer" type="CorporateCustomer"/>
<xsd:complexType name="PersonalCustomer">
  <xsd:sequence>
    <xsd:element name="customer" type="Customer"/>
    <xsd:element name="charge"   type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

7.2. サブクラスのみ XML の要素として定義する方法

 スーパークラスの属性を各サブクラスに下げ、サブクラスのみ XML の要素として定義します。共通な属性が少なくサブクラスごとの違いが大きい場合や、サブクラス間の違いを意識して利用したい場合に、この形が便利です。スーパークラス部分の定義にパラメータエンティティを用いると、DTD の記述が簡単になります。

XML の記述例
<personalCustomer id="C001">
  <name>山田 太郎</name>
  <address>東京都大田区XXXX-XX</address>
  <phone>03-1234-XXXX</phone>
  <birthday>1969-01-23</birthday>
</personalCustomer>

<corporateCustomer id="C002">
  <name>○×商事</name>
  <address>東京都港区XXXX-XX</address>
  <phone>03-5678-XXXX</phone>
  <charge>佐藤 花子</charge>
</corporateCustomer>
DTD による定義
<!ENTITY % Customer.conts "(name, address, phone)">
<!ENTITY % Customer.attrs "id ID #REQUIRED">

<!ELEMENT personalCustomer  (%Customer.conts;, birthday)>
<!ATTLIST personalCustomer  %Customer.attrs;>
<!ELEMENT corporateCustomer (%Customer.conts;, charge)>
<!ATTLIST corporateCustomer %Customer.attrs;>
...
RELAX による定義
<attPool role="Customer.attrs">
  <attribute name="id" required="true" type="ID"
</attPool>
<hedgeRule label="Customer.conts">
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</hedgeRule>

<tag name="personalCustomer">
  <ref role="Customer.attrs"/>
</tag>
<elementRule role="personalCustomer">
  <sequence>
    <hedgeRef label="Customer.conts"/>
    <element name="birthday" type="date"/>
  </sequence>
</elementRule>

<tag name="corporateCustomer">
  <ref role="Customer.attrs"/>
</tag>
<elementRule role="corporateCustomer">
  <sequence>
    <hedgeRef label="Customer.conts"/>
    <element name="charge" type="string"/>
  </sequence>
</elementRule>

 XML Schema では拡張による派生を用いて継承関係を記述することができます。

XML Schema による定義
<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
  <xsd:attribute name="id" type="xsd:ID" use="required"/>
</xsd:complexType>

<xsd:element name="personalCustomer" type="PersonalCustomer"/>
<xsd:complexType name="PersonalCustomer">
  <xsd:complexContent>
    <xsd:extension base="Customer">
      <xsd:sequence>
        <xsd:element name="birthday" type="xsd:date"/>
      </xsd:sequence>
    </xsd:extension>
  </xsd:complexContent>
</xsd:complexType>

<xsd:element name="corporateCustomer" type="CorporateCustomer"/>
<xsd:complexType name="CorporateCustomer"
  <xsd:complexContent>
    <xsd:extension base="Customer">
      <xsd:sequence>
        <xsd:element name="charge" type="xsd:string"/>
      </xsd:sequence>
    </xsd:extension>
  </xsd:complexContent>
</xsd:complexType>

7.3. スーパークラスのみ XML の要素として定義する方法

 サブクラスの属性を全てスーパークラスに上げ、スーパークラスのみ XML の要素として定義します。7.2 とは反対に、共通な属性が多くサブクラスごとの違いが少ない場合や、サブクラス間の違いをあまり意識せず統一的に扱いたい場合、この形が便利です。サブクラスの区別が必要ならば属性で指定します。

XML の記述例
<customer id="C001" type="PersonalCustomer">
  <name>山田 太郎</name>
  <address>東京都大田区XXXX-XX</address>
  <phone>03-1234-XXXX</phone>
  <birthday>1969-01-23</birthday>
</customer>

<customer id="C002" type="CorporateCustomer">
  <name>○×商事</name>
  <address>東京都港区XXXX-XX</address>
  <phone>03-5678-XXXX</phone>
  <charge>佐藤 花子</charge>
</customer>
DTD による定義
<!ELEMENT customer (name, address, phone, birthday?, charge?)>
<!ATTLIST customer id   ID #REQUIRED
                   type (PersonalCustomer | CorporateCustomer) #REQUIRED>
...
RELAX による定義
<tag name="customer">
  <attribute name="id"   required="true" type="ID"/>
  <attribute name="type" required="true" type="NMTOKEN">
    <enumeration value="PersonalCustomer"/>
    <enumeration value="CorporateCustomer"/>
  </attribute>
</tag>
<elementRule role="customer">
  <sequence>
    <element name="name"     type="string"/>
    <element name="address"  type="string"/>
    <element name="phone"    type="string"/>
    <element name="birthday" type="date"   occurs="?"/>
    <element name="charge"   type="string" occurs="?"/>
  </sequence>
</elementRule>

 RELAX では属性の値によって、要素の内容を変化させることができます。以下の例では、属性 type の値が PersonalCustomer なら子要素 birthday を、属性 type の値が CorporateCustomer なら子要素 charge をもつと定義しています。

RELAX による定義 (その2)
<attPool role="Customer.attrs">
  <attribute name="id" required="true" type="ID"
</attPool>
<hedgeRule label="Customer.conts">
  <sequence>
    <element name="name"    type="string"/>
    <element name="address" type="string"/>
    <element name="phone"   type="string"/>
  </sequence>
</hedgeRule>

<tag name="customer" role="PersonalCustomer">
  <ref role="Customer.attrs"/>
  <attribute name="type" required="true" type="NMTOKEN">
    <enumeration value="PersonalCustomer"/>
  </attribute>
</tag>
<elementRule role="PersonalCustomer" label="customer">
  <sequence>
    <hedgeRef label="Customer.conts"/>
    <element name="birthday" type="date"/>
  </sequence>
</elementRule>

<tag name="customer" role="CorporateCustomer">
  <ref role="Customer.attrs"/>
  <attribute name="type" required="true" type="NMTOKEN">
    <enumeration value="CorporateCustomer"/>
  </attribute>
</tag>
<elementRule role="CorporateCustomer" label="customer">
  <sequence>
    <hedgeRef label="Customer.conts"/>
    <element name="charge" type="string"/>
  </sequence>
</elementRule>
XML Schema による定義
<xsd:element name="customer" type="Customer"/>
<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="name"     type="xsd:string"/>
    <xsd:element name="address"  type="xsd:string"/>
    <xsd:element name="phone"    type="xsd:string"/>
    <xsd:element name="birthday" type="xsd:date"   minOccurs="0"/>
    <xsd:element name="charge"   type="xsd:string" minOccurs="0"/>
  </xsd:sequence>
  <xsd:attribute name="id"   type="xsd:ID"       use="required"/>
  <xsd:attribute name="type" type="CustomerType" use="required"/>
</xsd:complexType>

<xsd:simpleType name="CustomerType">
  <xsd:restriction base="xsd:NMTOKEN">
    <xsd:enumeration value="PersonalCustomer"/>
    <xsd:enumeration value="CorporateCustomer"/>
  </xsd:restriction>
</xsd:simpleType>

 XML Schema ではスーパークラスで宣言された要素にサブクラスの内容を記述することができます。その場合、XML ファイルにおいて、属性 xsi:type で実際の型を指定する必要があります。なお、プレフィクス xsi は名前空間 "https://www.w3.org/2001/XMLSchema-instance" に割り当てられているとします。

XML Schema による定義 (その2)
<xsd:element name="customer" type="Customer">
<xsd:complexType name="Customer">
  <xsd:sequence>
    <xsd:element name="name"    type="xsd:string"/>
    <xsd:element name="address" type="xsd:string"/>
    <xsd:element name="phone"   type="xsd:string"/>
  </xsd:sequence>
  <xsd:attribute name="id" type="xsd:ID" use="required"/>
</xsd:complexType>

<xsd:complexType name="PersonalCustomer">
  <xsd:complexContent>
    <xsd:extension base="Customer">
      <xsd:sequence>
        <xsd:element name="birthday" type="xsd:date"/>
      </xsd:sequence>
    </xsd:extension>
  </xsd:complexContent>
</xsd:complexType>

<xsd:complexType name="PersonalCustomer">
  <xsd:complexContent>
    <xsd:extension base="Customer">
      <xsd:sequence>
        <xsd:element name="charge" type="xsd:string"/>
      </xsd:sequence>
    </xsd:extension>
  </xsd:complexContent>
</xsd:complexType>
XML の記述例 (その2)
<customer id="C001" xsi:type="PersonalCustomer">
  <name>山田 太郎</name>
  <address>東京都大田区XXXX-XX</address>
  <phone>03-1234-XXXX</phone>
  <birthday>1969-01-23</birthday>
</customer>

<customer id="C002" xsi:type="CorporateCustomer">
  <name>○×商事</name>
  <address>東京都港区XXXX-XX</address>
  <phone>03-5678-XXXX</phone>
  <charge>佐藤 花子</charge>
</customer>

8. まとめ

 ここまで紹介したガイドラインにしたがって、例題の「注文伝票」を XML で設計した結果が次のクラス図とファイルです。

設計モデル

 今回の設計のポイントは次の通りです。

 なお、今回の説明で XML Schema の定義は型ベースで行いましたが、DTD や RELAX に似た形式で書くこともできます。OrderSlip2.xsd はその形式で記述したものです。

 以上、XML 特有の構造による変更を加えることによって、UML の分析モデルから XML の設計モデルが作成可能なことがお分かりいただけたかと思います。このガイドラインが、みなさんの UML-XML を用いたソリューションのお役に立てば幸いです。Let's UML & XML !


《参考文献・URL》

UML 関係

XML 関係


© 2001-2002 OGIS-RI Co., Ltd.
HOME HOME TOP オブジェクトの広場 TOP
Valid XHTML 1.0!