Decoding Json with nested lists of dissimilar structs

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"}
  ]
}