Problem: Convert nested structs or nested encoded maps into a decoded map and remove keys with nil values.
Solution: Walk the structure and convert all structures to decoded map.
Before (nested struct):
%TopLevelStuct{
_type: "Purchase",
account_key: "ZBmrcY7sv47nCaZOx5TXBTJuazy"
payment_method_key: "Qg9SqEaGl909ulsNhzrzJsBYSYm",
payment_method_snapshot: %NestedStruct{
_type: nil,
card_type: "master"}
}
After (Struct to Map):
%{
_type: "Purchase",
account_key: "ZBmrcY7sv47nCaZOx5TXBTJuazy",
payment_method_key: "Qg9SqEaGl909ulsNhzrzJsBYSYm",
payment_method_snapshot: %{card_type: "master"}
}
defmodule MapMaker do
def convert_to_map!(nil) do
nil
end
def convert_to_map!(attribute) when is_map(attribute) do
ensure_struct_is_converted_to_map(attribute)
|> Map.keys
|> Enum.reduce(%{}, fn(key, new_map) ->
update_map(new_map, key, convert_to_map!(Map.get(attribute,key)))
end)
|> convert_map_keys_to_atoms
|> check_for_empty_map
end
def convert_to_map!(attribute) when is_binary(attribute) do
case Poison.decode(attribute) do
{:ok, decoded_attribute=%{}} -> convert_to_map!(decoded_attribute)
{:ok, decoded_attribute} -> decoded_attribute
{:error, _ } -> attribute
{:error, _ , _} -> attribute
end
end
def convert_to_map!(attribute) do
attribute
end
def convert_map_keys_to_atoms(map) do
map
|> Enum.reduce(%{}, fn ({key, val}, acc) -> Map.put(acc, ensure_key_is_atom(key), val) end)
end
def ensure_key_is_atom(key) when is_binary(key) do
String.to_atom(key)
end
def ensure_key_is_atom(key) when is_atom(key) do
key
end
defp ensure_struct_is_converted_to_map(struct=%{__struct__: _}) do
Map.from_struct(struct)
end
defp ensure_struct_is_converted_to_map(map) do
map
end
defp update_map(map, _key, _value = nil), do: map
defp update_map(map, key, value), do: Map.put(map, key, value)
defp check_for_empty_map(map) when map == %{}, do: nil
defp check_for_empty_map(map), do: map
end