このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

カジュアル過ぎるmicroformatsを少しだけ厳密に

5年ほど前に「micro*」というタイトルの記事をいくつか書いたことがあります。

読みやすいように、これら3つの記事を次のURLにまとめてあります。

この当時僕はmicroformatsに強い共感・興味・期待を抱いていました。その後、microformatsがブレークしたわけではありませんが、着実に使われてはいるようです。2008年に次の書籍(翻訳)も出ています。

ISBN:978-4839925444:detail

最近また僕は、マイクロフォーマット(microformats)について調べたり考えたりしています。マイクロフォーマットは、単純で軽量なフォーマットですから、その仕様もまた単純軽量で、フォーマットごとに1枚のWikiページにまとめられています。事例を多用して、カジュアルに書かれています。このような親しみやすい記述が、マイクロフォーマットの趣旨にふさわしいということでしょう。

しかし、マイクロフォーマットの応用や実装を前提に読む立場からは、カジュアルな記述では正確な解釈が困難です。読み解く労力・負担がかえって増えてしまい、場合によっては曖昧にしか解釈できないこともあり、ほんとに困ってしまいます。

geo(地理的位置情報)、adr(住所)、hCard(名刺情報)、hCalendar(カレンダー項目、イベント情報)などの各種フォーマットが定義されていますが、その仕様には次が含まれます。

  • 想定しているデータ型
  • 許容できるHTMLマークアップ

このデータ型とマークアップについて、自然言語と事例を使って解説されているのですが、フォーマルで厳密な定義はありません。

データ型の定義にはスキーマ言語が必要であり、許容できるマークアップの定義はデータ抽出言語を使うのが適切でしょう。このような仕様記述用メタ言語を導入するのがそもそもオオゴトであり、マイクロ(小規模、単純)な方針からは外れるので採用できない -- そういう判断でしょうが、僕はそう思いません。マイクロなスキーマ言語/データ抽出言語を使えば、あまりオオゴトにならずに正確な記述が可能です。

事例:geoフォーマット

geoフォーマットのマークアップの例を挙げます。

<div class="geo">GEO: 
 <span class="latitude">37.386013</span>, 
 <span class="longitude">-122.082932</span>
</div>

この例だけで事情が了解できてしまうので、確かにそれ以上の説明も定義も要らない感じがします。

geoフォーマットには別なマークアップパターンもあります。

<p>位置は 
  <abbr class="geo" title="37.386013; -122.082932">このあたり</abbr>
  です。
</p>

マイクロフォーマットのマークアップは自由度が高く、用途と状況に応じて多様なマークアップを利用できます。これがマイクロフォーマットの特徴・優位性となっているのです。

では、プログラムで地理的位置情報を取り出すとき、どの程度のマークアップまでを対象として考えたらいいのでしょうか? あるいは「正しいgeoマークアップ」とは何でしょうか? HTML文書から取り出した地理的位置情報はいかなるデータ構造を持つのでしょうか? -- 自然言語と事例の記述をもとに、これらの問に正確に答えるのは困難です。

JSONのデータ型とスキーマ

データ型記述の基本に、JSONのスカラー・データ型を使うことにします。

  • string - Value must be a string.
  • number - Value must be a number, floating point numbers are allowed.
  • integer - Value must be an integer, no floating point numbers are allowed. This is a subset of the number type.
  • boolean - Value must be a boolean.
  • null - Value must be null. Note this is mainly for purpose of being able use union types to define nullability.

JSONデータ型にないデータ型は、 XMLSchema-2 (http://www.w3.org/TR/xmlschema-2/) に従うことにします。XMLSchema-2 で定義されたデータ型より、JSONの型を優先的に使うことにします。例えば、decimal型よりはnumber型を使います。

構造的なデータ型はJSONスキーマで表現できます。ここでは、オリジナルのJSONスキーマではなくて、CatyのJSONスキーマ構文を使います。Catyのスキーマ構文は、JSONオブジェクトリテラルと同じ形なので、読めば分かる形をしています(が、厳密な意味論を持っています)。

geoの型定義は次のとおり:

type geo = {"geo" : geoValue};
type geoValue = {
  "latitude" : number,
  "longitude" : number
};

念のため:

  1. geoという型は、"geo"という名前のプロパティを持つ。
  2. "geo"というプロパティの値は、geoValueという型である。
  3. geoValueという型は、"latitude", "longitude" という名前のプロパティを持つ。
  4. "latitude"というプロパティの値は、number型である。
  5. "longitude"というプロパティの値は、number型である。

次は、geo型データのインスタンスです。

{
  "geo" : {
    "latitude" : 37.386013,
    "longitude" : -122.082932
  }
}

そして次は、geoValue型データのインスタンスとなります。

{
  "latitude" : 37.386013,
  "longitude" : -122.082932
}

データ抽出言語とマークアップの正しさ

基本的に、「CSSセレクタによるデータ抽出」で説明したような構文を使いますが、便宜上undefinedというデータ型(シングルトン型)を追加します。undefined型の唯一のインスタンスもundefinedと表記します。undefined型はnull型とはまったく別物です。オブジェクトのプロパティにundefinedが代入されると、そのプロパティは「存在しない」として扱われます。string? のように、お尻に疑問符が付いた型は、(string | undefined) の意味です。(今回の話ではundefinedは使ってませんけどね。)

さて、次のような表現を考えます。

{
  "geo" : {
    "latitude" : number(content(".geo .latitude")),
    "longitude" : number(content(".geo .longitude"))
  }
}

この表現(式、expression)を、先のHTML断片に対応するDOMツリーに対して評価するとgeoデータ(JSONオブジェクト)が得られるはずです。

順番に説明します。対象とするデータは、次のHTML断片に対応するDOMツリーです。

<div class="geo">GEO: 
 <span class="latitude">37.386013</span>, 
 <span class="longitude">-122.082932</span>
</div>

CSSセレクター ".geo .latitude" は、要素 <span class="latitude">37.386013</span> を抽出します。その要素内容は文字列 "37.386013" です。文字列を数値に変換すれば、37.386013。まとめて言えば、当該のDOMツリーに対して、number(content(".geo .latitude")) を評価した結果は 37.386013 です。同様に、number(content(".geo .longitude")) は -122.082932 となります。

よって、データ抽出結果から得られるJSONオブジェクトは:

{
  "geo" : {
    "latitude" : 37.386013,
    "longitude" : -122.082932
  }
}

このJSONオブジェクトを、geo型のスキーマに対して検証すれば妥当となります。これは、もとのgeoマークアップが正しかったことを意味します。「正しさ」は次の2段階で定義されます。

  1. データ抽出言語の式に基づき、データ抽出が成功する。
  2. 得られたデータ(JSONオブジェクト)がスキーマに対して妥当である。

複数のマークアップパターン

geoフォーマットのもうひとつのマークアップパターンも見てみます。データ抽出の定義は次のとおり。

{
  "geo" : {
    "latitude" : number(attrVal("abbr.geo", "title").split(';')[0]),
    "longitude" : number(attrVal("abbr.geo", "title").split(';')[1]) 
  }
}

「.split(';')[0]」の部分は、関数記法が面倒でメソッド記法を使ってしまいました(苦笑)。文字列をセミコロンを区切りとして分割して、その最初の部分を取り出しています。「.split(';')[1]」も同様です。次のようなマークアップから、正しくgeo型データが構成されることを確認してください。

<p>位置は 
  <abbr class="geo" title="37.386013; -122.082932">このあたり</abbr>
  です。
</p>

フォーマットを形式的に定義するには

マイクロフォーマットの意味でのフォーマットを形式的に定義する手順は次のとおりです; まず、JSONスキーマによりデータ型を定義します。これで、マークアップからは独立したデータ構造が定義できます。次に、CSSセレクタやデータ型変換関数などを組み合わせてデータ抽出の定義を行います。1つのフォーマットにたいして複数のマークアップパターンがあるなら、それぞれのマークアップパターンごとにデータ抽出の定義を作ります。

このような定義があれば、与えられたマークアップが当該フォーマットに適合(conform)しているのか、得られたデータが妥当(valid)なのかを正確に判断することができます。また、プログラミング言語による実装に落とすことも容易です。

カジュアルな記述は良いと思いますが、補助的に形式的な記述もないと辛いなー、と感じましたね。まーともかく、geo以外のフォーマットに対しても形式的な記述を与えるつもりです(既にだいたいはやってみた)。