【问题标题】:pattern matching with nested json response attributes与嵌套 json 响应属性匹配的模式
【发布时间】:2026-01-14 18:35:01
【问题描述】:

在得到get_public_request 函数的响应后,我试图找到一种更优雅的方式来设置变量。我在以下代码示例中引用了var1var2

def get_prices(item) do
    url = item_path(item)
    response = get_public_request(url)
    var1 = response["item"]["buy"]
    var2 = response["item"]["sell"]
end

def get_public_request(url) do
    HTTPoison.start
    case HTTPoison.get(url) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        Poison.decode!(body)
    ...
    end
end

我得到的响应(在 Poison.decocode 之后)如下所示:

%{"at" => 1536333060, "item" => %{"buy" => "8971.71", "area" => "A16", "sell" => "9019.89"}}

【问题讨论】:

    标签: pattern-matching elixir phoenix-framework


    【解决方案1】:

    在您提供的代码中,get_prices 中所需的一切都是这两个参数。选项是从get_public_request 显式返回它们:

    def get_public_request(url) do
      HTTPoison.start
      case HTTPoison.get(url) do
        {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
          with %{"item" => %{"buy" => buy, "sell" => sell}} <- Poison.decode!(body),
            do: {buy, sell}
        ...
        end
    end
    

    Kernel.SpecialForms.with/1 将按原样返回任何不匹配的 RHO,因此如果我们进入do,我们就有了我们需要的东西。称它为:

    def get_prices(item) do
      url = item_path(item)
      {var1, var2} = get_public_request(url)
    end
    

    如果您希望它们并不总是出现在响应中,请使用Kernel.SpecialForms.case/2

    def get_public_request(url) do
      HTTPoison.start
      case HTTPoison.get(url) do
        {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
          case Poison.decode!(body) do
            %{"item" => %{"buy" => buy, "sell" => sell}} -> {buy, sell}
            %{"item" => %{"buy" => buy}} -> {buy, nil}
            %{"item" => %{"sell" => sell}} -> {nil, sell}
            _ -> {nil, nil}
          end
        ...
        end
    end
    

    【讨论】:

      【解决方案2】:

      像 @mudasobwa 一样,我会在 get_public_request 中提取数据,但使用其他两种语法之一:

      地图上的模式匹配

      def get_prices(item), do:
          %{"buy":var1, "sell": var2} = item_path(item) |> get_public_request do 
      
      def get_public_request(url) do
          HTTPoison.start
          case HTTPoison.get(url) do
            {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
              Poison.decode!(body)["item"] |> Map.take(["buy", "sell"])
          ...
          end
      end
      

      注意:如果缺少“buy”或“sell”两个字段之一,此解决方案将在模式匹配期间崩溃。你可以使用case来避免这种情况。

      元组上的模式匹配

      def get_prices(item), do: {var1, var2} = item_path(item) |> get_public_request
      def get_public_request(url) do
          HTTPoison.start
          case HTTPoison.get(url) do
            {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
              Poison.decode!(body)["item"] |> (&({&1["buy"],&1["sell"]}).()
          ...
          end
      end
      

      如果要处理字段中是否有数据的情况,可以添加以下检查:

      Poison.decode!(body)["item"] |> (&({Map.has_key?(&1,"buy") && &1["buy"] || nil, Map.has_key?(&1,"sell") && &1["sell"] || nil}).()
      

      【讨论】: