elixiruuidelixir-jason

Jason encode binary to UUID in Elixir


I'm trying to think of a clean way to override the JasonEncoder for binary that would allow me to pull out UUIDs from binary. Here is what I would like to do in theory:

defimpl Jason.Encoder, for: BitString do
  def encode(binary, opts) when is_binary(binary) do
    with false <- String.valid?(binary),
    {:ok, uid} <- Ecto.UUID.cast(binary) do
      uid
    else
    _ -> Jason.Encode.string(binary, opts)
    end
  end

  def encode(bitstring, _opts) do
    raise Protocol.UndefinedError,
      protocol: @protocol,
      value: bitstring,
      description: "cannot encode a bitstring to JSON"
  end
end

I'm thinking along the lines of this stackoverflow example, except I think the issue here is that native types like BitString can not be overwritten.


Solution

  • Rather than trying to globally override the protocol for BitString, you can wrap a UUID in its own struct, and implement the Jason.Encoder protocol for that struct:

    defmodule JsonUUID do
      defstruct [:uuid]
    
      defimpl Jason.Encoder do
        def encode(%JsonUUID{uuid: uuid}, opts) when is_binary(uuid) do
          uuid
          |> Ecto.UUID.cast!()
          |> Jason.Encode.string(opts)
        end
      end
    end
    

    Test:

    Jason.encode!(%JsonUUID{uuid: Ecto.UUID.bingenerate()})
    => "\"8cbf3df9-8408-4ce3-ac44-980a0f7dc19b\""