Awesome
Ordered JSON (OJSON)
Ordered JSON (JSON) is a deterministic or stable JSON encoder and decoder for Erlang and Elixir.
OJSON is similar to projects like fast-stable-stringify and json-stable-stringify which allows for deterministic serialization of object key order.
Before you say, "JSON is unordered!", the initial use-case for this library is with cryptographic operations, which rely on stable input for authentication and verification in serialized form. The ordering only applies to the encoding/serialization operations.
Initially based on devinus/poison, but rewritten for use as a dependency for Erlang or Elixir projects.
Installation
If using Hex, the package can be installed
by adding ojson
to your list of dependencies in mix.exs
:
def deps do
[{:ojson, "~> 1.0.0"}]
end
If using rebar3, the package can be installed by adding ojson
to you list of dependencies in rebar.config
:
{deps, [
{ojson, ".*", {git, "git://github.com/potatosalad/erlang-ojson.git", {branch, "master"}}}
]}.
The HexDocs can be found at https://hexdocs.pm/ojson.
Usage
Basic Usage
Elixir
iex> OJSON.encode(%{"z" => 1, "a" => 2})
{:ok, "{\"a\":2,\"z\":1}"}
iex> OJSON.encode!(%{"z" => 1, "a" => 2})
"{\"a\":2,\"z\":1}"
iex> OJSON.encode_to_iodata(%{"z" => 1, "a" => 2})
{:ok, [123, [[34, ["a"], 34], 58, "2"], [[44, [34, ["z"], 34], 58, "1"]], 125]}
iex> OJSON.encode_to_iodata!(%{"z" => 1, "a" => 2})
[123, [[34, ["a"], 34], 58, "2"], [[44, [34, ["z"], 34], 58, "1"]], 125]
iex> OJSON.decode("{\"a\":2,\"z\":1}")
{:ok, %{"a" => 2, "z" => 1}}
iex> OJSON.decode!("{\"a\":2,\"z\":1}")
%{"a" => 2, "z" => 1}
Erlang
ojson:encode(#{ <<"z">> => 1, <<"a">> => 2 }).
% {ok, <<"{\"a\":2,\"z\":1}">>}
ojson:'encode!'(#{ <<"z">> => 1, <<"a">> => 2 }).
% <<"{\"a\":2,\"z\":1}">>
ojson:encode_to_iodata(#{ <<"z">> => 1, <<"a">> => 2 }).
% {ok, [123,[[34,[<<"a">>],34],58,<<"2">>],[[44,[34,[<<"z">>],34],58,<<"1">>]],125]}
ojson:'encode_to_iodata!'(#{ <<"z">> => 1, <<"a">> => 2 }).
% [123,[[34,[<<"a">>],34],58,<<"2">>],[[44,[34,[<<"z">>],34],58,<<"1">>]],125]
ojson:decode(<<"{\"a\":2,\"z\":1}">>).
% {ok, #{<<"a">> => 2, <<"z">> => 1}}
ojson:'decode!'(<<"{\"a\":2,\"z\":1}">>).
% #{<<"a">> => 2, <<"z">> => 1}
Deterministic Example
Elixir
# Let's first generate a map with 32 key/value pairs:
iex> map = for key <- :lists.seq(0, 32), into: %{}, do: {String.pad_leading(:erlang.integer_to_binary(key, 16), 2, "0"), 0}
%{"10" => 0, "0E" => 0, "14" => 0, "12" => 0, "16" => 0, "07" => 0, "1C" => 0,
"06" => 0, "19" => 0, "13" => 0, "04" => 0, "1E" => 0, "1F" => 0, "11" => 0,
"15" => 0, "17" => 0, "1A" => 0, "0A" => 0, "18" => 0, "0C" => 0, "00" => 0,
"0B" => 0, "09" => 0, "02" => 0, "0D" => 0, "1D" => 0, "20" => 0, "03" => 0,
"0F" => 0, "1B" => 0, "01" => 0, "05" => 0, "08" => 0}
# Notice that the key/value pairs are unordered.
# Now let's encode our unordered map into an ordered JSON string:
iex> OJSON.encode!(map)
"{\"00\":0,\"01\":0,\"02\":0,\"03\":0,\"04\":0,\"05\":0,\"06\":0,\"07\":0,\"08\":0,\"09\":0,\"0A\":0,\"0B\":0,\"0C\":0,\"0D\":0,\"0E\":0,\"0F\":0,\"10\":0,\"11\":0,\"12\":0,\"13\":0,\"14\":0,\"15\":0,\"16\":0,\"17\":0,\"18\":0,\"19\":0,\"1A\":0,\"1B\":0,\"1C\":0,\"1D\":0,\"1E\":0,\"1F\":0,\"20\":0}"
Erlang
%% Let's first generate a map with 32 key/value pairs:
Map = maps:from_list([{case erlang:integer_to_binary(I, 16) of <<B>> -> <<$0,B>>; B -> B end, 0} || I <- lists:seq(0, 32)]).
% #{<<"10">> => 0,<<"0E">> => 0,<<"14">> => 0,<<"12">> => 0,
% <<"16">> => 0,<<"07">> => 0,<<"1C">> => 0,<<"06">> => 0,
% <<"19">> => 0,<<"13">> => 0,<<"04">> => 0,<<"1E">> => 0,
% <<"1F">> => 0,<<"11">> => 0,<<"15">> => 0,<<"17">> => 0,
% <<"1A">> => 0,<<"0A">> => 0,<<"18">> => 0,<<"0C">> => 0,
% <<"00">> => 0,<<"0B">> => 0,<<"09">> => 0,<<"02">> => 0,
% <<"0D">> => 0,<<"1D">> => 0,<<"20">> => 0,<<"03">> => 0,
% <<"0F">> => 0,<<"1B">> => 0,<<"01">> => 0,<<"05">> => 0,
% <<"08">> => 0}
%% Notice that the key/value pairs are unordered.
%% Now let's encode our unordered map into an ordered JSON string:
ojson:'encode!'(Map).
% <<"{\"00\":0,\"01\":0,\"02\":0,\"03\":0,\"04\":0,\"05\":0,\"06\":0,\"07\":0,\"08\":0,\"09\":0,\"0A\":0,\"0B\":0,\"0C\":0,\"0D\":0,\"0E\":0,\"0F\":0,\"10\":0,\"11\":0,\"12\":0,\"13\":0,\"14\":0,\"15\":0,\"16\":0,\"17\":0,\"18\":0,\"19\":0,\"1A\":0,\"1B\":0,\"1C\":0,\"1D\":0,\"1E\":0,\"1F\":0,\"20\":0}">>