JSONハイパースキーマという仕様があります。
この仕様の動機や目的に、僕は強い共感をおぼえます。欲しかったんですよ! こういう機能。
だけど、JSONハイパースキーマ仕様はダメです。どこがどうダメかを解説するのも建設的でないのでしません。このままでは、ちょっと使えそうにありません >JSONハイパースキーマ仕様。そこで、できるだけもとの仕様を尊重・保存しながら僕の目的に適合するように変更します。
JSONハイパースキーマ仕様では、型システムとして定式化すべき部分と、型システムとは何の関係もない部分がゴッチャになっています。ここでは、型システムに吸収できそうな機能だけを取り上げます。
内容:
ハイパースキーマの目的
JSONハイパースキーマ仕様は、JSONデータ(主にJSONオブジェクトデータ)をハイパーメディアとみなすための仕様です。JSONオブジェクトをハイパーメディアとみなすためには、次のことが必要です。
- 与えられたJSONオブジェクトの、どのプロパティがハイパーリンクであるかを識別する。
- それぞれのハイパーリンクが、いかなる関係を表しているかを識別する
- ハイパーリンクのURIが、どのような法則に従うべきか/従っているかを知る。
例えば、次のようなJSONオブジェクトを考えます。
{ "id" : "m-hiyama", "top" : "http://d.hatena.ne.jp/", "sites" : ["http://www.chimaira.org/", "http://d.hatena.ne.jp/m-hiyama/"], "description" : "キマイラのオヤジ・檜山" }
人間がこれを見れば、次のことは容易に想像がつきます。
- idプロパティの値は一意識別子だろう。
- topプロパティの値はURLだから、ハイパーリンクだ。
- sitesプロパティの値はURLのリストだから、ハイパーリンクの集まりだ。
- descriptionプロパティの値はハイパーリンクではない。
しかし、機械(ソフトウェア)はそんな判断ができません。機械可読にしたいなら、それにふさわしい方法でリンクに関する情報(メタ情報)を与えなくてはなりません。JSONハイパースキーマはそのようなメタ情報を記述する方法 -- つまりハイパースキーマを定義します。
ここで採用する方式の概要
与えられたJSONオブジェクトの、どのプロパティがハイパーリンクであるかを識別する
これに関しては、型システムを使います。ハイパーリンクを表現するデータ(アンカー)のリテラル表現を定義して、「ハイパーリンクを表すデータ」という型を設けます。
それぞれのハイパーリンクが、いかなる関係を表しているかを識別する
ここでいう「関係」を型システム内で記述するかどうかは微妙で、異論もあるでしょうが、スキーマ属性を使うことにします。「ハイパーリンクを表すデータ」が「関係」という属性で制限される(あるいは修飾される)と考えます。
関係を表す名前は、HTMLのlink要素やa要素の"rel属性”で使っているものを採用します。stylesheet, next, start, license, canonical などが関係を表す名前の例です。
ハイパーリンクのURIが、どのような法則に従うべきか/従っているかを知る
JSONハイパースキーマ仕様では、URIテンプレート(http://bitworking.org/projects/URI-Templates/spec/draft-gregorio-uritemplate-03.html)を使って、ハイパーリンクURIのパターンを記述する方法を採用しています。その意義や必要性がハッキリしないし、この方法がいつでもうまくいくとは思えないので、今回この点は触れません。(いつか触れるかも。)
URIが指している先のリソースの型を指定する
この点は、JSONハイパースキーマ仕様ではまったく言及されてません。HTMLのa要素では、type属性がこの目的に使われます(単なるヒントですが)。型システムとしては、参照先の型は重要なことなので、明示的に指定できるようにします。
ハイパーリンクのリテラル表現
JSON利用者のあいだでは、{"$ref" : <URI文字列>} という表記でリンクを表す習慣がありました。しかし、JSONハイパースキーマでは、このような固定的な書き方は決めないで、スキーマ側でリンクの書き方を自由に設定できるようにしています。僕はこのアプローチはまったく間違っていると思います。HTMLのハイパーリンクが使いやすいのは、"a"というタグ名、"href"という属性名が固定されているからです。インスタンスごとに違ったルールでハイパーリンクを記述されてはたまりません。
そこで、{"$ref" : <URI文字列>} という習慣的フォーマットはそのまま採用します。HTMLで表現するなら:
<a href="<URI文字列>"><URI文字列></a>
リンクが表す関係もエンコードしたいので、オプショナルなプロパティ $rel も導入します。{"$ref" : <URI文字列>, "$rel" : <関係の名前>} と書きます。HTMLで表現するなら例えば:
<a href="<URI文字列>" rel="<関係の名前>"><関係の名前> : <URI文字列></a>
この約束で先の例を書き直すと次のようになります。
{ "id" : {"$id" : "m-hiyama"}, "top" : {"$ref" : "http://d.hatena.ne.jp/", "$rel" : "top"} "sites" : [ {"$ref" : "http://www.chimaira.org/"}, {"$ref" : "http://d.hatena.ne.jp/m-hiyama/"} ], "description" : "キマイラのオヤジ・檜山" }
$idなんてのも入れましたが、想像はつくでしょう(これ以上は触れません)。マイクロデータ方式でHTMLにしてみると例えば次のようです。
<dl itemscope itemtype="http://www.chimaira.org/vocab/profilelinks/"> <dt>id</dt> <dd itemprop="id">m-hiyama</dd> <dt>top</dt> <dd> <a itemprop="top" href="http://d.hatena.ne.jp/" rel="top">top : http://d.hatena.ne.jp/</a> </dd> <dt>sites</dt> <dd> <ul> <li> <a itemprop="sites" href="http://www.chimaira.org/">http://www.chimaira.org/</a> </li> <li> <a itemprop="sites" href="http://d.hatena.ne.jp/m-hiyama/">http://d.hatena.ne.jp/m-hiyama</a> </li> </ul> </dd> <dt>description</dt> <dd itemprop="description">キマイラのオヤジ・檜山</dd> </dl>
総称を使った参照型
プログラミング言語では、「他の場所にあるデータを指し示すデータ」という概念はよく出てきます。例えば、C言語だとポインターになります。
int i = 0; int * p = &i;
この例で、変数iには整数値が格納されますが、変数pには整数値を格納した他の場所へのポインターが格納されます。「int *」を「型パラメータとしてintを持った総称型」だと考えると、pointer<int> と書いてもいいでしょう。
単に語感の問題から、pointerではなくてreferenceを使うことにします(ポインターと参照の違いを問題にしているわけじゃありません)。
次の例を考えます。
{ "fn" : "檜山正幸", "url" : {"$ref" : "http://www.chimaira.org/"}, "adr" : {"$ref" : "hiyama.adr.json"} }
このインスタンスのスキーマは例えば次のようになるでしょう。
{ "fn" : string(minLength=1), "url" : reference<WebSite>?, "adr" : reference<hCardAdr>? }
reference<WebSite>型はWebサイトを指す参照型、reference<hCardAdr>型はhCardの住所記述データを指す参照型です。WebSiteって型をどうやって定義するかわかりませんが、hCardAdr(hCardの住所記述)ならハッキリとスキーマを書けます。ちなみに、reference<any>なら、なんでもいいから何かを指す参照型です。
プロパティの型として参照型を書けば、次の2つの課題は解決します。
- 与えられたJSONオブジェクトの、どのプロパティがハイパーリンクであるかを識別する
- URIが指している先のリソースの型を指定する
参照型のスキーマ属性
JSONスキーマのスキーマ属性は、型を修飾するスカラー値です。例えば、配列の最小長さはminItemsで指定できます。スキーマ属性を書くと、型に制約が加わります。ハイパーリンクの関係の指定はスキーマ属性としましょう。次が例です。
{ "fn" : string(minLength=1), "url" : reference<WebSite>?, "adr" : reference<hCardAdr>?, "detail" : reference<hCard>(rel="detail")? }
これは、「detailというプロパティはhCard型データへのリンクで、当該リソースと参照先リソースの関係は"detail"だ」と宣言しています。
言い残したこと
これで、ハイパーリンク(=参照)の「リテラル構文」と「型としての解釈」が定まりました。ただし、解釈は直感的なもので実装にはもっと精密な定義が必要です。ハイパーリンクを含む型に対して、JSONインスタンスが妥当であることを実効的に判断出来なくてはなりません。ここらへんはまたの機会に。