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

1. Welcome to ejson

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

Introduction

ejson is a JSON encoding/decoding library for Erlang. It gives a declarative interface for jsx by which we need to specify conversion rules and ejson will convert tuples according to the rules.

Using ejson one should define rules which gives a mapping for each field of a record describing how to convert the field possible values to JSON. ejson is intended to create such a conversion system with which one can easily decode JSONs back to Erlang records. So at first the rules can seem to be complicated but there reason is that we can convert and parse (encode and decode) terms and JSONs.

Simple example

-module(people).
%% You can specify in rebar.config, erlang.mk, too.
-compile({parse_transform, ejson_trans}).

%% Record definitions are not needed for ejson but you.
-record(person, {name, birth_year, projects}).
-record(project, {name, budget, successful}).

-json({person, {string, "name"}, {number, "yearOfBirth"},
               {list, "projects", [{type, project}]}]}).
-json({project, {string, "name"}, {number, "budget"},
                {boolean, "isSuccessful"}]}).

%% parse_transform generates to_json/1 and from_json/{1,2} local functions

start() ->
    Jim = #person{
        name = "Jim",
        birth_year = 1967,
        projects = [#project{name = "Authoring",
                             budget = 100000
                             successful = true}]},
    {ok, Json} = to_json(Jim),
    io:format("~s~n", [Json]),
    {ok, Person} = from_json(Json, person).  %% Specify the the expected type here

The person record results in a JSON like this:

{
    "name": "Jim",
    "yearOfBirth": 1967,
    "projects": [
       {
           "name": "Authoring",
           "budget": 100000,
           "isSuccessful": true
       }
    ]
}

How does ejson work?

ejson (by the help of ejson_trans parse transform) reads compiled modules which are compiled with that parse transform. It collects all -json(...) wild attributes to get the conversion rules for different record types. It also defines 3 functions in the module to be compiled (if they are not defined already).

to_json(Record) -> {ok, JSON::binary()} | {error, Reason::term()}.
from_json(Json) -> {ok, Record::term()} | {error, Reason::term()}.
from_json(Json, ExpectedType::atom()) -> {ok, Record::term()} | {error, Reason::term()}.

Those functions are for converting records to JSON and parsing JSON to records. Those functions are not exported since they are intended to be used in the current module as private function. To avoid compiler warnings the parse transform puts nowarn_unused_function compile attributes into the module in order that compiler doesn't complain for those "invisible" functions when they are not used.

to_json/1 converts records to json binaries, from_json/2 parse json binary and apply the rules to extract the record specified in the second parameter. In ejson we can convert records in a way that the JSON will contain a meta-type field (__rec field) which stores from what record the JSON has been converted. So in that way we can use from_json/1 to decode records back. The only disadvantage of that method is that JSONs will contain a __rec field, so picky applications may not accept those JSONs (or remove that field when sending JSONs back).