Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ It works similar like [OJSON](https://hex.pm/packages/ojson) but can output a pr
StableJason.encode(%{c: 3, b: 2, a: 1})
{:ok, ~S|{"a":1,"b":2,"c":3}|}

StableJason.encode(%{c: 3, b: 2, a: 1}, pretty: true)
StableJason.encode(%{c: 3, b: 2, a: 1}, :asc, pretty: true)
{:ok, "{\n \"a\": 1,\n \"b\": 2,\n \"c\": 3\n}"}

StableJason.encode!(%{c: 3, b: 2, a: 1})
"{\"a\":1,\"b\":2,\"c\":3}"

StableJason.encode!(%{c: 3, b: 2, a: 1}, pretty: true)
StableJason.encode!(%{c: 3, b: 2, a: 1}, :asc, pretty: true)
"{\n \"a\": 1,\n \"b\": 2,\n \"c\": 3\n}"
```

Expand Down
12 changes: 10 additions & 2 deletions lib/stable_jason.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ defmodule StableJason do
iex> StableJason.encode(%{c: 3, b: 2, a: 1})
{:ok, ~S|{"a":1,"b":2,"c":3}|}

iex> StableJason.encode(%{c: 3, b: 2, a: 1}, sorter: :desc)
{:ok, ~S|{"c":3,"b":2,"a":1}|}

iex> StableJason.encode(<<0::1>>)
{:error,
%Protocol.UndefinedError{
Expand All @@ -24,7 +27,8 @@ defmodule StableJason do
}}
"""
def encode(input, opts \\ []) do
case Encoder.encode(input) do
{sorter, opts} = Keyword.pop(opts, :sorter)
case Encoder.encode(input, sorter || :asc) do
{:ok, result} -> Jason.encode(result, opts)
{:error, error} -> {:error, error}
end
Expand All @@ -41,12 +45,16 @@ defmodule StableJason do
iex> StableJason.encode!(%{a: 1})
~S|{"a":1}|

iex> StableJason.encode!(%{a: 1, b: 3}, sorter: :desc)
~S|{"b":3,"a":1}|

iex> StableJason.encode!("\\xFF")
** (Jason.EncodeError) invalid byte 0xFF in <<255>>

"""
def encode!(input, opts \\ []) do
case Encoder.encode(input) do
{sorter, opts} = Keyword.pop(opts, :sorter)
case Encoder.encode(input, sorter || :asc) do
{:ok, result} -> Jason.encode!(result, opts)
{:error, error} -> raise error
end
Expand Down
20 changes: 10 additions & 10 deletions lib/stable_jason/encoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,32 @@ defmodule StableJason.Encoder do
description: "cannot encode a bitstring to JSON"
}}
"""
def encode(input) do
def encode(input, sorter \\ :asc) do
case Jason.encode(input) do
{:ok, result} -> {:ok, encode_stable(result)}
{:ok, result} -> {:ok, encode_stable(result, sorter)}
{:error, error} -> {:error, error}
end
end

defp encode_stable(input) when is_binary(input) do
defp encode_stable(input, sorter) when is_binary(input) do
input
|> Jason.decode!(%{objects: :ordered_objects})
|> do_encode_stable()
|> do_encode_stable(sorter)
end

defp do_encode_stable(%Jason.OrderedObject{} = ordered_object) do
defp do_encode_stable(%Jason.OrderedObject{} = ordered_object, sorter) do
stable_values =
for {k, v} <- List.keysort(ordered_object.values, 0) do
{k, do_encode_stable(v)}
for {k, v} <- List.keysort(ordered_object.values, 0, sorter) do
{k, do_encode_stable(v, sorter)}
end

%Jason.OrderedObject{values: stable_values}
end

defp do_encode_stable(input) when is_list(input) do
defp do_encode_stable(input, sorter) when is_list(input) do
input
|> Enum.map(&do_encode_stable/1)
|> Enum.map(fn i -> do_encode_stable(i, sorter) end)
end

defp do_encode_stable(input), do: input
defp do_encode_stable(input, _sorter), do: input
end
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
defmodule StableJason.MixProject do
use Mix.Project

@version "1.0.0"
@version "2.0.0"
@source_url "https://github.com/egze/stable_jason"

def project do
[
app: :stable_jason,
version: @version,
elixir: "~> 1.13",
elixir: "~> 1.14",
start_permanent: Mix.env() == :prod,
deps: deps(),
description: description(),
Expand Down
22 changes: 21 additions & 1 deletion test/stable_jason/encoder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ defmodule StableJason.EncoderTest do
end

test "nested map with more complex data types" do
input = %{c: 3, b: %{d: Date.utc_today(), a: 1.5}, a: 1}
input = %{c: 3, b: %{d: ~D[2024-01-18], a: 1.5}, a: 1}

assert Encoder.encode(input) ==
{:ok,
Expand Down Expand Up @@ -60,5 +60,25 @@ defmodule StableJason.EncoderTest do
1
]}
end

test "using a sorter atom" do
input = %{a: 5, aa: 4, b: 9, A: 12}

assert Encoder.encode(input, :desc) ==
{:ok, %Jason.OrderedObject{values: [{"b", 9}, {"aa", 4}, {"a", 5}, {"A", 12}]}}
end

test "using a sorter function" do
input = %{a: 5, aa: 4, b: 9, A: 12}

sorter = fn a, b ->
if String.length(inspect(a)) == String.length(inspect(b)),
do: a < b,
else: String.length(inspect(a)) < String.length(inspect(b))
end

assert Encoder.encode(input, sorter) ==
{:ok, %Jason.OrderedObject{values: [{"A", 12}, {"a", 5}, {"b", 9}, {"aa", 4}]}}
end
end
end
Loading