elixirelixir-poison

Duplicated modules: 'Elixir.Poison.Encoder.Map' specified in poison `mix release`


I am working in an umbrella application that works with Poison to encode and decode Json. In order to encode my ecto models I write the following implementation:

def defimpl Poison.Encoder, for: Any do
def encode(%{__struct__: App.Models.Task} = struct, options) do
  struct =
    if struct.geom do
      {lon, lat} =  struct.geom.coordinates
      %{struct | lon: lon, lat: lat}
    else
      struct
    end
  enconde_model(struct, options)
end

def encode(%{__struct__: _} = struct, options) do
  enconde_model(struct, options)
end

def encode({lon, lat}, options) when is_float(lon) and is_float(lat) do
  %{lon: lon, lat: lat}
  |> sanitize_map()
  |> Poison.Encoder.Map.encode(options)
end

defp enconde_model(model, options) do
  model
  |> Map.from_struct()
  |> sanitize_map()
  |> Poison.Encoder.Map.encode(options)
end

defp sanitize_map(map) do
  map
  |> Enum.filter(
    fn({_, %Ecto.Association.NotLoaded{}}) -> false
      ({:__meta__, _}) -> false
      ({:__struct__, _}) -> false
      ({_, _}) -> true
    end)
  |> Map.new()
end

end

The think is that once I run mix release the program raises the error unless I comment out the code I just show above.

So, there is another way to achieve this same functionality without using the implementation?


Solution

  • You're getting that error because you're overriding the existing encoder implementations. The right way to do this is to implement Poison.Encoder for each struct you want to encode. With @derive, this is pretty straightforward:

    @derive {Poison.Encoder, only: [:the, :field, :names, :you, :want, :in, :json]}
    schema "tasks" do
      ...
    end
    

    For the tuple, you'll need to manually convert those to maps in the encoding for the structs which can contain those tuples:

    defimpl Poison.Encoder, for: Something do
      def encode(%{a_tuple: {lat, lon}, something: else_}, options) do
        Poison.encode!(%{location: %{lat: lat, lon: lon}, something: else_}, options)
      end
    end