JSONスキーマはデータ構造の定義言語ですから、本来ユーザーインターフェースとは何の関係もないはずです。ですが、仕様(http://www.json-schema.org/)には、optionsというスキーマ属性があります。
optionsスキーマ属性はインスタンスの妥当性検証には影響しません(The "options" attribute does not affect validation)。ユーザーインターフェースへのヒントとなります(The "options" attribute can be used for user interfaces in conjunction with "enum" to define the labels for the possible values)。enumスキーマ属性と一緒に使うことが多いでしょうが、enumを常に要求するわけではありません。
次は、仕様書に載っているoptionsスキーマ属性の使用例です(原文にはtypoがあったので修正しました)。
{ "type" : "integer", "enum" : [1, 2, 3], "options" : [ {"value": 1, "label": "Small"}, {"value": 2, "label": "Medium"}, {"value": 3, "label": "Large"} ] }
僕がでっちあげた代替構文(私家版スキーマ言語)で書くとこんなふうです。
SMLSelection = integer( enum = [1, 2, 3], options = [ (value = 1, label = "Small"), (value = 2, label = "Medium"), (value = 3, label = "Large") ] );
この情報から、例えば次のようなHTMLフォームを生成できます。
<select name="sml"> <option value="1">Small</option> <option value="2">Medium</option> <option value="3">Large</option> </select>
この場合、個々の値にラベルを付けることによって、ユーザーインターフェースに対してヒントを与えています。
しかし、ラベルは値にだけではなく、オブジェクト・プロパティや配列項目に対しても必要になります。次の例を見てください。
BasicUserInfo = object { "name" : string, "gender" : integer(enum = [1, 2], options = [ (value = 1, label = "男性"), (value = 2, label = "女性") ]) };
これから生成できるユーザーインターフェースは次のようなものでしょう。
<div class="formField"> Name: <input type="text" name="name" /> </div> <div class="formField"> Gender: <select name="gender"> <option value="1">男性</option> <option value="2">女性</option> </select> </div>
ラベルには、オブジェクトのプロパティ名 name, gender をそのまま使ってますが、ここは日本語を使いたいところです。すべての型に対して、スキーマ属性labelを許すとすれば、ユーザーインターフェースの生成に役に立ちます。
BasicUserInfo = object(label = "お客様基本情報") { "name" : string(label = "お名前"), "gender" : integer(label = "性別", enum = [1, 2], options = [ (value = 1, label = "男性"), (value = 2, label = "女性") ]) };
labelで指定された情報を使えば、すべて日本語のユーザーインターフェースを構成できます。
<fieldset><legend>お客様基本情報<legend> <div class="formField"> お名前: <input type="text" name="name" /> </div> <div class="formField"> 性別: <select name="gender"> <option value="1">男性</option> <option value="2">女性</option> </select> </div> </fieldset>
これでも、スキーマ定義だけではやっぱり情報として不足です。例えば、ユーザーインターフェース生成時には次のような判断も必要です。
- enumによる列挙値を、radioボタングループにするか、selectメニューにするか。
- stringを、テキストフィールドにするかテキストエリアにするか。
- 初期値をどうするか(デフォルト値と初期値が同じ概念とは限らない)。
これらの判断もスキーマ属性として記述することはできますが、やりすぎると「メンドクセー、直接ユーザーインターフェースを書いたほうがいいや」なんてことになりかねません。バランスが大事
さて、少し実用的な例として、ランチの注文フォームのスキーマを書いてみました。これに対応するHTMLフォームも手動で生成してみました(リンクをたどって見ることができます)。
生成作業を手動から自動にはできそうだと思うのですが、その前に、問題があります。下の私家版スキーマ言語によるスキーマ定義は、オリジナルのJSONスキーマに比べれば随分と見やすいと思います。しかしそれでも、ウゲーッって感じがしますよね。このウゲーッ感をなんとかしないと使いものになりませんね。
// // 注意: // スキーマ属性 label, unique, unordered は、 // オリジナルJSONスキーマにはありません。 // LunchOrder = object(label = "ランチご注文") { "ident" : object(label = "ランチ会員情報") { "name" : string(label = "お名前"), "memberNum" : integer(label = "会員番号") }, "order" : object(label = "本日のご注文") { "mainDish" : string(label = "メイン", enum = ["special", "fish", "meat"], options = [ (value = "special", label = "日替わり"), (value = "fish", label = "魚"), (value = "meat", label = "肉") ] ), "drink" : string(label = "お飲み物", enum = ["teaHot", "teaIce", "coffeeHot", "coffeeIce"], options = [ (value = "teaHot", label = "紅茶 (ホット)"), (value = "teaIce", label = "紅茶 (アイス)"), (value = "coffeeHot", label = "コーヒー (ホット)"), (value = "coffeeIce", label = "コーヒー (アイス)") ] ), "dessert" : array(label = "デザート", unique = true, unordered = true) [ string( enum = ["pudding", "strawberryShortcake", "bavarianCream", "gateauChocolat"], options = [ (value = "pudding", label = "プリン"), (value = "strawberryShortcake", label = "苺ショートケーキ"), (value = "bavarianCream", label = "ババロア"), (value = "gateauChocolat", label = "ガトーショコラ") ] ) * ] } // 本日のご注文 } // ランチご注文 ;