Ballerina, a programming language primarily targeting network-distributed applications, just hit 1.0!
Among the numerous features that make Ballerina attractive, ranked high up there are arguably its native support for JSON and its type-safety. Any standard JSON value has a natural representation in Ballerina, which makes it quite straightforward to work with data in the JSON format.
Ballerina has a built-in
json type which represents any all valid JSON values.
Ballerina’s representation of the basic JSON data types are as follows:
- Any number can be represented in Ballerina as an
- Similarly any boolean value would be represented by the
- Any string would be represented by the
()(nil) type represents the absence of any other value. In a JSON context, Ballerina’s
()type also represents
- A JSON array is naturally a Ballerina array, where the element type is a JSON compatible type. Basically the
jsontype represents all possible JSON arrays.
- A JSON object, which is basically a collection of key-value pairs, is represented by a map in Ballerina, where the constraint of the map is a JSON compatible type. The
map<json>type represents all possible JSON objects.
json type is a union type in Ballerina, whose value space is defined as the union of the value spaces of
() | boolean | int | float | decimal | string | json | map<json>.
Lax Static Typing
Ballerina is a statically typed language.
Ballerina also has the concept of lax static typing where static typing rules are less strict, effectively moving some of the type-checking from compile time to runtime. This is allowed only with types that are defined to be lax. With Ballerina 1.0 this is basically
T is lax.
For example, field access (accessing a member via the
. operator — e.g.,
foo.name) is generally allowed only on object/record values where it is guaranteed that the value has a field by that name. Similarly optional field access (via the
?. operator) is generally allowed only on record values where it is possible that the value could have a field by the name (i.e., a required field or an optional field in the type-descriptor).
The only exception to this is when it comes to lax types. Field access and optional field access are also allowed on lax types, but the type of such an expression would also allow for error scenarios.
Field Access on Lax Types
Field access is allowed on lax types. For example it is allowed on a variable (say
j) of type
json. At runtime one of the following could happen when attempting to access a field named
jis a JSON object (i.e.,
jhas a key-value pair
name. Here the resultant value of
vwhich would be
jis a JSON object, but
jdoes not have a key-value pair where
nameis the key. In such a scenario, accessing a field named
namewould fail, and the resultant value of
j.namewould be an error indicating that
jdoes not have an entry with the particular key.
jmay not be a JSON object (given that the
jsontype is a union type representing all possible JSON values including numbers, strings, booleans, arrays and objects). In such a scenario, accessing a field is not possible, and the resultant value of
j.namewould again be an error indicating that
jis not a JSON object.
Thus the effective type of the expression
j.name would be the union of the type of the value if present (
error. This approach leaves pretty much no room for errors, and ensures all possible error scenarios are handled.
Optional Field Access on Lax Types
Optional field access on lax types also has similar behaviour, with the only exception being the result on a missing key.
While field access returns an error if a JSON object does not have a field by the specified key, optional field access return
() (nil) in such a scenario.
Thus the effective type of the expression
j is lax, would be the union of the type of the value if present,
() (nil), and
Accessing fields of JSON values is something that underwent significant changes from the previous release of Ballerina, and naturally a lot of people have had a lot of questions regarding this change.
If you haven’t used a pre-1.0 version of Ballerina, do feel free to skip to the end! :)
With pre-1.0 versions, accessing a field on a
json-typed variable did not return an error. Instead, in any of the error scenarios (i.e., not a JSON object, missing key, etc.) nil (
()) was returned as the value.
This had obvious drawbacks and was error-prone since it was not quite straightforward to figure out why accessing a field returned
() — was it because the value had a field by the specified key and the value was nil, or was it because the value did not have a field by the particular key, or was it because it was not even a JSON object?!
While the changes and formalization to JSON access may seem cumbersome due to additional type tests/casts, this also leaves little to no room for error, and forces a user to handle any and all possible scenarios which IMO makes it worthwhile. :)
This is simply a basic introduction to Ballerina’s JSON type and lax static typing. Ballerina additionally provides a number of operations facilitating working with JSON data.
- Check out the JSON Ballerina by Examples under the JSON/XML section for examples of the language level support to work with JSON.
- Check out the standard library API docs for conversion from XML/table to JSON and from JSON to XML.