第6回 XMLにおける名前、トークン、リテラルデータ

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

川俣 晶
株式会社ピーデー
2003/2/6



運用しているときに知りたくなる
今回の主な内容
運用しているときに知りたくなる
名前とトークン
引用符でくくって記述するリテラルデータ
リテラルのBNF表記
EntityValueの注意事項

 今回は、XML1.0勧告の、第2章「2. Documents(文書)」の「2.3 Common Syntactic Constructs(共通の構文構成子)」の途中から、「2.4 Character Data and Markup(文字データ及びマーク付け)」までを解説していく。

 「2 Documents」は、文書の基本的な構造について述べている部分である。その中で、「2.3 Common Syntactic Constructs」は名前文字などを定義している。つまり、要素などで使用される名前やNmtoken型の値に、使用してよい文字の組み合わせの詳細を記述している。「2.4 Character Data and Markup」は、文字データとマーク付けの基本的な説明が行われている。例えば文字データとして認識される空白文字と、認識されない空白文字の例などが説明されている。

 この部分は、後で役に立つことになる可能性が高い。例えば、要素や属性の書き方はだれでも熱心に勉強するので不安はないだろう。しかし、Nmtoken型の値として記述できる正しい文字の並びは何かということは、実際に運用しているときにふと分からなくなり、知りたくなる可能性が出てくるものである。

編集局注:この連載では、XML 1.0勧告の日本語訳として、日本工業規格 JIS X 4159:2002(リンク先は該当規格の原案ですが、最終版とほぼ同等の内容です)を参照しています。

名前とトークン

 さて、前回「『整形式』のXML文書と文字の定義」の続きである。まずはNmtokenについてである。NmtokenというのはNameとTokenを足した造語であって、英語の辞書には載っていない。分からない人には、何のことやらさっぱり分からないだろうが、実際にXMLを使うときには便利なものなので、特に定義されているものである。W3CのXML仕様書によると、Nmtokenは次のように説明されている。

An Nmtoken (name token) is any mixture of name characters.

 ここでは、Nmtoken(名前トークン)は名前文字の列とする、と記述されている。名前文字は前回説明したと思うが、その文字を任意の回数繰り返したものである。例えば、“XML”はNmtokenになるし、“小泉純一郎”もNmtokenになる。しかし、名前文字に該当しない文字を含んでいるとNmtokenにならない。例えば、“Extensible Markup Language”は名前文字ではない空白文字を含んでいるので、Nmtokenにならない。この特徴によって、この後出てくるNmtokens、つまりNmtokenの複数形が成立するのである。

 以下の表は、ここまで出てきた定義をBNFで書いたものである。しかし、まだ出てきていない新しい内容も含まれている。

Names and Tokens
[4]    NameChar    ::=    Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
[5]    Name    ::=    (Letter | '_' | ':') (NameChar)*
[6]    Names    ::=    Name (S Name)*
[7]    Nmtoken    ::=    (NameChar)+
[8]    Nmtokens    ::=    Nmtoken (S Nmtoken)*

 表の1つ目にあるNameCharは、名前文字の定義を示している。次のNameは、NameCharを使った名前の定義を示している。Nameは要素や属性の名前などに使われるもので、XML利用者には最もなじみ深いものである。要素や属性の名前に使用できる文字は、ここで定義されている。すでに説明していることだが、1文字目と、2文字目以降では使える文字のバリエーションが異なるのがNameの特徴である。

 Namesは、Nameの複数形である。例えば、“AkaRanger”はNameであるが、“AkaRanger AoRanger KiRanger MomoRanger MidoRanger”はNameを5つ含むNamesである。Nameの区切りは空白文字を記述する。もちろん、上のBNFの定義に出てくるSは、すでに解説した空白文字の定義を参照している。

 次はNmtokenである。見てのとおり、名前文字(NameChar)の1回以上の繰り返しというシンプルな定義で、Nameとは異なっている。これにより、先頭が半角数字の0である“0Tester”はNameとしては間違いになるが、Nmtokenとしては正しいことになる。Nameは要素や属性の名前として使われるが、Nmtokenは属性のデータ型などとして使われる。

 Nmtokensについては、“0Tester”はNmtokenだが“0Tester Save The Earth”はNmtokenを空白文字(S)で区切って列挙したNmtokensとなる。これは、複数の名前を参照する属性のデータ型として便利に利用されるものである。

引用符でくくって記述するリテラルデータ

 さて次はリテラルデータ(Literal data)関係についての説明である。 XML仕様書では、リテラルデータを次のように解説している。

Literal data is any quoted string not containing the quotation mark used as a delimiter for that string. Literals are used for specifying the content of internal entities (EntityValue), the values of attributes (AttValue), and external identifiers (SystemLiteral). Note that a SystemLiteral can be parsed without scanning for markup.

 リテラルデータとは、ある文字列を引用符でくくって表記したものである。例えば

<a attr="value">

と書いたとき、"value"の部分がリテラルデータに当たる。しかし、XMLではリテラルデータにも何種類かあり、それぞれ定義が異なっている。上記の文章では、リテラルデータは引用符で囲まれた文字列であり、リテラルデータを区切る引用符は内部に含まれない文字だ。つまり、区切りがシングルクオーテーション(')のとき、'Let's go!'とは書けないということである。ただし、区切る引用符を変えて"Let's go!"ならOKである。

 上記の説明では、リテラルは内部実体の内容(EntityValue)、属性値(AttValue)、外部識別子(SystemLiteral)の指定に使用される、としている。詳細はこの後のBNFを見れば分かる。そして最後の一文で、外部識別子はマーク付けを走査することなく解析できると書いてある。外部識別子とは、外部のリソースを指定するために使われる。例えば、

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1- strict.dtd">

と書かれたとき、"DTD/xhtml1-strict.dtd"の部分がそれにあたる。

 この説明は逆にいえば、外部識別子の値を記述する際に、何らかのマーク付けを使用することはできないことを意味する。例えば、属性値は文字参照という一種のマーク付けを使うことができるので、シフトJISでXML文書を書いている場合でも、XMLで利用可能なあらゆる文字を記述できる。しかし、外部識別子の値を記述する際には文字参照を記述できないので、シフトJISでXML文書を書いている場合、シフトJISに含まれない文字は記述できないことになる。余談だが、このような制約があるので、XML文書をどんな文字エンコーディング方式で記述してもよい、ということは厳密には間違いということになる。

リテラルデータのBNF表記

 次はいよいよリテラルデータのBNF表記である。

Literals
[9]    EntityValue    ::=    '"' ([^%&"] | PEReference | Reference)* '"'
|  "'" ([^%&'] | PEReference | Reference)* "'"
[10]    AttValue    ::=    '"' ([^<&"] | Reference)* '"'
|  "'" ([^<&'] | Reference)* "'"
[11]    SystemLiteral    ::=    ('"' [^"]* '"') | ("'" [^']* "'")
[12]    PubidLiteral    ::=    '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
[13]    PubidChar    ::=    #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]

 さて上記のBNFを順番に見ていこう。[9]で定義されているEntityValueは、内部実体の内容を記述するために使われる。内部実体は、DTD内に内容が直接記述されている実体である。例えば、

<!ENTITY ProductName "最強XML解析機関">

として宣言された実体は内部実体である。

 EntityValueのBNFを詳しくみていくと、まずこの定義は、

'"' ([^%&"] | PEReference | Reference)* '"'

か、あるいは、

"'" ([^%&'] | PEReference | Reference)* "'"

のどちらかということを示している。両者の相違は、最初と最後に書かなくてはならない引用符の種類が「"」か「'」かというこtだけである。問題はこの引用符に囲まれた部分である、

([^%&"] | PEReference | Reference)*

という部分をさらに分解して読み取る必要がある。[^%&"]という部分は、%&"の3種類の記号以外のすべての文字(1文字)を表現している。「"」が入るのは、引用符が「"」のときで、引用符が「'」のときは「'」に変わっている。PEReferenceはパラメタ実体参照、Referenceは実体参照を示す。

 これらが「|」で区切られていることにより、どれか1つを記述することができることが分かる。そして最後の「*」により、0回以上の繰り返しであることが示されている。つまり、%&"または%&'の記号以外の全ての文字、パラメタ実体参照、実体参照のいずれかを0回以上繰り返して記述することが許されているわけである。ここで、「%」と「&」が例外として記述できないのはパラメタ実体参照や実体参照と区別できなくなるためである。しかし、この2つの記号は文字参照か定義済み実体の参照で記述できるので、直接記述できないだけで、書き込むことはできる。

 次の[10]として定義されるAttValueは属性値を記述するために使われる。属性値は、もちろん属性を記述する時の値である。

<tagname src="pic.jpg">

なら、"pic.jpg"の部分が属性値である。定義は、パラメタ実体参照を含まないだけでEntityValueとほぼ同じとなっている。属性はDTD外に記述するので、パラメタ実体参照を使うことはできない。また、パラメタ実体参照を含まないことにより、例外として除去される文字に%が含まれないことも分かるだろう。

 [11]のSystemLiteralが外部識別子である。これは、AttValueからさらに実体参照を除いた定義といえる。それに伴って、除外される文字に「&」が含まれていない。例えば、属性値として"Cut&Paste"と書いたら間違いであるが、外部識別子に書いてもエラーにはならないわけである。

 残る2つ[12]と[13]は、公開識別子のリテラルデータを定義している。公開識別子とは、さまざまなリソースを一意に識別するために公的に定められた名前である。SGML時代から使われている古いもので、具体的な名前はISOなどの規格などで名前が定められているものである。例えば、

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">

と書かれたとき、"-//W3C//DTD XHTML 1.0 Strict//EN"の部分がそれに当たる。

 公開識別子はすでにISOなどの規格で非常に厳密に定義がなされているので、使用できる文字も限定されている。しかし、XMLで使われる名前文字とは互換性がないので、別個の定義がここにある。PubidCharが、それらの文字を定義している。PubidCharの定義は、

#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]

となっているので、半角空白、復帰、改行、半角アルファベットaからzまでの小文字、大文字、半角数字の0から9まで、そして、-'()+,./:=?;!*#@$_%のそれぞれの記号が使用できる。公開識別子そのものを記述するリテラルデータはPubidLiteralによって定義されている。

 ここで注意が必要なのは、区切り引用符に「'」を使用した場合のみ、内部に記述できる文字から'が除外されていることである。つまり、「'」を含む値を書きたい場合は、引用符として「'」を使わず、「"」を使わねばならない。XMLではほとんどの場合、引用符として「"」と「'」はどちらも使っても優劣はないが、ここだけは例外的な定義だといえる。

EntityValueの注意事項

 XML仕様書では、続いてEntityValueの使い方に関する注意事項が記述されている。

Note:

Although the EntityValue production allows the definition of an entity consisting of a single explicit < in the literal (e.g., <!ENTITY mylt "<">), it is strongly advised to avoid this practice since any reference to that entity will cause a well-formedness error.

 EntityValueでは、リテラルデータに1つの不等号記号(<)からなる実体の定義を認めている。<!ENTITY mylt "<">といった感じである。しかし、これを参照すると整形式としてエラーとなってしまうので避けることを強く勧める、としている。これは、XMLパーサが検出すべきエラーとは少し違うことに注意が必要である。つまり、

<!ENTITY mylt "<">

と記述することはエラーではないが、これを参照すると結果的にエラーになるので、使うべきではない、という注意事項である。

文字データとマーク付け

 さて、W3CのXML 1.0仕様書を読み進めて、次は2.4 Character Data and Markupに入る。ここでは、文字データとマーク付けに関する説明が記述されている。しかし、詳細の定義はまだ出てこないものが多い。全体的な枠組みを説明している。

Text consists of intermingled character data and markup. [Definition: Markup takes the form of start-tags, end-tags, empty-element tags, entity references, character references, comments, CDATA section delimiters, document type declarations, processing instructions, XML declarations, text declarations, and any white space that is at the top level of the document entity (that is, outside the document element and not inside any other markup).]

 まず、テキスト(Text)は、文字データとマーク付けが混じり合って構成されたものである、としている。これはXML文書を作成したことがあれば容易にイメージできるだろう。

 次に、マーク付け(Markup)という言葉についての定義が記述されている。開始タグ、終了タグ、空要素タグ、実体参照、文字参照、コメント、CDATAセクションの区切りとなる文字列、文書型宣言、処理命令とXML宣言、テキスト宣言、文書の最上位レベルにあるすべての空白文字がマーク付けとなる形である。最後の「文書の最上位レベルにあるすべての空白文字」という表現が分かりにくいかもしれないが、カッコ内に補足説明が記述されている。そこには文書要素の外側で、ほかのマーク付けの内側にない空白文字、と記述されている。例えば空白文字を[S]と記述したとき、

<?xml version="1.0"?>[S]<a>[S]</a>

というXML文書の最初の空白文字はマーク付けと見なされるが、2番目の空白文字はa要素の内容の文字データであると見なされるというわけである。そのため、文書要素の外側にある空白文字に、文字データとしての意味を持たせることはできないことになる。

 さて、今回はここまでである。次は詳細な文字の使い方などの説明に話は進む。

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


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

注目のテーマ

HTML5+UX 記事ランキング

本日月間