Thanks @mudasobwa, for prompting me to dig further into the decoders
I’ve gone with custom Poison decoders for each of the relevant structs. It’s really simple to just add the possible structs to the children_types in the decode function and it catches them all on the first pass instead of needing to rewalk the object until all the structs have been matched.
defmodule TypeA do
defstruct shared: "", only_a: ""
end
defmodule TypeB do
defstruct shared: "", only_b: ""
end
defmodule Parent do
defstruct name: "", children: []
end
defimpl Poison.Decoder, for: Parent do
def decode(value, options) do
children_types = fn
%{only_a: _a} -> %TypeA{}
%{only_b: _b} -> %TypeB{}
end
value
|> Map.update!(:children, fn children ->
children
|> Enum.map(
&Poison.Decode.transform(
&1,
Map.put(options, :as, children_types)
)
)
end)
end
end
"""
{
"name": "parent",
"children": [
{
"shared": "value",
"only_a": "A"
},
{
"shared": "value",
"only_b": "B"
}
]
}
"""
|> Poison.decode!(keys: :atoms!, as: %Parent{})
%Parent{
name: "parent",
children: [
%TypeA{shared: "value", only_a: "A"},
%TypeB{shared: "value", only_b: "B"}
]
}






















