13.6 JSONプログラミング例
以下の対話セッションは,MLのレコードのリストに対応する 構造を持つJSONを読み込む例です.
# val J = "[{n̈ame:̈J̈oe,̈ äge:̈21, g̈rade:̈1.1},"
^ "{n̈ame:̈S̈ue,̈ äge:̈31, g̈rade:̈2.0},"
^ "{n̈ame:̈B̈ob,̈ äge:̈41, g̈rade:̈3.9}]";
val J =
"[{"name":"Joe", "age":21, "grade":1.1},
{"name":"Sue", "age":31, "grade":2.0},
{"name":"Bob", "age":41, "grade":3.9}]" : string
# fun getNames l = map #name l;
val getNames = fn : [’a#{name: ’b}, ’b. ’a list -> ’b list]
# val j = Dynamic.fromJson J;
val j = _ : Dynamic.void Dynamic.dyn
# val vl = _dynamic j as {name:string, age:int, grade:real} list;
val vl =
[
{age = 21,grade = 1.1,name = "Joe"},
{age = 31,grade = 2.0,name = "Sue"},
{age = 41,grade = 3.9,name = "Bob"}
] : {age: int, grade: real, name: string} list
# val nl = getNames vl;
val nl = ["Joe","Sue","Bob"] : string list
より複雑な例を見てみましょう. 以下のようなJSONを考えます.
[
{"name":"Alice", "age":10, "nickname":"Allie"},
{"name":"Dinah", "age":3, "grade":2.0},
{"name":"Puppy", "age":7}
]
このJSON文字列が変数Jに束縛されているとします. これはヘテロジニアスなリストです. このようなリストは,直接MLのリストにキャストすることができません. 一方,このリストを,各要素の共通部分に注目し, 「少なくともnameとageフィールドを持つレコードのリスト」 と見ることはできます. この見方に基づき,以下のようにしてJを読み込みます.
val j = Dynamic.fromJson J
val vl = _dynamic j as {name:string, age:int} Dynamic.dyn list
vlの型は {name:string, age:int} Dynamic.dyn listです. このリストの静的なビューは,
val l = map Dynamic.view vl
として取り出すことができます. さらに,各要素からnameフィールドの値を取り出したければ,
val names = map #name l
とします.
リストの各要素に対して,あるフィールドの有無をチェックしたい 場合は,個々の要素に対して動的検査を行います. 例えば,nicknameがある場合はそれを,そうでなければnameを 取り出したい場合は,以下のように書きます.
fun getFriendlyName x =
_dynamiccase x of
{nickname = y:string, ...} => y
| _ : Dynamic.void Dynamic.dyn => #name (Dynamic.view x)
val friendlyNames = map getFriendlyName vl
このgetFriendlyName関数の型は, [’a#reify#{name : string}. ’a Dynamic.dyn -> string]です. 多相レコード型と動的レコード型が組み合わさっていることに 注意してください. これは,JSONを処理する観点からは,直感的には {name : string} Dynamic.dyn -> stringと同様の意味を持つように 見えますが, これらの厳密な意味は異なります. 後者は, name以外のフィールドの存在は動的型検査しなければ分からないものだけ しか引数に取れないのに対し, 前者はname以外のフィールドの存在が確認されているものも引数に 取ることができます.