第16回 属性の型、文字列型とトークン化型を理解する

XML 1.0は、1998年にW3Cから勧告として公開された。当然中身は英語で、しかもEBNFと呼ばれる式によって重要な部分が記述してある。この連載では、XML 1.0を深く理解するために、そのXML 1.0勧告の最新版「Extensible Markup Language (XML) 1.0 (Second Edition)」をだれでも分かるように、やさしく読み解きながら解説していくことを目指している。(編集局)

川俣 晶
株式会社ピーデー
2003/12/9



3種類ある属性の型
今回の主な内容
3種類ある属性の型
属性の型と次世代スキーマ言語
属性の型の内容
属性の型の生成規則
 AttType
 StringType
 TokenizedType
TokenizedTypeの妥当性制約
  妥当性制約:ID
  妥当性制約:1つの要素ごとに1つのID
  妥当性制約:ID属性のデフォルト
 妥当性制約:IDREF
 妥当性制約:実体名
 妥当性制約:名前トークン

 前回は、XML1.0勧告の中で「3.2.2 Mixed Content(3.3.2 混合内容)」から「3.3 Attribute-List Declarations(3.3 属性リスト宣言)」、つまり「3.3.1 Attribute Types(3.3.1 属性の型)」の手前までを読んだ。混合内容は、前々回の要素内容と対をなすものである。混合内容は、文章系のXML利用においては、必須ともいえる重要な機能である。XMLを単なるRDBMSの交換言語や、ちょっと高度なCSVとして使う方法しか知らない読者には価値ある解説だったかもしれない。属性リスト宣言は、それまでの要素の型を宣言する要素型宣言から離れて、属性に関する宣言の説明に入る入り口である。要素と属性の相違についても説明を行ってみたが、XMLで両者をどう使い分けるかを考える参考になったのではないかと思う。

 さて、今回は「3.3.1 Attribute Types(3.3.1 属性の型)」について、文字列型・トークン化型・列挙型と3種類あるうちの、文字列型・トークン化型について解説する。属性の型は、DTDにおいては要素型とはまったく異なるものなので、新しい知識として読みこなす必要がある。これは多くの新しいXMLのスキーマ言語に継承されているものなので、ここでしっかり学んでおく価値があるだろう。これらの新しいスキーマ言語では、DTDとの互換性のために、今回説明する属性の型と互換のある型を導入している場合も多く、DTDでの型の定義を知っておくと、新しいスキーマ言語がよく分かるという効能もあるだろう。

編集注:この連載では、XML 1.0勧告であるW3Cの「Extensible Markup Language (XML) 1.0 (Second Edition)」(英語)を参照し、その日本語訳として、日本工業規格 JIS X 4159:2002(リンク先は該当規格の原案ですが、最終版とほぼ同等の内容です)を参照しています。本文中のピンクの地の部分は、XML 1.0勧告の原文を示しています。

属性の型と次世代スキーマ言語

 それでは「3.3.1 Attribute Types(3.3.1 属性の型)」を読んでいこう。属性の型と要素型は、XML 1.0勧告では、まったく別個のものである。属性の型とはこれから説明するようなIDや記法などだが、要素型とは要素の名前である。しかし、新しい世代の多くのXMLのスキーマ言語では、両者に共通の型を指定できるものが多く、そこで使われる型は、ここで説明されている属性の型を含む、さらにさまざまなデータ型を追加したものであることが多い。

 例えば、ここで説明される型には、整数型のような型はない。それらは、新しい世代のスキーマ言語で追加されているものである。逆に、ここで説明されている属性の型の多くは、そのまま新しい世代のスキーマ言語の型に取り込まれている。さらに、属性の型は属性にしか適用できないが、新しい世代のスキーマ言語を使うと、同じ型を要素にも適用できる場合もある。そういう意味で、ここで属性の型をしっかりと理解し、把握しておくことは価値がある。

 この話題には逆の側面もある。新しい世代のスキーマ言語が、ここで説明される属性の型をサポートするということは、DTDとの互換性を維持するという意味合いがある。つまり、DTDを単純に機械的に変換するだけで、等価なスキーマが生成可能であることが、DTDから新しいスキーマ言語へ移行するためには必須条件となる。それを実現するためには、まったくゼロから作られた新しいスキーマ言語であっても、ここで説明されている属性の型と互換性を持たせる必要があるということである。その点で、新しいスキーマ言語を学んでいて感じる「どうしてこの型はこういう定義なのだろうか」という疑問を、この属性の型の説明によって解消できるケースがあるかもしれない。

属性の型の内容

 それでは実際の内容を読んでいこう。まず、XMLの属性の型の種類の説明からである。

XML attribute types are of three kinds: a string type, a set of tokenized types, and enumerated types. The string type may take any literal string as a value; the tokenized types have varying lexical and semantic constraints. The validity constraints noted in the grammar are applied after the attribute value has been normalized as described in 3.3 Attribute-List Declarations.

XMLの属性の型は、文字列型・トークン化型・列挙型の3種類とする。文字列型は値として任意のリテラル文字列を取る。トークン化型は、字句および意味に関して、次に示すさまざまな制約を持つ。文法中で書かれている妥当性制約は、3.3 属性リスト宣言で記述されるとおり属性値が正規化された後で適用される。

 冒頭で3つの型を紹介し、それぞれの詳しい説明がその後に続く。しかし、説明の順序が少し相前後しているので注意が必要である。最初に、文字列型・トークン化型の説明があり、その次に、EBNFの生成規則が書かれている(EBNFの詳細については、「第2回 XML勧告読解に必須のEBNF」を参照)。この生成規則には、3種類を参照する生成規則AttTypeが含まれるが、列挙型に関する生成規則は、後で別途まとめて出てくるので、ここには記述されない。また、列挙型に関する説明文も、後でまとめて出てくる。

 文章を順番どおり読んでいこう。まず文字列型については、文字として記述できるすべての文字の列ということである。これは特に難しいことはないだろう。整形式でXML文書を扱っている場合は、属性の型は暗黙的にこの型として扱っていることになる

 次はトークン化型である。字句とは文字の並び順、意味とは文字の並びが何を表現しているかを示す。次に示すさまざまな制約とは、次に記述されているEBNFの生成規則と、いくつかの妥当性制約を意味する。つまり、トークン化型とは何かという定義が、文章ではなく、これらの生成規則と妥当性制約から与えられているので、これらを読まずして、トークン化型が何かを把握することはできない。

 なお、トークン(token)とは、辞書を引くと「しるし、証拠、特徴、記念品」といった意味が出てくるが、コンピュータの世界では、1つの区切られた文字列、というような意味合いで使われることが多い。例えば、Javaのクラスライブラリに見られるStringTokenizerクラスは、文字列(string)を分割して複数のトークンにする(tokenize)機能を持ったクラスである。ここでは、何らかのルールを課して、文字列をいくつかに分割したものがトークンであると考えるとよい。そう考えると、後で説明されるトークン化型には複数形が存在することの意味が分かりやすくなる。文字列型は、1つの文字列型が1つの文字列に対応するが、トークン化型は1つの文字列が複数のトークンを含む場合があるのである。

 話を戻そう。次の文章については、具体的に正規化で何が行われるかは、「3.3 Attribute-List Declarations(3.3 属性リスト宣言)」で説明されるので、ここでは、正規化と呼ばれる手順があるということだけ頭の片隅に置いて話を先に進めよう。

属性の型の生成規則

 次は、EBNFによる生成規則である。

Attribute Types
[54]    AttType    ::=    StringType | TokenizedType | EnumeratedType
[55]    StringType    ::=    'CDATA'
[56]    TokenizedType    ::=    'ID' [VC: ID]
[VC: One ID per Element Type]
[VC: ID Attribute Default]
| 'IDREF' [VC: IDREF]
| 'IDREFS' [VC: IDREF]
| 'ENTITY' [VC: Entity Name]
| 'ENTITIES' [VC: Entity Name]
| 'NMTOKEN' [VC: Name Token]
| 'NMTOKENS' [VC: Name Token]

AttType

 まず、生成規則[54] AttTypeだが、これは属性の型(Attribute Types)そのものを表現している。中身は、生成規則StringTypeまたはTokenizedTypeまたはEnumeratedTypeのいずれかであるとしているが、これは文字列型・トークン化型・列挙型のいずれか1つという意味に等しい。

StringType

 次に生成規則[55] StringTypeは文字列型の生成規則である。その内容は文字列CDATAということである。文字列型は「文字として記述できるすべての文字の列」だと説明したばかりなのに、どうしてCDATAとしか書けないのか、と思った読者もいると思う。このような疑問は、属性値として使用可能な文字列と、型を指定する文字列の相違を把握することで解くことができる。いまここで読んでいる個所は、DTD内で属性の型を指定する構文に関する説明である。実際に属性に記述される文字列に関する説明ではない。つまり、ここでCDATAと書かれている文字列は、「属性に文字として記述できるすべての文字の列を使用可能とする指定を行うために記述すべきキーワード」という位置付けになるわけである。

TokenizedType

 次は、生成規則[56] TokenizedTypeである。これは、トークン化型に対応する。これも同様に、「トークン化型の指定を行うために記述すべきキーワード」であることに変わりはない。実際に記述できるのは、ID、IDREF、IDREFS、ENTITY、ENTITIES、NMTOKEN、NMTOKENSの各キーワードである。それぞれには、妥当性制約が付加されていて、それが型の意味、意図を表現するような形になっている。

 なお、同じキーワードで最後にSが付くものと付かないものの組み合わせがあることに気付いた人もいるだろう。例えば、IDREFとIDREFSである。ここで、Sが付くものは付かないものの複数形となっている。例えば、IDREFはIDの参照という型であるが、IDREFSは複数のIDを列挙して参照できるIDREFの複数形となる。このルールを覚えておけば、覚える必要のあるトークン化型名は約半分に減る。なお、IDのみ複数形はないが、これはIDが一意に文書中の要素を識別するための名前であることを考えれば、当然のことといえるだろう。

TokenizedTypeの妥当性制約

 さて、それでは個々の妥当性制約を読んでいこう。

妥当性制約:ID

Validity constraint: ID

Values of type ID must match the Name production. A name must not appear more than once in an XML document as a value of this type; i.e., ID values must uniquely identify the elements which bear them.

妥当性制約:ID

ID型の値は、生成規則Nameにマッチしなければならない。1つのXML文書内では、1つの名前が、この型の値として複数回現れてはならない。つまり、IDの値は、要素を一意に特定しなければならない。

 まず、3つあるID型の妥当性制約の「妥当性制約:ID」である。型の名前も妥当性制約の名前も、どちらもIDなので分かりにくいが、慎重に区別して読んでほしい。冒頭に書かれている生成規則Nameは、要素や属性の名前を記述するために使われているものなので、それと同じ規則でID型の属性値も書くことができると思えば、難しいことはないだろう。次に続く2つの文章の意味は、特定の要素を識別するために使われるのがID型であるから、同じ名前が重複してはまずい、ということである。これは、1つのディレクトリに同じファイル名のファイルを複数作成できないことと似ている。

妥当性制約:1つの要素ごとに1つのID

Validity constraint: One ID per Element Type

No element type may have more than one ID attribute specified.

妥当性制約:1つの要素ごとに1つのID

要素型は、複数のID属性を持ってはならない。

 「妥当性制約:1つの要素ごとに1つのID」もID型の妥当性制約である。ID型の属性は、よくidのような名前の属性として使われるが、必ずidという名前であると制約されているわけではない。つまり、aという名前の属性と、bという名前の属性の両方がID型であるとき、<element a="id1" b="id2">というように記述することもできそうに見える。しかし、この妥当性制約が、このように記述することが誤りであることを示している。

妥当性制約:ID属性のデフォルト

Validity constraint: ID Attribute Default

An ID attribute must have a declared default of #IMPLIED or #REQUIRED.

妥当性制約:ID属性のデフォルト

ID属性は、デフォルトとして、#IMPLIEDまたは#REQUIREDを宣言しなければならない。

 「妥当性制約:ID属性のデフォルト」もID型の妥当性制約である。#IMPLIEDと#REQUIREDは、属性のデフォルトの説明(「3.3.2 Attribute Defaults(3.3.2 属性のデフォルト)」)に出てくるもので、ここでは詳しく説明しないことにする。簡単にいえば、#REQUIREDはその属性が必須であること、#IMPLIEDはデフォルト値がないことを意味する。裏を返せば、ID型の属性にはデフォルト値を指定できないことを意味する。#REQUIREDが指定されれば属性を省略することはできず、デフォルト値は意味を持たない。#IMPLIEDであれば、デフォルト値がないことが明示的に示される。これは、ID型の意図から考えれば当然のことといえる。デフォルト値を指定すると、同じ値が複数の要素に付与される可能性があり、それは要素に一意の識別名を付けるというID型の意図に反してしまうのである。

妥当性制約:IDREF

Validity constraint: IDREF

Values of type IDREF must match the Name production, and values of type IDREFS must match Names; each Name must match the value of an ID attribute on some element in the XML document; i.e. IDREF values must match the value of some ID attribute.

妥当性制約:IDREF

IDREF型の値は、生成規則Nameにマッチしなければならない。IDREFS型の値は、Namesにマッチしなければならない。おのおののNameは、XML文書内に存在する要素のID属性の値とマッチしなければならない。つまり、IDREFの値は、あるID属性の値とマッチしなければならない。

 「妥当性制約:IDREF」は、IDREF型とIDREFS型に関する妥当性制約である。IDREF型の値の生成規則は、当然ID型と同じである。IDREF型はID型の値を参照するものだから、ID型で記述可能な名前と、IDREF型で記述可能な名前の条件が一致していなければならない。もし、一致していなければ、記述されているID型の値をIDREF型で参照できない、という事態も起こってしまうだろう。次のIDREFS型の値であるが、最後にSが付くものは、付かないものの複数形である。生成規則Namesは、要するに空白文字で名前を区切って記述したものである。この生成規則の詳細は、本連載の「第6回 XMLにおける名前、トークン、リテラルデータ」で解説しているので参照してほしい。後半の文章は、IDREFとIDREFSの双方に含まれるNameに関する記述である。これらは、XML文書の中に書かれたいずれかのID型の属性と同じ名前を持つ必要があることを示す。IDREFとIDREFSは、どちらも、存在するID型の値を参照するために使われるものであって、存在しないものを参照することに意味はないわけである。

妥当性制約:実体名

Validity constraint: Entity Name

Values of type ENTITY must match the Name production, values of type ENTITIES must match Names; each Name must match the name of an unparsed entity declared in the DTD.

妥当性制約:実体名

ENTITY型の値は、生成規則Nameにマッチしなければならない。ENTITIES型の値は、Namesにマッチしなければならない。おのおののNameは、DTDで宣言する解析対象外実体とマッチしなければならない。

 次の「妥当性制約:実体名」は、ENTITY型とENTITIES型に関する妥当性制約である。冒頭の2つの文章を見る限り、IDREF型とIDREFS型との違いが見えてこないかもしれない。しかし、次の文章ではっきりとした違いが見えてくる。つまり、IDREF型とIDREFS型はID型の値を参照する型であるのに対して、ENTITY型とENTITIES型は、解析対象外実体を参照するものである。解析対象外実体は、「4 Physical Structures(4 物理構造)」に説明が出てくるので、ここでは深く立ち入らないで先に進もう。

妥当性制約:名前トークン

Validity constraint: Name Token

Values of type NMTOKEN must match the Nmtoken production; values of type NMTOKENS must match Nmtokens.

妥当性制約:名前トークン

NMTOKEN型の値は、生成規則Nmtokenにマッチしなければならない。NMTOKENS型の値はNmtokensにマッチしなければならない。

 次の「妥当性制約:名前トークン」は、NMTOKEN型とNMTOKENS型に関する妥当性制約である。ここで記述されている内容はこれだけである。IDREF(S)型とENTITY(ENTITIES)型にあったような追加の説明がない。しかし、よく読むと重大なヒントがこの文章に盛り込まれていることが分かる。IDREF(S)型とENTITY(ENTITIES)型では、生成規則NameとNamesを参照していたが、それとは異なり、ここでは生成規則NmtokenとNmtokensを参照している。Nmtokenは、name tokenの略である。生成規則Name、Names、Nmtoken、Nmtokensはいずれも、連載の第6回で、「Nameでは1文字目と2文字目以降では使える文字のバリエーションが異なるのが、Nmtokenにはそのような区別はない」と説明した。例えば、Nameでは先頭が半角数字という文字列は許されないが、Nmtokenではそれが許される。NMTOKEN型とNMTOKENS型は、そのような規則で記述されるNmtoken(s)に当たる文字列を属性に記述するための型であることが分かる。

 しかし、この説明だけでは釈然としない読者も多いと思う。IDREF(S)型やENTITIE(S)型には、ID型の値や、解析対象外実体を参照するという明確な目的があったが、それに匹敵する具体的な目的が書かれていないのである。これは書き忘れではなく、そのような具体的な目的は存在しない、というのが正解である。つまり、NMTOKEN(S)型は、IDREF(S)型やENTITIE(S)型のように、特定の何かを参照する型ではない、ということである。NMTOKEN型とNMTOKENS型が何を表現するために使われるかは、DTD作成者が決めることになる。

 だが、特定の何かを参照しないなら、文字列型でも同じではないかと思うかもしれない。しかし、生成規則Nmtokenで制約されるだけで有用な使い方ができる。例えば、何かの処理の都合で、データに名前を付けたいとする。その際、処理を楽にして安全性を高めるために、空白文字や、引用符などの記号を名前文字として許さないように制約したいとする。そういう場合に、特に厳密に文字の種類を制限する必要がなければ、NMOTKEN(S)型を指定すれば、すぐに目的を達成できる。また、使用できる文字の規則が要素や属性の名前と非常に似通っているので、要素や属性を自由に書ける人に説明する場合には都合がよい。「要素の名前と同じ文字が使えます」と説明し、「これらの名前では1文字目に使えなかった文字を1文字目に書いても使えます」と付け加えるだけでよい。とても簡潔で分かりやすく話を進めることができる。また、複数形が簡単に使えることも有益な特徴といえるだろう。

 さて、以上で文字列型・トークン化型の説明は終わりである。次回は、3種類の属性の型の最後の1つ、列挙型の説明に入る。

 

連載 やさしく読む「XML 1.0勧告」


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間