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
相关产品推荐
相关产品推荐

