Skip to content

Instantly share code, notes, and snippets.

@jschoch
Created April 9, 2015 14:22
Show Gist options
  • Save jschoch/b9c4f9c08fa5be127c67 to your computer and use it in GitHub Desktop.
Save jschoch/b9c4f9c08fa5be127c67 to your computer and use it in GitHub Desktop.
struct encoding and decoding nested structs with poison
defmodule MyTime do
defstruct stamp: {0,0,0}
defimpl Poison.Encoder, for: MyTime do
def encode(%MyTime{} = t,options) do
list = Tuple.to_list(t.stamp)
Poison.Encoder.Map.encode(%MyTime{stamp: list},options)
end
end
defimpl Poison.Decoder, for: MyTime do
def decode(s,options) do
t = List.to_tuple(s.stamp)
%MyTime{stamp: t}
end
end
end
defmodule Y do
#require Poison.Decoder.MyTime
defmacro __using__(_) do
quote do
require Logger
defimpl Poison.Encoder, for: __MODULE__ do
def encode(y,options) do
Poison.Encoder.Map.encode(y,options)
end
end
defimpl Poison.Decoder, for: __MODULE__ do
def decode(map,options) when is_map(map) do
map = Enum.reduce(Map.keys(map),map,fn(key,map) ->
if (is_map(map[key])) do
case Map.has_key?(map[key],:__struct__) do
true ->
mod_name = map[key][:__struct__]
mod_atom = String.to_atom(mod_name)
mod_struct = mod_atom.__struct__.__struct__
["Elixir"|short_name] = String.split(mod_name,".")
decoder_name = :"Elixir.Poison.Decoder.#{short_name}"
case Code.ensure_loaded(decoder_name) do
{:module,_} ->
decoded = decoder_name.decode(map[key],as: mod_struct)
map = Map.put(map,key,decoded)
{:error,reason} ->
Loggger.warn "couldn't decode #{decoder_name} #{inspect reason}"
end
false ->
Logger.warn "didn't find a :__struct__ for #{key}"
end
end
map
end)
end
end
#defoverridable [__struct__: 0]
end
end
end
defmodule X do
#require MyTime
@derive Access
defstruct name: :foo, ts: %MyTime{stamp: {2,2,2}}, m: nil, z: nil
use Y
end
defmodule S do
defstruct name: :foo
use Y
end
defmodule Z do
defstruct name: :baz
end
defmodule UsingMixinsTest do
use ExUnit.Case
test "the truth" do
item = %MyTime{stamp: {1,1,1}}
s = Poison.encode!(item)
IO.puts "s: #{inspect s}"
de = Poison.decode!(s,as: MyTime)
IO.puts "de: #{inspect de}"
assert item.stamp == de.stamp
end
test "embed works" do
s = Poison.encode!(%X{name: :bar,m: %{},z: %Z{}})
IO.puts "X: " <> inspect s
x = Poison.decode!(s, [keys: :atoms,as: X])
IO.puts "X: " <> inspect x
assert %MyTime{} = x.ts
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment