「カジュアル過ぎるmicroformatsを少しだけ厳密に」の続きです。
マイクロフォーマット(microformats)の仕様は、自然言語と事例により記述されています*1。これは、親しみやすく読みやすく、教育的な読み物としては良い手法だと思います。が、仕様書としては問題と困難をかかえています。それを具体的に指摘したいと思います。例として、hCard仕様とhReview仕様(のごく一部)を取り上げます。
内容:
プロパティの値って何?
マイクロフォーマットのデータでは、プロパティと呼ばれる名前・値ペア(name-value pair)が基本になります。単一のプロパティ、またはプロパティの(階層的な)集合が、マイクロフォーマットの想定しているデータでしょう。が、そうハッキリ書いてあるわけではありませんし、プロパティ値のデータ型についてもハッキリしません。とりあえず、階層の末端にあるプロパティ値は文字列型だとして話を進めます。
原則として、プロパティ名はHTML要素のクラス名、プロパティ値はHTML要素の内容としてマークアップされます。
<p class="vcard"> 私の名前は<span class="fn">檜山正幸</span>です。 </p>
これは、次のようなJSONデータを表現していると思っていいでしょう。
{ "vcard" : { "fn" : "檜山正幸" } }
では、次のマークアップはどうでしょう。[追記]ホントのHTMLコメントを書くとそれは見えなくなってしまうようなので、余分な空白を入れて見えるようにしました。でも、HTMLコードを入れるブロック内でコメントを表示しないのは「はてなダイアリー」のバグくさい。[/追記]
<p class="vcard"> 私の名前は<span class="fn">< !-- キマイラ飼育記のオヤジ -->檜山正幸</span>です。 </p>
常識的に考えてコメントは無視するので、データとしては何も変わらないでしょうね。
じゃ、次は?
<p class="vcard"> 私の名前は<span class="fn"><[CDATA[檜山正幸]]></span>です。 </p>
CDATAセクションはテキスト扱いでいいでしょう。これもまー常識。
それじゃ、これは?
<p class="vcard"> 私の名前は<span class="fn">檜山正幸 <img src="hiyama.jpeg" /></span>です。 </p>
値のデータ型を文字列だとする限り、子要素を値として解釈する適切な方法がないので、選択肢は次のどちらかです。
- エラーとする。
- 子要素であるimgを無視する。
マイクロフォーマットとしてはどちらなのか? 明白な記述はないようです(僕の見落としかもしれませんが)。推測するに、子要素は無視する方針のようです(推測に過ぎません!)。そうなると、次のマークアップはうまくないことになります。
<p class="vcard"> 私の名前は<span class="fn"><b>檜山</b> <i>正幸</i></span>です。 </p>
まとめてみます:
- 文字列型のプロパティ値を要素内容として表現するとき、要素内容に含まれるコメントと子要素は無視される。
このルールは「マイクロフォーマット仕様」からの引用ではありません。僕の推測です。他の人は別な推測をするかも知れません。何種類の推測が可能かは推測できません。
複数のクラス名
マイクロフォーマット仕様に、次のようなサンプルがあります(そのまんまの引用ではありませんが)。
<p class="vcard"> <span class="fn nickname">m-hiyama</span> </p>
これは、標準の名前(fn)としてもニックネームとしても m-hiyama を使うという意味で、次のマークアップと等価です。
<p class="vcard"> <span class="fn">m-hiyama</span> <span class="nickname">m-hiyama</span> </p>
一方、次のような例もあります。
<div class="hreview"> <div class="item vcard"> <span class="nickname">m-hiyama</span> </div> </div>
この例の class="item vcard" という指定を上と同じように展開すると:
<div class="hreview"> <div class="item"> <span class="nickname">m-hiyama</span> </div> <div class="vcard"> <span class="nickname">m-hiyama</span> </div> </div>
これは違うよな! 正解は(おそらく)次でしょう。
<div class="hreview"> <div class="item"> <div class="vcard"> <span class="nickname">m-hiyama</span> </div> </div> </div>
つまり、並列に展開するのではなくて、階層的な入れ子を作ることになります。そういうことが許されるなら、次もいいはずです。
<div class="hreview item vcard"> <span class="nickname">m-hiyama</span> </div>
ほんとにいいかどうかは分かりませんけど。
さらに、次の例はどうでしょうか。
<div class="hreview item vcard fn nickname"> m-hiyama </div>
クラス名として、hreview, item, vcard, fn, nickname の5つが列挙されていますが、hreview, item, vcard は入れ子に展開し、fn, nickname は並列に展開すべきでしょう。
<div class="hreview"> <div class="item"> <div class="vcard"> <div class="fn">m-hiyama</div> <div class="nickname">m-hiyama</div> </div> </div> </div>
複数のクラス名の列挙は、「値の共有」と「入れ子階層の省略」の両方の意味で使っているようです。そうだとすると、複数のクラス名をキチンと解釈するプログラムを書くには、すべてのクラス名(プロパティ名)の親子関係をプログラム側で把握してなくてはなりません。クラス名を調べながら、正規化されたツリー構造を構築していく手間がかかります。
ここらへんの事情に関して、実装(プログラミング)のガイドラインがないと、必要な処理を見落としたり、アルゴリズムが思いつかないで途方にくれたりするんじゃないのかな。実装者には全然優しくないですね。これだと実装はなかなか出てこないでしょう。
CSSセレクタでデータを抽出できるか
前の節と同じ例で、「hreviewプロパティの下のitemプロパティの下のvcardプロパティの下のfnプロパティ値」をCSSセレクタで取り出すことを考えてみます。ただし、fnプロパティの値そのもの(文字列)ではなくて、値を保持する要素を選択できればいいとします。
前節の最後のように、階層構造が正規化されていれば、次のセレクターでOKです。
.hreview .item .vcard .fn
しかし、複数クラス名で階層の省略がされている状況では、いくつかの候補があります。
.hreview .item .vcard .fn, .hreview.item .vcard .fn, .hreview .item.vcard .fn, .hreview .item .vcard.fn, .hreview.item.vcard .fn, .hreview .item.vcard.fn, .hreview.item.vcard.fn
これで全てではありません。マイクロフォーマットのマークアップルールには、いくつかの例外規則(exception rules)があります。そのなかのひとつが、「abbr要素を使えば、値は要素内容ではなくてtitle属性値になる」というのがあります。つまり、次のセレクタで要素を探し、title属性値を取り出すことも考えなくてはなりません。
.hreview .item .vcard abbr.fn, .hreview.item .vcard abbr.fn, .hreview .item.vcard abbr.fn, .hreview.item.vcard abbr.fn
それだけでは済まない
前節と同じ状況で、名前(fnプロパティ値)ではなくて、電話番号を取り出す場合はさらに厄介なことになります。電話番号には次のようなマークアップが許されるのです。
<!-- type と value に分ける --> <span class="tel"> <span class="type">Home</span>: <span class="value">+81 03-1234-5678</span> </span> <!-- 複数の type を、同じ value に付ける --> <span class="tel"> <span class="type">Home</span> (<span class="type">pref</span>erred): <span class="value">+81 03-1234-5678</span> </span> <!-- value を省略 --> <span class="tel"><span class="type">Home</span> +81 03-1234-5678</span> <!-- type も value も省略 --> <span class="tel">+81 03-1234-5678</span>
このようなマークアップから電話番号の型(データ型ではなくて電話の役割)と値の組を取り出すことを考えます。「カジュアル過ぎるmicroformatsを少しだけ厳密に」で導入した記法を使うことにします。一番簡単な例は次のとおりです。
/* hreviewプロパティの下のitemプロパティの下のvcardプロパティの下の * 電話番号情報 */ { "type" : string(content(".hreview .item .vcard .tel .type")), "value" : string(content(".hreview .item .vcard .tel .value")) }
type, valueを省略するマークアップパターンも書いてみます。
/* value を省略 */ { "type" : string(content(".hreview .item .vcard .tel .type")), "value" : string(content(".hreview .item .vcard .tel")) } /* type, value を省略 */ { "value" : string(content(".hreview .item .vcard .tel")) }
同じvalueに複数のtypeが付いた例では、事前に複数のtelプロパティに展開しておくか、typeプロパティ値のデータ型を「型の名前のリスト」として対処する必要があります。そのときは、elements(".hreview .item .vcard .tel .type")(複数形に注意)で要素リストを取得して、要素内容を文字列化したリストをtypeの値とすることになります(実は、この方法には落とし穴があります*2)。
以上の例は、階層構造は正規化されている前提でした。複数クラス名の指定により「入れ子の階層構造が省略」されると場合分けは増えます。組み合わせの数は掛け算で増えます。abbr例外規則も考慮しなくてはなりません。さらに組み合わせが掛け算で増えます。ひょっとすると、僕が見落としている例外規則があるかもしれません。あればまた掛け算で増えます。
こんな事していいのか?
マイクロフォーマットの特徴は、1つのデータインスタンスに対して、多様なマークアップ表現を許していることです。その多様さは、主に事例を通じて説明されています。多様さを統制している法則は明示的ではないので、許されるマークアップの境界線はかなり曖昧です。仕様の隅から隅まで、行間も含めて読み通せば、法則も境界線も分かるかも知れませんが、それには相当の手間と努力が必要でしょう。
例えば、次のようなマークアップはどうでしょう。
<div class="vcard"> <a class="tel" href="tel:09012345678">私の携帯電話番号</a> </div>
人間にとって、このマークアップの意味は明らかです。「人が先、機械は後」というマイクロフォーマットの方針からすると、「人間が読めるからいいんじゃないの」という気がします。また、HTMLのa要素やimg要素を使う場合は、プロパティ値としてhrefやsrc属性値を使っている前例もあります。「前例があるんだからいいんじゃないの」という気がします。
しかし、hCard仕様にこの例そのものは登場しません。このマークアップを正当化する根拠も見当たりません。とはいえ、hCard仕様の書きっぷりは「ここに書いてないものは許さない」という厳格性がないので「いいんじゃないの」という雰囲気も濃厚です。
と、こういう曖昧な状況では、マトモな実装やシステムが作れるはずもないでしょう。
マイクロフォーマットのアプローチはなぜ間違いなのか
マイクロフォーマットは、それ以前のメガフォーマットへの反省に基づき、アンチテーゼとして単純で軽量をウリにしました。しかし、マイクロフォーマット・データを解析・処理するプログラムが簡単かと言うと、それほど簡単じゃないのですよね。多少複雑なプログラミングを要求するのはまだいいのですが、どういうプログラムを書けばいいのかが曖昧すぎて、実装者をウンザリさせるでしょう。何百ページもあるメガフォーマットの仕様は量でウンザリさせましたが、1ページのマイクロフォーマット仕様は(実装者から見た)質でウンザリさせます。
マイクロフォーマットでは、1つのデータインスタンスに対して無限のマークアップを許します。マイクロフォーマット仕様書は、その無限集合の全体を、有限個の典型例で推測させるアプローチを採用しています。比喩的に言えば、「偶数の全体」を次のように定義するようなもんです。
- 2, 4, 6, 8 みたいな数
これだと、10, 0, -2 が偶数かどうかはホントのところ分かりません。やはり次の定義が必要なのです。
- 2で割って余りが0となる整数 (ただし、整数の定義は既知とする)
コンピュータ処理に関係する仕様が、形式性や厳密性に配慮しないのはヤッパリ全然ダメだなー、と感じます。巨大で難解な仕様も使えないけど、情報不足で曖昧な仕様も使えないのです。
それでも使いたい
なんで僕がこんなにマイクロフォーマットの悪口を縷縷述べるかというと、マイクロフォーマットに対して今でも共感と期待を抱いているからです。いくつかの問題点を指摘はしましたが、マイクロフォーマットの発想や主張には強く賛同するし、根本的なアイディアの秀逸さには魅力を感じます。だから、ダメなところを何とかしたいとも思うのです。
マイクロフォーマットは、Webページの作成者にほとんど負担をかけずに*3、Webページ/Webサイト、ひいてはWeb全体に構造を与える手段となります。処理プログラムを作るのは意外に面倒ですが、そうはいってもHTMLスクレイピングと比べれば雲泥の差があります。
マイクロフォーマットをみんなが使えば、みんなが(少しだけは)幸せになれると思うのだけど。