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

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

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

参照用 記事

最近のCatyスキーマを解説します

Catyのスキーマ記述言語は、JSONスキーマ仕様(http://tools.ietf.org/html/draft-zyp-json-schema-01)に基づいています。構文は違いますが、できる限り本家JSONスキーマのセマンティクスを保存するように努めています。ですから、JSONスキーマの書き方がなんとなくでも理解できた人なら、Catyスキーマも書けます。そして、Catyスキーマのほうがはるかに簡潔に記述できます。

以下では、「最近のJSONスキーマを解説します」と同じ構成で、Catyスキーマの記述パターンを説明します。Catyスキーマがいかに単純明快かが分かると思います*1

内容:

  1. 事例
  2. 型の分類
  3. Catyスキーマの記述パターン
    1. 基本スカラー
    2. リスト型
    3. タプル型
    4. タプル+リスト型
    5. 閉じたオブジェクト型
    6. 開いたオブジェクト型
    7. ユニオン型
    8. 列挙型
    9. 全称型
    10. オプショナル指定
    11. リテラルとシングルトン型
    12. スキーマ属性

事例

「しょうがないので、マイクロフォーマットのデータモデルを僕が考えた」で例題とした、マイクロフォーマットのhCardに適合した簡単な人物情報の例を挙げましょう。インスタンスは次のようなものです。


{
"vcard" : {
"fn" : "檜山 正幸",
"email" : ["hiyama{AT}chimaira{DOT}org"],
"url" : ["http://www.chimaira.org/", "http://d.hatena.ne.jp/m-hiyama/"]
}
}

次の約束をします。

  1. fnプロパティは必須
  2. emailプロパティは最低1つの項目を持つ配列
  3. urlプロパティは任意個数の項目を持つ配列、空でもよい

メールアドレスとURLは、本来なら専用のデータ型を定義すべきでしょうが、文字列で済ませます。

Catyスキーマの型表現は、インスタンスとほとんど同じ形になります。次のとおりです。


{
"vcard" : {
"fn" : string,
"email" : [string, string*],
"url" : [string*]
}
}

JSONスキーマなら以下のようになります。


{
"type" : "object",
"properties" : {
"vcard" : {
"type" : "object"
"properties" : {
"fn" : {"type", "strning"},
"email" : {
"type" : "array",
"items" : [
{"type": "string"}
],
"additionalProperties" : {"type": "string"}
},
"url" : {
"type" : "array",
"items" : {"type": "string"}
}
}
}
}
}

本家JSONスキーマに不公平にならないように、JSONスキーマのメリットも言っておきます。(「最近のJSONスキーマを解説します」より引用):

JSONスキーマの特徴は、型表現がまたJSONデータであることです。これはメタデータをデータとして扱えるという大きな利点があります。



Catyスキーマの型表現自体はJSONデータではありません。JSONデータとすごく似てますが、実は違います。よって、JSONデータを扱うツールでCatyスキーマを扱うことはできません。例えば、JSONデータをコンテキストとするテンプレートエンジンへの入力としてCatyスキーマは使えません。JSONスキーマならそれができます。

型の分類

「最近のJSONスキーマを解説します」にある分類とまったく同じ内容ですが再掲します。

  1. 基本スカラー型: integer, number, string, boolean, null
  2. リスト型: 項目がすべて同じ型である配列型、項目の個数は任意
  3. タプル型: 決まった個数の項目を持つ配列型
  4. タプル+リスト型: 決まった個数の項目と、それに続く同じ型の任意個の項目を持つ配列型
  5. 閉じたオブジェクト型: 決まった個数のプロパティを持つオブジェクト型
  6. 開いたオブジェクト型: 決まった個数のプロパティと、同じ型の任意個のプロパティを持つオブジェクト型
  7. ユニオン型: 複数の型のどれかを意味する型
  8. 列挙型: 有限個の定数リテラルのどれかを意味する型
  9. 全称型: any

Catyスキーマの記述パターン

型の分類ごとに、どのようにスキーマを書くかを説明します。

基本スカラー

型の名前そのものを書きます。


<型の名前>

例えば、integer とか。

基本スカラー型の名前は次の5つで全部です。nullはスカラーとは別な「特殊な型」として扱うこともあります*2

  1. integer
  2. number
  3. string
  4. boolean
  5. null

リスト型

リストの項目の型表現を、<項目の型表現>と書くとします。リスト型は次のとおりです。


[<項目の型表現>*]

星印は正規表現の「任意回の繰り返し」の記号です。例えば整数値のリストは [integer*] です。空リストも含まれます。

タプル型

タプルの項目ごとの型を表現する型表現を、<項目の型表現1>、<項目の型表現2>などと書くとします。タプル型は次のとおりです。


[<項目の型表現1>, <項目の型表現2>, ..., <項目の型表現n>]

例えば、[string, string, integer] とか。

タプル+リスト型

タプルの項目の型を表現する型表現を、<項目の型表現1>、<項目の型表現2>など、残りの項目の型は<残余項目の型表現>と書くとします。タプル+リスト型は次のとおりです。


[<項目の型表現1>, <項目の型表現2>, ..., <項目の型表現n>, <残余項目の型表現>*]

例えば、最初が真偽値で、後は文字列が並ぶような配列なら、[boolean, string*] です。

閉じたオブジェクト型

オブジェクトのプロパティ名を<プロパティ名1>、<プロパティ名2>など、プロパティ値の型を表現する型表現を、<プロパティの型表現1>、<プロパティの型表現2>などと書くとします。閉じたオブジェクト型は次のとおりです。


{
"プロパティ名1" : <プロパティの型表現1>,
"プロパティ名2" : <プロパティの型表現2>,
...
"プロパティ名n" : <プロパティの型表現n>
}

開いたオブジェクト型

オブジェクトのプロパティ名を<プロパティ名1>、<プロパティ名2>など、プロパティ値の型を表現する型表現を、<プロパティの型表現1>、<プロパティの型表現2>など、残りのプロパティの型は<残余プロパティの型表現>と書くとします。開いたオブジェクト型は次のとおりです。


{
"プロパティ名1" : <プロパティの型表現1>,
"プロパティ名2" : <プロパティの型表現2>,
...
"プロパティ名n" : <プロパティの型表現n>,
* : <残余プロパティの型表現>?
}

最後の<残余プロパティの型表現>には疑問符を付けるのが正統的記法ですが、「しばしば忘れる」という人間的な理由で疑問符の省略を許します。

ユニオン型

ユニオン型を構成する型の表現を <型表現1>、<型表現2>などとします。ユニオン型は次のとおりです。


(<型表現1> | <型表現2> | ... | <型表現n>)

縦棒で区切って型表現を並べます。外側を囲んでいる丸括弧は省略することもあります。Catyでは、成分となる各型(alternatives)が排他的であることを要求します。

列挙型

列挙型は次のとおりです。


(<リテラル1> | <リテラル2> | ... |<リテラルn>)

縦棒で区切って定数リテラルを並べます。外側を囲んでいる丸括弧は省略することもあります。一番短い表現になるように、各リテラルは違う値になるように書く必要があります。

全称型

any です。

オプショナル指定

プロパティや項目がオプショナルであることを表すには、次のように書きます。


<型表現>?

お尻に疑問符を付けます。正規表現と同じです。

リテラルとシングルトン型

すべてのJSONリテラルは、その値だけを持つシングルトンセットである型を表します。型表現とリテラルを自由に混ぜることができます。例えば、{"family-name" : "檜山", "given-name" : string(minLength=1)} とか、(integer | "infinity" | null) とか。

スキーマ属性

型ごとに有効なスキーマ属性は、型表現に続けて (<属性名1>=<属性値1>, <属性名2>=<属性値2>, ...) の形で書きます。integer(minimum=0, maximum=5)、[string*](maxItems=10) とか。属性の名前と値は、本家JSONスキーマに従いますが、一部の属性(requires, default, readonlyなど)はサポートしません -- 型システムの整合性を壊してしまう可能性があるからです。型システムと相性が悪いスキーマ属性はアノテーションとして記述するかもしれません。

*1:諸般の都合により、Catyの公開リリースが滞っています。新スキーマ構文を解釈できる次期バージョンまで、もうしばらくお待ちください。

*2:JSONのnullは実体的な値で、非存在とかヌルポインターではないことには注意してください。イミュータブルなシングルトンオブジェクトと考えるのが適切です。