Skip to content
This repository has been archived by the owner on Jan 12, 2023. It is now read-only.

3. Embedded records

Richard Jonas edited this page Feb 12, 2016 · 2 revisions

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.

Record type

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.

Untyped record

-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"
    }
}

Typed record

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"
    }
}

List of records

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.

List of typed records

-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"
        }
    ]
}

List of untyped records

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.