defmodule :zj do @moduledoc false def encode(term) do term |> normalize_for_json(:loose) |> Jason.encode!() |> String.to_charlist() end def decode(stream) do with {:ok, decoded} <- stream |> :unicode.characters_to_binary() |> Jason.decode() do {:ok, to_charlist_values(decoded)} end end def binary_encode(term) do term |> normalize_for_json(:strict) |> Jason.encode!() end def binary_decode(stream) do with {:ok, decoded} <- stream |> :unicode.characters_to_binary() |> Jason.decode() do {:ok, replace_null(decoded)} end end defp to_charlist_values(nil), do: :undefined defp to_charlist_values(value) when is_binary(value), do: String.to_charlist(value) defp to_charlist_values(value) when is_list(value) do Enum.map(value, &to_charlist_values/1) end defp to_charlist_values(value) when is_map(value) do Map.new(value, fn {key, item} -> {to_charlist_values(key), to_charlist_values(item)} end) end defp to_charlist_values(value), do: value defp replace_null(nil), do: :undefined defp replace_null(value) when is_list(value), do: Enum.map(value, &replace_null/1) defp replace_null(value) when is_map(value) do Map.new(value, fn {key, item} -> {key, replace_null(item)} end) end defp replace_null(value), do: value defp normalize_for_json(:undefined, _mode), do: nil defp normalize_for_json(value, _mode) when is_binary(value), do: value defp normalize_for_json(value, _mode) when is_number(value), do: value defp normalize_for_json(value, _mode) when value in [nil, true, false], do: value defp normalize_for_json(value, _mode) when is_atom(value) do Atom.to_string(value) end defp normalize_for_json(value, :loose) when is_list(value) do if List.ascii_printable?(value) do List.to_string(value) else Enum.map(value, &normalize_for_json(&1, :loose)) end end defp normalize_for_json(value, :strict) when is_list(value) do Enum.map(value, &normalize_for_json(&1, :strict)) end defp normalize_for_json(value, mode) when is_tuple(value) do value |> Tuple.to_list() |> Enum.map(&normalize_for_json(&1, mode)) end defp normalize_for_json(value, mode) when is_map(value) do Map.new(value, fn {key, item} -> {normalize_key(key), normalize_for_json(item, mode)} end) end defp normalize_for_json(value, _mode) do to_string(value) end defp normalize_key(key) when is_binary(key) or is_atom(key), do: key defp normalize_key(key) when is_list(key), do: List.to_string(key) defp normalize_key(key), do: to_string(key) end