13.4 JSON as a partially dynamic record
JSON is a data structure consisting of the following: basic types such as integers and strings; object types, which associate values with labels; and array types which represent data sequences. These structures would corresponds to basic types, record types, and list types of ML.
However, JSON and ML are inherently different since JSON is untyped and heterogeneous. In JSON, objects received from the same source do not always have the same type of values in a same label. Even the existence of a label is not guaranteed. In addition, all elements in a JSON array does not always have same type. In practice of JSON handling, we often meet such heterogeneous data. This means the two facts: it is difficult to give ML types to JSON data, and it is useless to limit JSON data in those that have ML types.
SML# deals with such heterogeneous JSON data in a type-safe way based on the idea of “partially dynamic records.” A partially dynamic record is a dynamically typed records, but some parts of it is statically known. In SML#, every JSON data is regarded as a partially dynamic record.
Let us look into JSON handling in SML# through an example.
When a JSON is read, it initially has Dynamic.void Dynamic.dyn type. This type means that it is a JSON whose structure is not statically known at all. For example, suppose that we intend to read the following JSON:
{"name" : "Sendai", "wind" : {"speed" : 7.6, "deg" : 170.0}}
Its type is Dynamic.void Dynamic.dyn at first.
Suppose also that we expect the given JSON includes at least name field of string (if it is not found, a runtime exception is raised) and intend to read the value of that field. To do this, we perform a dynamic type check against the JSON to make sure that it has the name field of string type. If this check is passed, the JSON certainly has the name field as we expected. To represent this fact in static typing, SML# gives the result of this check a type {name : string} Dynamic.dyn.
After the dynamic check, we extract its statically known part by using the following function:
Dynamic.view : [’a#reify. ’a Dynamic.dyn -> ’a]
This function allows us to obtain a record of type {name : string} from the JSON of type {name : string} Dynamic.dyn. The meaning of type kind #reify shall be described below.
We successfully convert the given JSON data to an ML record. Succeeding data processing can be carried out by freely combining ML constructs in a type-safe way.