0

I am not able to encode values which I am getting from Ecto query result Poison encode method

Controller code:

def companies(conn, params) do
    companies = Repo.all(
      from(
        c in Company,
        select: {c.name, c.uid},
        limit: 20
      )
    ) 

    conn
    |> put_resp_header("content-type", "application/json; charset=utf-8")
    |> send_resp(200, Poison.encode!(companies, pretty: true))
end

Template code :

<div class="form-group row">
  <%= label f, :company_id, class: "control-label" %>
  <%= select f, :company_id, @companies, class: "form-control"%>
<%= error_tag f, :company_id %>

Error message:

Request: GET /companies
** (exit) an exception was raised:
    ** (Poison.EncodeError) unable to encode value: {"Loews Corporation", 1000285930}
        (poison) lib/poison/encoder.ex:383: Poison.Encoder.Any.encode/2
        (poison) lib/poison/encoder.ex:268: anonymous fn/4 in Poison.Encoder.List.encode/3
        (poison) lib/poison/encoder.ex:269: Poison.Encoder.List."-encode/3-lists^foldr/2-0-"/3
        (poison) lib/poison/encoder.ex:269: Poison.Encoder.List.encode/3
        (poison) lib/poison.ex:41: Poison.encode!/2
Jagdish
  • 752
  • 8
  • 23
  • 1
    I would think it is problem of the tuple you are returning, have you tried with encoding a map instead? something like: `select: %{name: c.name, uid: c.uid}` – Javier Gonzalez Apr 13 '18 at 18:14

2 Answers2

2

Tuples can't be encoded to JSON values by Poison. If you want [{name: ..., uid: ...}, {name: ..., uid: ...}] in the resulting JSON, you can use map() or a map literal in select in the query:

select: map(c, [:name, :uid])

or

select: %{name: c.name, uid: c.uid}
Dogbert
  • 212,659
  • 41
  • 396
  • 397
  • It worked but any idea about the error: protocol Phoenix.HTML.Safe not implemented for %{name: – Jagdish Apr 13 '18 at 17:36
  • <%= label f, :company_id, class: "control-label" %> <%= select f, :company_id, @companies, class: "form-control"%> <%= error_tag f, :company_id %> – Jagdish Apr 13 '18 at 17:49
  • In the code in the question you're sending a JSON response. Where are you calling this template code from? – Dogbert Apr 13 '18 at 18:17
  • So on the same form, I am calling autocomplete search
    <%= label(:user, :country, "Country", class: "control-label col-sm-2") %>
    <%= awesomplete(:user, :country, [class: "form-control"], [ url: rule_set_path(@conn, :companies), loadall: true, prepop: true, minChars: 1, maxItems: 8, value: "name" ]) %>
    – Jagdish Apr 13 '18 at 18:27
  • 1
    It worked after I made changes to form like <%= select f, :company_id, @companies |> Enum.map(&{&1.name, &1.uid}), class: "form-control"%> – Jagdish Apr 13 '18 at 18:55
2

Adding to the @Dogbert’s answer: you also might produce a JSON object by producing a map out of what Ecto returned manually:

companies =
  from(c in Company, select: {c.name, c.uid}, limit: 20)
  |> Repo.all()     # returning list of tuples
  |> Enum.into(%{}) # this

#⇒ %{"Loews Corporation" => 1000285930, "Foo" => 1, "Bar" => 2}

Maps might be encoded to JSON with Poison, producing JS objects.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160