Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encoding map to JSON using Poison for use with Slack

I'm using Poison to encode a map to JSON that will send it to the Slack API. This is what Poison gives me:

"{\"text\":\"changed readme fad996e98e04fd4a861840d92bdcbbcb1e1ec296\"}"

When I put that into JSON lint it says it is valid JSON, but Slack responds "invalid payload".

If I change the JSON to look like this

{"text":"changed readme fad996e98e04fd4a861840d92bdcbbcb1e1ec296"}

Then it works. Does anyone know where I'm going wrong with this? Do I need to do extra processing on the encoded JSON or is there some header I need to set?

Here is my controller

def create(conn, opts) do
    message = Message.create_struct(opts)
    response = Slack.Messages.send(message)

    case response do
      {:ok, data} ->
        render conn, json: Poison.encode!(data)
      {:error, reason} ->
        render conn, json: reason
    end
end

Here is part of the library for sending the messages

defmodule Slack.Messages do

  def format_simple_message(map) do
    text = map.description <> " " <> map.commits
    message = %{text: text}
  end

  def post_to_slack(map) do
    Slack.post(:empty, map)
  end

  def send(map) do
    map
    |> format_simple_message
    |> post_to_slack
  end

end

And my HTTPoison processing

defmodule Slack do
  use HTTPoison.Base

  @endpoint "http://url.com"

  def process_url() do
    @endpoint
  end

  def process_response_body(body) do
    body
    |> Poison.decode! # Turns JSON into map
  end

  def process_request_body(body) do
    body
    |> Poison.encode! # Turns map into JSON
  end
end

The part that creates the JSON is in the last block.

like image 468
humdinger Avatar asked Jul 16 '16 19:07

humdinger


People also ask

What is encoding in JSON?

The default encoding is UTF-8. (in §6) JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON is written in UTF-8, JSON is 8bit compatible. When JSON is written in UTF-16 or UTF-32, the binary content-transfer-encoding must be used.

What is JSON encoding and decoding?

The decoder functionality lets you read JSON data, navigate the objects and arrays within the data, and read and get metadata about individual values. This saves you from writing a parser to read and store complex, multi-layered data. The encoder functionality lets you write JSON data in a field-by-field manner.

What is poison elixir?

Poison is a new JSON library for Elixir focusing on wicked-fast speed without sacrificing simplicity, completeness, or correctness. Poison takes several approaches to be the fastest JSON library for Elixir.


1 Answers

It seems your request payload is JSON encoded twice: At first it returns the output string {"text":"..."} and then that string is encoded again. JSON can not only encode objects, but also strings so the encoding the above again will give an output of "{\"text\":\"...\"}". That is, a string that contains the JSON encoded representation of an object.

However, the Slack API expects an object of the form {"text": "..."}, not a string "...". The latter is still valid JSON, but it is not a valid request as far as the API is concerned. That is why you get the “invalid payload” error back.

Where I’m not entirely sure is where the object is encoded a second time. Your code above looks okay, but maybe there are other processing steps that are not in those code snippets. I would suggest you carefully examine your code for a path where two Poison.encode! calls are applied to some data.

There is, however, a workaround for this – although I highly recommend you find and fix the root cause, the double JSON encoding. In process_request_body/1 you could match the argument – and if it is already a string, skip the Poison.encode!. To do so, use the following code:

def process_request_body(body) when is_binary(body), do: body
def process_request_body(body)
  body
  |> Poison.encode! # Turns map into JSON
end

This will pass strings through verbatim and JSON encode anything else. However, this may be dangerous because a string is still a valid input for JSON encoding, so you have to be careful if you want to send pure JSON encoded strings over the API. Again, I recommend fixing the root cause, but depending on your situation, this workaround might also be viable.

like image 125
user1449556 Avatar answered Sep 28 '22 18:09

user1449556