Convert string representation of an array of json objects to map Elixir

Cyzanfar Source

Here I have a payload coming to my controller action endpoint:

%{      
  "mandrill_events" => "[{\"event\":\"send\",\"msg\":{\"ts\":1365109999,\"subject\":\"This an example webhook message\",\"email\":\"[email protected]\",\"sender\":\"[email protected]\",\"tags\":[\"webhook-example\"],\"opens\":[],\"clicks\":[],\"state\":\"sent\",\"metadata\":{\"user_id\":111},\"_id\":\"exampleaaaaaaaaaaaaaaaaaaaaaaaaa\",\"_version\":\"exampleaaaaaaaaaaaaaaa\"},\"_id\":\"exampleaaaaaaaaaaaaaaaaaaaaaaaaa\",\"ts\":1518203456},{\"event\":\"send\",\"msg\":{\"ts\":1365109999,\"subject\":\"This an example webhook message\",\"email\":\"[email protected]\",\"sender\":\"[email protected]\",\"tags\":[\"webhook-example\"],\"opens\":[],\"clicks\":[],\"state\":\"sent\",\"metadata\":{\"user_id\":111},\"_id\":\"exampleaaaaaaaaaaaaaaaaaaaaaaaaa1\",\"_version\":\"exampleaaaaaaaaaaaaaaa\"},\"_id\":\"exampleaaaaaaaaaaaaaaaaaaaaaaaaa1\",\"ts\":1518203456}]"
}

I am trying to decode the content of mandrill_events, so that I can then access some values, but I think the bracket is throwing it off.

get_in(payload, ["mandrill_events"]) |> Base.url_decode64 |> Poison.decode!

But that didn't work either.

** (ArgumentError) argument error
    :erlang.iolist_to_binary(:error)
    (poison) lib/poison/parser.ex:35: Poison.Parser.parse/2
    (poison) lib/poison/parser.ex:51: Poison.Parser.parse!/2
    (poison) lib/poison.ex:83: Poison.decode!/2
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    (iex) lib/iex/evaluator.ex:250: IEx.Evaluator.handle_eval/5
    (iex) lib/iex/evaluator.ex:230: IEx.Evaluator.do_eval/3
    (iex) lib/iex/evaluator.ex:208: IEx.Evaluator.eval/3
    (iex) lib/iex/evaluator.ex:94: IEx.Evaluator.loop/1
    (iex) lib/iex/evaluator.ex:24: IEx.Evaluator.init/4
jsonelixirphoenix-frameworkelixir-poison

Answers

answered 3 months ago Kevin Johnson #1

Short answer: get_in(a, ["mandrill_events"]) |> Poison.decode! should give you what you want.

The reason as to why the answer provided here includes the operation |> Base.url_decode64 is because that question was dealing with base64 encoded payloads. Base64 encoding is simply a way of mapping a payload to a subset of the ASCII characters that are guaranteed to be recognized by every router, so that the payload value does not get corrupted when in transit.

For example, you could do:

get_in(a, ["mandrill_events"]) |> Base.url_encode64

which will render something like this:

"W3siZXZlbnQiOiJzZW5kIiwibXNnIjp7InRzIjoxMzY1MTA5OTk5LCJzdWJqZWN0IjoiVGhpcyBhbiBleGFtcGxlIHdlYmhvb2sgbWVzc2FnZSIsImVtYWlsIjoiZXhhbXBsZS53ZWJob29rQG1hbmRyaWxsYXBwLmNvbSIsInNlbmRlciI6ImV4YW1wbGUuc2VuZGVyQG1hbmRyaWxsYXBwLmNvbSIsInRhZ3MiOlsid2ViaG9vay1leGFtcGxlIl0sIm9wZW5zIjpbXSwiY2xpY2tzIjpbXSwic3RhdGUiOiJzZW50IiwibWV0YWRhdGEiOnsidXNlcl9pZCI6MTExfSwiX2lkIjoiZXhhbXBsZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLCJfdmVyc2lvbiI6ImV4YW1wbGVhYWFhYWFhYWFhYWFhYWEifSwiX2lkIjoiZXhhbXBsZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLCJ0cyI6MTUxODIwMzQ1Nn0seyJldmVudCI6InNlbmQiLCJtc2ciOnsidHMiOjEzNjUxMDk5OTksInN1YmplY3QiOiJUaGlzIGFuIGV4YW1wbGUgd2ViaG9vayBtZXNzYWdlIiwiZW1haWwiOiJleGFtcGxlLndlYmhvb2tAbWFuZHJpbGxhcHAuY29tIiwic2VuZGVyIjoiZXhhbXBsZS5zZW5kZXJAbWFuZHJpbGxhcHAuY29tIiwidGFncyI6WyJ3ZWJob29rLWV4YW1wbGUiXSwib3BlbnMiOltdLCJjbGlja3MiOltdLCJzdGF0ZSI6InNlbnQiLCJtZXRhZGF0YSI6eyJ1c2VyX2lkIjoxMTF9LCJfaWQiOiJleGFtcGxlYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYTEiLCJfdmVyc2lvbiI6ImV4YW1wbGVhYWFhYWFhYWFhYWFhYWEifSwiX2lkIjoiZXhhbXBsZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWExIiwidHMiOjE1MTgyMDM0NTZ9XQ=="

When you are dealing with base64 encoded payload, you will need to first decode it so you get a JSON string which in turn you can deserialize using Poison.

As a full sanity test, the following would work as well:

get_in(a, ["mandrill_events"]) |> Base.url_encode64 |> Base.url_decode64 |> Poison.decode!

Of course, if the string is not base64 encoded, and you try to base64 decode it accordingly as you currently are doing, then it will throw an :error which Poison does not know how to convert to an elixir term as its input is to be a JSON string, not an atom

comments powered by Disqus