You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Poison无法编码Ecto查询结果的技术问题求助

解决Poison无法编码Ecto查询返回的元组列表问题

你遇到的问题核心是:Ecto查询返回的元组列表不在Poison默认支持的编码类型范围内,咱们来一步步解决它:

问题根源

你在Ecto查询里使用了select: {c.name, c.uid},这会生成类似[{"Acme Corp", "uid_123"}, {"Beta Inc", "uid_456"}]的元组列表。Poison的默认编码器仅支持基本类型、列表和映射(Map),元组不在默认兼容列表里,所以调用Poison.encode!时会触发编码错误。

解决方案(优先推荐第一种)

1. 修改Ecto查询返回映射(Map)

这是最简洁且符合JSON规范的方案,让Ecto直接返回映射列表,Poison可以直接编码这种结构:

def companies(conn, params) do
  companies = Repo.all(
    from(
      c in Company,
      select: %{name: c.name, uid: c.uid},  # 用Map替代元组
      limit: 20
    )
  )
  conn
  |> put_resp_header("content-type", "application/json; charset=utf-8")
  |> send_resp(200, Poison.encode!(companies, pretty: true))
end

编码后的JSON会是结构化的对象数组,前端处理起来非常直观:

[
  {
    "name": "Acme Corp",
    "uid": "uid_123"
  },
  {
    "name": "Beta Inc",
    "uid": "uid_456"
  }
]

2. 手动转换元组为映射

如果无法修改Ecto查询,可以在编码前用Enum.map把元组列表转换成映射列表:

def companies(conn, params) do
  companies = Repo.all(
    from(
      c in Company,
      select: {c.name, c.uid},
      limit: 20
    )
  )
  # 遍历元组列表,逐个转为Map
  formatted_companies = Enum.map(companies, fn {name, uid} -> %{name: name, uid: uid} end)
  
  conn
  |> put_resp_header("content-type", "application/json; charset=utf-8")
  |> send_resp(200, Poison.encode!(formatted_companies, pretty: true))
end

3. 为Poison添加元组编码器

如果需要保留元组结构(比如输出JSON数组的数组),可以注册自定义编码器来处理元组:
在应用启动模块(比如lib/your_app/application.ex)的start函数中添加:

def start(_type, _args) do
  # 注册元组编码器,将元组转为列表后编码
  Poison.Encoder.define(Tuple, fn tuple, options ->
    Poison.Encoder.encode(Tuple.to_list(tuple), options)
  end)

  # 其他启动代码...
end

这样Poison.encode!会把元组转成JSON数组,最终输出如下:

[
  ["Acme Corp", "uid_123"],
  ["Beta Inc", "uid_456"]
]

不过这种方式需要前端通过索引获取字段,不如结构化对象友好,仅推荐有特殊需求时使用。

内容的提问来源于stack exchange,提问作者Jagdish Narayandasani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:37:53