1

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_responseparam は厳密に 1 までですが、aを実行しても機能しません。

次のエラーがあります

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

次に、paramが1に等しい場合、関数は次のようになるはずです

   def decode_response({:ok, body}) do
        Poison.decode!(body, as: %Personne{})
    end

最終的に、フェッチ関数によって指定された文字列内のタプルをカウントし、ガードを使用して使用するdecode_responseを選択するのは良い考えだと思いましたが、方法がわかりません。

誰かが私を正しい方向に向けてくれますか?

よろしく、

ピエール

4

2 に答える 2

1

これを行うには、まず を使用してネイティブ マップ/リストにデコードしPoison.decode!/1、次に に関連する値を計算し、最後にネイティブ データ構造とデコードする構造を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" => "ðƒð╗ð¥Ðéð¢ð©ð║ð¥ð▓"}]
于 2016-08-17T08:59:51.717 に答える
0

少し素朴ですが、JSON の 2 番目の解析を実行する手間を省きます。

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を使用してキーを変換し、次に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
于 2016-08-17T09:00:25.763 に答える