-
Notifications
You must be signed in to change notification settings - Fork 16
3. Embedded records
The real strength of ejson
is that it converts Erlang terms recursively applying rules, so it can handle embedded records, list of records, embedded records of records and so on. Let us see how.
ejson
can handle embedded records. We have two possibilities: we can specify the type of the record (the record name) or we don't want to (cannot) do that. In the first case when we convert a typed record to JSON (and parse that back), it is obvious what kind of record we expect (it is in the rule). When the type is omitted we can convert any record to JSON objects, so it can be regarded as a general embedded record rule. But during parsing (decoding JSON to records) we don't know what kind of record we need to create from a JSON object (it is not specified in the rule). So here we need something extra and that is why __rec
meta-type field is created. In case of untyped record a __rec
field is generated into the JSON object, so during decoding the ejson
engine will know what kind of record it needs to create. Let us an example.
-json({http_url, {string, url}}).
-json({http_basic, {string, url}, {string, user}, {string, password}}).
-json({http_request, {atom, method}, {record, url}).
to_json({http_request, 'GET', {http_basic, "/index.html", "scott", "tiger"}}).
We want to pass different urls to the http request, so we created an untyped record. We can pass either an http_url
record or an http_basic
for handling basic authentication. But since we want to decode the JSON back to records we need to know what kind of record we passed in the url
field. So a __rec
field will be generated.
{
"method": "GET",
"url": {
"__rec": "http_basic",
"url": "/index.html",
"user": "scott",
"password": "tiger"
}
}
If we know what kind of record we want to pass in the url we can specify so. In that case there won't be __rec
field generated, since the target types are ambiguous.
-json({http_url, {string, protocol}, {string, host}, {number, port},
{string, path}, {string, query_string}}).
-json({http_request, {atom, method}, {record, url, [{type, http_url}]}).
to_json({http_request, 'GET',
{http_url, "http", "localhost", 8080,
"/index.html", "?q=poker"}}).
In the {record, url, [{type, http_url}]}
we can specify field options as a list. In that list we says that the expected type is the http_url
record, so during decoding ejson
will know that an http_url
record should be constructed and it doesn't generate __rec
field.
{
"method": "GET",
"url": {
"protocol": "http",
"host": "localhost",
"port": 8080,
"path": "/index.html",
"queryString": "?q=poker"
}
}
If you understood the concept of typed and untyped record you will easily understand the list of typed and list of untyped records. The idea is the same: when we want to restrict the elements in the list, we can specify the type of the list elements.
-json({header, {binary, name}, {binary, value}}).
-json({http_request, {string, url}, {list, headers, [{type, header}]}}).
to_json({http_request, "http://example.com",
[{header, <<"Content-Type">>, <<"text/html">>},
{header, <<"Cookie">>, <<"secret">>}]}).
So it generates a typed list (without the meta-type field, right?).
{
"url": "http://example.com",
"headers": [
{
"name": "Content-Type",
"value": "text/html"
},
{
"name": "Cookie",
"value": "secret"
}
]
}
It is useful when we don't know the types of the list elements and we are not bothered by the generated __rec
field. To create such a rule we only need to omit the type
field option from the list rule. So the rule {list, headers, [{type, header}]}
from the example above will look like {list, headers}
. In that case the objects in the JSON list will have __rec
fields generated.