Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

:null atom and JSON #198

Open
grepz opened this issue Sep 20, 2021 · 0 comments
Open

:null atom and JSON #198

grepz opened this issue Sep 20, 2021 · 0 comments

Comments

@grepz
Copy link

grepz commented Sep 20, 2021

Hello, i have following problem that I cannon solve right now due to specifics of handling null and nil in projects which uses both erlang land elixir libs.
Situation:
there are both erlang and elixir projects.
Both type of projects uses same erlang library.
Erlang library is using jiffy as a library of choice to handle JSON encoding/decoding, jiffy, being erlang library, handles JSON null values as null atoms in erlang terms, and vice versa.
Elixir projects are mostly using Poison for handling their HTTP responses, and Poison on the other hand handles JSON null value as nil and vice versa. Data from library's jiffy goes to Poison http encoder. Thus creating a problem with nil/null, since null atom for Poison is a string.
I can set jiffy to produce nil's instead of null's(use_nil option for decoder/encoder), but that will break erlang projects that are using same library, since they don't expect to meet nil's in place of null's.

It would be great to have use_null option in poison to be able to override this madness.
Problem example:

:jiffy.decode("{\"key\":null}", [:return_maps]) |> Poison.encode
{:ok, "{\"key\":\"null\"}"}

Naive approach to handle such situation will be:

   @typep indent :: non_neg_integer
   @typep offset :: non_neg_integer
   @typep strict_keys :: boolean
+  @typep use_null :: boolean
 
   @type options :: %{
           optional(:escape) => escape,
           optional(:pretty) => pretty,
           optional(:indent) => indent,
           optional(:offset) => offset,
-          optional(:strict_keys) => strict_keys
+          optional(:strict_keys) => strict_keys,
+          optional(:use_null) => use_null
         }
 
   @spec encode(t, options) :: iodata
@@ -102,6 +104,12 @@ defimpl Poison.Encoder, for: Atom do
   def encode(nil, _options), do: "null"
   def encode(true, _options), do: "true"
   def encode(false, _options), do: "false"
+  def encode(:null, options) do
+    case Map.get(options, :use_null, false) do
+      true -> "null"
+      false -> Poison.Encoder.BitString.encode("null", options)
+    end
+  end
 
   def encode(atom, options) do
     Poison.Encoder.BitString.encode(Atom.to_string(atom), options)

I understand that it affects performance, but right now can't find a way to solve problem without it.
I've tried to introduce conditional macros on erlang library side(LIBRARY_ENV_TYPE=(elixir|erlang) which must be set/overriden in project that is using this library(rebar's overrides), but it seems mix does not support overrides in any way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant