毒药:当 json 值可能是对象或对象列表时如何解码为结构
Poison: How to decode to structs when the json value could be an object, or a list of objects
我正在尝试使用 Poison
解码以下 json 字符串
iex(1)> fetch(1)
{:ok,
"{\"name\":\"Anabela\",\"surname\":\"Neagu\",\"gender\":\"female\",\"region\":\"Romania\"}"}
iex(2)> fetch(2)
{:ok,
"[{\"name\":\"Juana\",\"surname\":\"Su├írez\",\"gender\":\"female\",\"region\":\"Argentina\"},{\"name\":\"ðíðÁÐÇð│ðÁð╣\",\"surname\":\"ðƒð╗ð¥Ðéð¢ð©ð║ð¥ð▓\",\"gender\":\"male\",\"region\":\"Russia\"}]"}
执行 fetch(1) |> decode_response
是行不通的,尽管它的参数严格限制为 1。
我确实有以下错误
10:07:52.663 [error] GenServer Newsequence.Server terminating
** (BadMapError) expected a map, got: {"gender", "female"}
(stdlib) :maps.find("gender", {"gender", "female"})
(elixir) lib/map.ex:145: Map.get/3
lib/poison/decoder.ex:49: anonymous fn/3 in Poison.Decode.transform_struct/4
(stdlib) lists.erl:1262: :lists.foldl/3
lib/poison/decoder.ex:48: Poison.Decode.transform_struct/4
lib/poison/decoder.ex:24: anonymous fn/5 in Poison.Decode.transform/4
(stdlib) lists.erl:1262: :lists.foldl/3
lib/poison/decoder.ex:24: Poison.Decode.transform/4 Last message: {:getpeople, 1} State: {[], #PID<0.144.0>}
我的函数如下:
def decode_response({:ok, body}) do
Poison.decode!(body, as: [%Personne{}])
end
然后我认为参数等于 1,函数应该是
def decode_response({:ok, body}) do
Poison.decode!(body, as: %Personne{})
end
我最终认为在我的 fetch 函数给出的字符串中计算元组数量并使用守卫来选择使用哪个 decode_response 是个好主意,但我不知道如何做。
有人可以给我指明正确的方向吗?
此致,
皮埃尔
您可以先使用 Poison.decode!/1
解码为本机 map/list,然后计算 as:
的相关值,最后使用本机调用 Poison.Decoder.decode/2
数据结构和解码成的结构:
def decode_response({:ok, body}) do
parsed = Poison.decode!(body)
as =
cond do
is_map(parsed) -> %Personne{}
is_list(parsed) -> [%Personne{}]
end
Poison.Decoder.decode(parsed, as: as)
end
演示:
defmodule Personne do
defstruct [:name, :surname, :gender, :region]
end
defmodule Main do
def main do
f1 = {:ok, "{\"name\":\"Anabela\",\"surname\":\"Neagu\",\"gender\":\"female\",\"region\":\"Romania\"}"}
f2 = {:ok, "[{\"name\":\"Juana\",\"surname\":\"Su├írez\",\"gender\":\"female\",\"region\":\"Argentina\"},{\"name\":\"ðíðÁÐÇð│ðÁð╣\",\"surname\":\"ðƒð╗ð¥Ðéð¢ð©ð║ð¥ð▓\",\"gender\":\"male\",\"region\":\"Russia\"}]"}
f1 |> decode_response |> IO.inspect
f2 |> decode_response |> IO.inspect
end
def decode_response({:ok, body}) do
parsed = Poison.decode!(body)
as =
cond do
is_map(parsed) -> %Personne{}
is_list(parsed) -> [%Personne{}]
end
Poison.Decoder.decode(parsed, as: as)
end
end
Main.main
输出:
%{"gender" => "female", "name" => "Anabela", "region" => "Romania",
"surname" => "Neagu"}
[%{"gender" => "female", "name" => "Juana", "region" => "Argentina",
"surname" => "Suárez"},
%{"gender" => "male", "name" => "ðíðÁÐÇð│ðÁð╣",
"region" => "Russia",
"surname" => "ðƒð╗ð¥Ðéð¢ð©ð║ð¥ð▓"}]
这有点天真,但它会节省对 JSON:
的第二次解析
if (body |> String.trim_leading() |> String.starts_with?("[")) do
Poison.decode!(body, as: [%Personne{}])
else
Poison.decode!(body, as: %Personne{})
end
请注意,如果您使用的是 Elixir 1.2 或更低版本,那么您需要使用 String.lstrip/1
而不是 String.trim_leading/1
。
另一种方法是将其解码为映射,使用 String.to_existing_atom/1 to convert the keys, then use Kernel.struct/2 转换为结构。
def decode_response({:ok, body}) do
body |> Poison.decode!() |> string_map_struct(Personne)
end
defp string_map_struct(entry, module) do
params =
entry
|> Enum.map(fn {key, val} ->
key = to_existing_atom(key)
{key, val}
end)
|> Enum.into(%{})
struct(module, params)
end
defp to_existing_atom(key) do
try do
String.to_existing_atom(key)
rescue
ArgumentError -> key
end
end
我正在尝试使用 Poison
解码以下 json 字符串iex(1)> fetch(1)
{:ok,
"{\"name\":\"Anabela\",\"surname\":\"Neagu\",\"gender\":\"female\",\"region\":\"Romania\"}"}
iex(2)> fetch(2)
{:ok,
"[{\"name\":\"Juana\",\"surname\":\"Su├írez\",\"gender\":\"female\",\"region\":\"Argentina\"},{\"name\":\"ðíðÁÐÇð│ðÁð╣\",\"surname\":\"ðƒð╗ð¥Ðéð¢ð©ð║ð¥ð▓\",\"gender\":\"male\",\"region\":\"Russia\"}]"}
执行 fetch(1) |> decode_response
是行不通的,尽管它的参数严格限制为 1。
我确实有以下错误
10:07:52.663 [error] GenServer Newsequence.Server terminating
** (BadMapError) expected a map, got: {"gender", "female"}
(stdlib) :maps.find("gender", {"gender", "female"})
(elixir) lib/map.ex:145: Map.get/3
lib/poison/decoder.ex:49: anonymous fn/3 in Poison.Decode.transform_struct/4
(stdlib) lists.erl:1262: :lists.foldl/3
lib/poison/decoder.ex:48: Poison.Decode.transform_struct/4
lib/poison/decoder.ex:24: anonymous fn/5 in Poison.Decode.transform/4
(stdlib) lists.erl:1262: :lists.foldl/3
lib/poison/decoder.ex:24: Poison.Decode.transform/4 Last message: {:getpeople, 1} State: {[], #PID<0.144.0>}
我的函数如下:
def decode_response({:ok, body}) do
Poison.decode!(body, as: [%Personne{}])
end
然后我认为参数等于 1,函数应该是
def decode_response({:ok, body}) do
Poison.decode!(body, as: %Personne{})
end
我最终认为在我的 fetch 函数给出的字符串中计算元组数量并使用守卫来选择使用哪个 decode_response 是个好主意,但我不知道如何做。
有人可以给我指明正确的方向吗?
此致,
皮埃尔
您可以先使用 Poison.decode!/1
解码为本机 map/list,然后计算 as:
的相关值,最后使用本机调用 Poison.Decoder.decode/2
数据结构和解码成的结构:
def decode_response({:ok, body}) do
parsed = Poison.decode!(body)
as =
cond do
is_map(parsed) -> %Personne{}
is_list(parsed) -> [%Personne{}]
end
Poison.Decoder.decode(parsed, as: as)
end
演示:
defmodule Personne do
defstruct [:name, :surname, :gender, :region]
end
defmodule Main do
def main do
f1 = {:ok, "{\"name\":\"Anabela\",\"surname\":\"Neagu\",\"gender\":\"female\",\"region\":\"Romania\"}"}
f2 = {:ok, "[{\"name\":\"Juana\",\"surname\":\"Su├írez\",\"gender\":\"female\",\"region\":\"Argentina\"},{\"name\":\"ðíðÁÐÇð│ðÁð╣\",\"surname\":\"ðƒð╗ð¥Ðéð¢ð©ð║ð¥ð▓\",\"gender\":\"male\",\"region\":\"Russia\"}]"}
f1 |> decode_response |> IO.inspect
f2 |> decode_response |> IO.inspect
end
def decode_response({:ok, body}) do
parsed = Poison.decode!(body)
as =
cond do
is_map(parsed) -> %Personne{}
is_list(parsed) -> [%Personne{}]
end
Poison.Decoder.decode(parsed, as: as)
end
end
Main.main
输出:
%{"gender" => "female", "name" => "Anabela", "region" => "Romania",
"surname" => "Neagu"}
[%{"gender" => "female", "name" => "Juana", "region" => "Argentina",
"surname" => "Suárez"},
%{"gender" => "male", "name" => "ðíðÁÐÇð│ðÁð╣",
"region" => "Russia",
"surname" => "ðƒð╗ð¥Ðéð¢ð©ð║ð¥ð▓"}]
这有点天真,但它会节省对 JSON:
的第二次解析if (body |> String.trim_leading() |> String.starts_with?("[")) do
Poison.decode!(body, as: [%Personne{}])
else
Poison.decode!(body, as: %Personne{})
end
请注意,如果您使用的是 Elixir 1.2 或更低版本,那么您需要使用 String.lstrip/1
而不是 String.trim_leading/1
。
另一种方法是将其解码为映射,使用 String.to_existing_atom/1 to convert the keys, then use Kernel.struct/2 转换为结构。
def decode_response({:ok, body}) do
body |> Poison.decode!() |> string_map_struct(Personne)
end
defp string_map_struct(entry, module) do
params =
entry
|> Enum.map(fn {key, val} ->
key = to_existing_atom(key)
{key, val}
end)
|> Enum.into(%{})
struct(module, params)
end
defp to_existing_atom(key) do
try do
String.to_existing_atom(key)
rescue
ArgumentError -> key
end
end