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?
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