126 lines
2.8 KiB
Elixir
126 lines
2.8 KiB
Elixir
defmodule Poison.DecodeError do
|
|
@type t :: %__MODULE__{message: String.t(), value: any}
|
|
|
|
defexception message: nil, value: nil
|
|
|
|
def message(%{message: nil, value: value}) do
|
|
"unable to decode value: #{inspect(value)}"
|
|
end
|
|
|
|
def message(%{message: message}) do
|
|
message
|
|
end
|
|
end
|
|
|
|
defmodule Poison.Decode do
|
|
@moduledoc false
|
|
|
|
alias Poison.Decoder
|
|
|
|
def transform(value, options) when is_map(value) or is_list(value) do
|
|
case Map.get(options, :as) do
|
|
nil -> value
|
|
as -> transform(value, Map.get(options, :keys), as, options)
|
|
end
|
|
end
|
|
|
|
def transform(value, _options) do
|
|
value
|
|
end
|
|
|
|
defp transform(nil, _keys, _as, _options), do: nil
|
|
|
|
defp transform(value, keys, %{__struct__: _} = as, options) do
|
|
transform_struct(value, keys, as, options)
|
|
end
|
|
|
|
defp transform(value, keys, as, options) when is_map(as) do
|
|
transform_map(value, keys, as, options)
|
|
end
|
|
|
|
defp transform(value, keys, [as], options) do
|
|
for v <- value, do: transform(v, keys, as, options)
|
|
end
|
|
|
|
defp transform(value, _keys, _as, _options) do
|
|
value
|
|
end
|
|
|
|
defp transform_map(value, keys, as, options) do
|
|
Enum.reduce(as, value, fn {key, as}, acc ->
|
|
case Map.get(acc, key) do
|
|
value when is_map(value) or is_list(value) ->
|
|
Map.put(acc, key, transform(value, keys, as, options))
|
|
|
|
_ ->
|
|
acc
|
|
end
|
|
end)
|
|
end
|
|
|
|
defp transform_struct(value, keys, as, options)
|
|
when keys in [:atoms, :atoms!] do
|
|
as
|
|
|> Map.from_struct()
|
|
|> Map.merge(value)
|
|
|> do_transform_struct(keys, as, options)
|
|
end
|
|
|
|
defp transform_struct(value, keys, as, options) do
|
|
as
|
|
|> Map.from_struct()
|
|
|> Enum.reduce(%{}, fn {key, default}, acc ->
|
|
Map.put(acc, key, Map.get(value, Atom.to_string(key), default))
|
|
end)
|
|
|> do_transform_struct(keys, as, options)
|
|
end
|
|
|
|
defp do_transform_struct(value, keys, as, options) do
|
|
default = struct(as.__struct__)
|
|
|
|
as
|
|
|> Map.from_struct()
|
|
|> Enum.reduce(%{}, fn {key, as}, acc ->
|
|
new_value =
|
|
case Map.fetch(value, key) do
|
|
{:ok, ^as} when is_map(as) or is_list(as) ->
|
|
Map.get(default, key)
|
|
|
|
{:ok, value} when is_map(value) or is_list(value) ->
|
|
transform(value, keys, as, options)
|
|
|
|
{:ok, value} ->
|
|
value
|
|
|
|
:error ->
|
|
Map.get(default, key)
|
|
end
|
|
|
|
Map.put(acc, key, new_value)
|
|
end)
|
|
|> Map.put(:__struct__, as.__struct__)
|
|
|> Decoder.decode(options)
|
|
end
|
|
end
|
|
|
|
defprotocol Poison.Decoder do
|
|
@fallback_to_any true
|
|
|
|
@typep keys :: :atoms | :atoms!
|
|
@typep as :: map | struct | [as]
|
|
|
|
@type options :: %{
|
|
optional(:keys) => keys,
|
|
optional(:as) => as
|
|
}
|
|
|
|
@spec decode(t, options) :: any
|
|
def decode(value, options)
|
|
end
|
|
|
|
defimpl Poison.Decoder, for: Any do
|
|
def decode(value, _options) do
|
|
value
|
|
end
|
|
end
|