XMLでは属性にすべきか要素にすべきか

いまさらXMLの解説ではないが、オブジェクトをXMLで永続化しようとした場合、オブジェクトの属性を要素(element)で表現するか、属性(attribute)で表現するかが悩むところだ。elementで表現する場合は、さらにそのelementのテキスト(text)で表現するか、elementのattributeで表現するかも問題となる。昨日、ある方から質問をいただいたので久々に考えてみた。XMLは以前、eXcelon(現Cyber Luxeon)とYggdrasill(現EsTerra)を使用した経験があり、その時分に考えていたことを思い出してみた。

  <user id="1234" name="tacohachi" birthday="1970-10-13"/>
  <!-- または -->
  <user>
     <id>1234</id>
     <name>tacohachi</name>
     <birthday>1970-10-13</birthday>
  </user>

人の名前や誕生日を表現すると、上のようなバリエーションが思い浮かぶ。例えば、誕生日の表現はこれでいいのか。まず、以下のようにすべきだろう。

     <birth>
       <date>1970-10-13</date>
     </birth>

こうしておけば、以下のような拡張に対応しやすいことが分かる。

     <birth>
       <date format="yyyy-Mm-Dd">1970-10-13</date>
       <place>広島県</place>
     </birth>

この例は、基本的な考え方を示している。以下、質問された項目ごとにまとめてみる。

(1) 属性にすべきか要素にすべきか

「基本的には属性であるが、複数・順序・内部構造・拡張性を考慮して必要な場合のみ要素にする」の考え方で正しいのか。
正しい考え方だ。要素(element)にする場合、さらにそのelementのテキスト(text)にするか、属性(attribute)にするかという観点もある。elementのtextの観点では、いわゆる"テキスト"かどうかとい判断基準があり、attributeからの観点では、将来細分化する可能性があるかどうか(可変性)が判断基準になる。これらは後述する。
tacohachiのようにtextで表現するのか、のようなattributeで表現するのがいいのか。この判断基準は何か。
いくつか観点があるが、まずelementのtextには、原則、いわゆる"テキスト"しか用いない。"テキスト"とは文章やラベルなどユーザーがテキストとして読んで意味のわかるものだ。そのまま画面やドキュメントに差し込んでもユーザーが理解できるものだ。日本語のテキストは原則、elementのtextとしたほうがいい。また改行や&,",<,>のなどの参照表現(&amp;など)を必要とするような文字列もattributeでなく、elementのtextを使う方がいい。
attributeからの観点では、これ以上、将来も細分化する必要がなさそうなものをattributeとすること。プリミティブなものといってもいい。名前(name)という概念は細分される可能性があるし、数値といっても単位など添えられる場合もあるので注意が必要だ。
上記のnameはIDなのか、単にテキストとなる字面(名前)なのかにも拠る。名前であるなら、attributeで表現することは避けた方がいい。名前を考える際には、英語/日本語の区別がない場合、逆に日本語名/英語名/略称/ふりがななどの区別が必要な場合、ユニークなIDとは別に存在するがペアにしてはおきたい、などの状況が想定される。ユニーク性を示すIDなどは、そのコード体系が確定していればattributeとすることを推奨するが、テキスト表現したい名前(name)などとペアで扱いたいなら、ユニークなIDであってもelementのtextで表現してもよい。以下のようなバリエーションがある。

  (a)
      <user id="1234">
        <name>tacohachi</name>
      </user>
  (b)
      <user>
        <id>1234</id>
        <name>tacohachi</name>
      </user>
  (c)
      <user>
        <id>1234</id>
        <name_ja>たこはち</name_ja>
        <name_en>tacohachi</name_en>
      </user>
  (d)
      <user id="1234" name="tacohachi"/>

言語化を別なところで実現するなら(c)は不要だ。(a)か(b)が妥当だろう。(d)も条件付きではOKだ。nameに&などの文字が入る可能性がないこと、日本語名/英語名/略称などの拡張が今後もなく、アルファベットで固定されるのならいいだろう。

(2) 属性はいくつまで並べていいか

属性(attribute)として沢山並べると、どこかで要素(element)にするといった判断が必要だが、判断基準はあるのか。
素数、属性数による基準や制約はない。原則いくつでもよいが、attributeについては確かにあまり多くを並べない方がいい。HTMLでもそうだがテキストエディタなどでattributeを複数行にきれいに並べるのは面倒だからだ。複数のattributeは、なんらかの抽象化を行うと独立したelementにできるはずだ。elementに昇格させて、attributeのベタな並びを抑制した方がいい。せいぜい、属性群が1行(80文字程度)に収まるといった、暗黙的なローカル基準はあってもよい。

(3) 汎化表現の扱い

汎化で表現されるクラスをXMLで表現する場合にどうするか。特化されたクラス名相当をタグ名とした要素(element)にした方がよいか。例えば、媒質には均質な媒質と不均質な媒質があり、そこを通過する光線の屈折率にはそれぞれの計算式があるとする。媒質は均質媒質と不均質媒質に特化している。均質媒質(HOMOGENEOUS)の屈折率計算式にはSELLMEIERやHERZBERGERなどがある。以下はどうだろう。

    <medium>
      <!-- snip -->
      <homogeneous type="SELLMEIER">
        <var>10.0</var>
        <var>20.0</var>
        <!-- snip -->
      </homogeneous>
    </medium>

"homogeneous"(均質媒質)をタグ名にするのはよくないだろう。不均質媒質も存在するなら抽象化して、"homogeneous"はなんらかのタイプなどとしてattributeなどで表現した方がいい。例えば以下のようになる。

      <expression type="homogeneous" method="SELLMEIER">
        <!-- snip -->
      </expression>

(4) 論理値の扱い

要素/属性によってはオプション的なものがある。例えば、あるか/ないかという論理値の表現だ。これらを全ての要素に、わざわざ「="true"」と書くのも冗長な気がする。trueとなる要素に対してのみ、ちょうどHTML表現での"nowrap"(表のセルを自動改行しないようにする)ように記述できないか。

   <td width="100" nowrap />

これは、XMLではと言うより、私はまさにnowrap="true"のようにしか記述したことがない。HTMLで許されるnowrapのような省略された記述はしたことがない。しようと思ったこともなく、できるのかどうかも確認はしていない。
nowrap="true"と属性に記述する場合、プログラム上では注意が必要だ。nowrapが存在して、その値をlowercaseに変換して比較し、"true"の場合にtrueとして判定することになる。この判定ルールを決めておく必要がある。つまり、単にnowrapのような省略形で値がないものはfalseとして判定するということになる。"ture"のようなスペルミスもfalseになる。通常、falseのチェックは行わない。これは通常のプログラミング言語の条件判定(0=false、0以外=true)と論理が逆になるという意見もいただいたが、よしとしよう。また、厳密に"true"|"false"以外は例外にするという仕様もあるかとは思うがやったことはない。
このように考えると、単にnowrapだけの省略形というのは、trueなのかfalseなのか解釈がはっきりしないと言える。冗長でもXMLでは、nowrap="true"のように明示させる方ががいい。

(5) ユニークキーの表現

IDなどの要素をユニークに特定できるキーとなる情報は、どのように表現すべきか。
elementのtextで表現してもよいが、ユニークなIDのコード体系が確立しているなら、attributeで表現するほうが望ましい。複数の属性でユニーク性が判断される場合、それらの属性群はattributeで表現するなら全てattribute、elementのtextで表現するなら全てtextで表現すること。例えば、絵の具がメーカ名、製品名、バージョンの3項目でユニークになるなら、当然以下が望ましい。

    <paints maker-id="Holbein" name="deluxe-paints" version="001"/>
    <!-- または -->
    <paints>
      <maker-id>Holbein</maker-id>
      <name>deluxe-paints</name>
      <version>001</version>
    </paints>

(6) 複数要素の表現

配列など複数個を表すにはelementを用いるが、その際の要素の個数については以下のように明示しておく必要があるか。

    <paints type="tube" num="3">
      <color>Viridian</color>
      <color>Turquoise Blue</color>
      <color>Coral Red</color>
    </paints>

私ならば、num="3"というattributeは追加しない。むしろ、が複数であることを明示してというelementで挟み込む。これによって、要素の数ではなく、要素が複数ある可能性を明示しておくのだ。またこれによって、type="tube"がelementに昇格するなどの場合にも対応できるのだ。また、にorder="1"のような順序性やユニーク性を表現できるものをattributeに添えておく場合もある。の中のの順序がおかしくなった場合にも対応できる。

    <paints type="tube">
      <colors>
        <color order="1">Viridian</color>
        <color order="2">Turquoise Blue</color>
        <color order="3">Coral Red</color>
      <colors>
    </paints>

ただし、順序を固定した配列やリストの場合にはnum="3"のような要素数を明示しておくことはありえるし、見たことも多々ある。私はお奨めはしない。
以上、回答した内容をメモして整理しておいた。この回答の前提では、XMLファイルはSAXではなく、DOMで読み込むことになっていたが、特にその差異によって構造を変える必要はないだろう。