13.4 JSONと部分動的レコード
JSONは,整数や文字列などの基本データ型, データをラベルに対応づけるオブジェクト型,および 任意のデータを並べた配列型からなる データ構造です. これらのデータ構造はそれぞれ,MLの基本型, レコード型,リスト型に対応します.
しかし,MLのそれらとは異なり,JSONには型の制約がありません. JSONオブジェクトでは, 同じラベルに同じ型のデータが入っているとは限りませんし, そもそもそのラベルが常にあるとも限りません. また,JSONの配列は,全ての要素の構造が一致する必要はありません. 実際にJSONが利用される局面においても,このようなヘテロジニアスな データが頻繁に現れます. そのため,JSONにMLの型をそのまま付けることは難しく, またMLの型が付くものに限定することはJSONの実態に合っていません.
SML#では,「動的部分レコード」と呼ばれる考え方に 基づき,SML#プログラム中でJSONデータに型を与えます. 動的部分レコードとは, その構造の一部のみが静的に分かっていて, それ以外の構造は実行時チェックをしなければ分からないような レコード構造のことです. SML#では,実行時に得られたJSONデータを動的部分レコード として取り扱います.
SML#での型付きJSON操作を,例を通じて見てみましょう. SML#では,初めて読み込まれたJSONデータは全て Dynamic.void Dynamic.dyn型を持ちます. このDynamic.voidは,データ構造が静的には一切分かっていないことを 表します. 例えば,
{"name" : "Sendai", "wind" : {"speed" : 7.6, "deg" : 170.0}}
というJSONは,まずDynamic.void Dynamic.dyn型の値として読み込まれます.
このJSONを受け取るプログラムは, 受け取ったJSONは少なくとも文字列型のnameフィールドを持つことを 期待していて(持っていなければ実行時例外とする),そのフィールドの値を 取り出したいとします. そのために, プログラムは受け取ったJSONデータに対して動的型検査を行い, 文字列を持つnameフィールドが存在することを確認します. この検査が成功すれば,そのJSONは 少なくとも文字列型のnameフィールドがあることが確認されているはずです. この検査の結果に対し,SML#は, このことが確認されたことを表す型 {name : string} Dynamic.dynを静的に与えます.
部分的に型が判明しているJSONから値を取り出すには, 以下の関数を用いて,その静的なビューを取り出します.
Dynamic.view : [’a#reify. ’a Dynamic.dyn -> ’a]
この関数は,動的部分レコードであるJSONデータのうち, 静的に判明している構造のみを,MLのデータ構造に変換する関数です. この関数を用いることで, {name : string} Dynamic.dyn型のJSONデータから, {name : string}型のレコードを取り出すことができます.
JSONとして受け取ったデータは,もはやMLのデータに変換されました. これ以降の操作は,MLの言語機能を自由に組み合わせて 型安全に書くことができます.