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

5. Pre encode and post decode

Richard Jonas edited this page May 17, 2016 · 4 revisions

Converting data during encoding and decoding

We have the most powerful tool which we need to use wisely. For almost each type of data we can pre-process values before encoding and also we can post-process values after decoding data from JSON. Let us a simple example for that. Suppose that we need to convert calendar:datetime() to ISO date format.

%% We serialize log messages where both the message
%% and the time will be binary (string) in JSON.
-json({log, {binary, message},
            {binary, time, [{pre_encode, {?MODULE, time_to_binary}},
                            {post_decode, {?MODULE, binary_to_time}}]
            }
      }).

-export([time_to_binary/2, binary_to_time/2]).

time_to_binary(_LogRecord, Time) ->
    %% We got the whole record and the time before encoding
    %% Time is supposed to be a calendar:datetime() format.
    %% We convert something like 2011-08-28T15:41:09Z
    {{Y, Mo, D}, {H, Mi, S}} = Time,
    io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0BZ",
                    [Y, Mo, D, H, Mi, S])).

binary_to_time(_Record, IsoDate) ->
    %% Here we have a structure which is half-ready in the first
    %% parameter. It is in jsx raw format like:
    %% [{<<"attr1">>, <<"value1">>}, ...] where value part can be
    %% a binary, a boolean, a number or undefined
    parse_iso_to_date(IsoDate).

parse_iso_date(<<Y1, Y2, Y3, Y4, $-, Mo1, Mo2, $-, D1, D2, $T,
                 H1, H2, $:, Mi1, Mi2, $:, S1, S2, $Z>>) ->
    {{list_to_integer([Y1, Y2, Y3, Y4]),
      list_to_integer([Mo1, Mo2]),
      list_to_integer([D1, D2])},
     {list_to_integer([H1, H2]),
      list_to_integer([Mi1, Mi2]),
      list_to_integer([S1, S2])}}.

In this example the converter functions are a bit long but covers most of the input possibilities. Here is the workflow during encoding:

  • we create a log record as {log, <<"Hello">>, {{2011, 8, 28}, {15, 41, 9}}}
  • call ejson:to_json/1 to that value
  • ejson calls the pre_encode function for that value
  • the intermediate result is [{<<"__rec">>, <<"log">>}, {<<"message">>, <<"Hello">>}, {<<"time">>, <<"2011-08-28T15:41:09Z">>}]
  • so the JSON can be resulted from that jsx list

During decoding:

  • from JSON string jsx creates the intermediate jsx list: [{<<"__rec">>, <<"log">>}, {<<"message">>, <<"Hello">>}, {<<"time">>, <<"2011-08-28T15:41:09Z">>}]
  • ejson knows from the value of <<"__rec">> that the target type is log
  • applies normal decoding process, so we will have a {log, <<"Hello">>, <<"2011-08-28T15:41:09Z">>} record
  • applies post_decode function for the ISO date value, so the time will be {{2011, 8, 28}, {15, 41, 9}}
  • and we get the record we have in the beginning.
Clone this wiki locally