jsonelixirdecodeelixir-poison

Elixir decode with Poison


I'm getting this string as query result from my database:

"%Sample.Struct{list: [], total: \"0.00\", day: 6, id: \"8vfts6\"}"

Is there any way to convert this one back to map? I'm getting this error decoding it with poison

** (Poison.SyntaxError) Unexpected token: %
(poison) lib/poison/parser.ex:56: Poison.Parser.parse!/2
(poison) lib/poison.ex:83: Poison.decode!/2

I can't fix the way data is being added to database, i must find a proper way for a key/value route to easily retrive data from that. (this is just a sample for a more complex result)


Solution

  • As it was mentioned in comments, you should not use Code.eval_string. But, there is a way to safely convert your code to Elixir struct, using Code module:

    ex(1)> encoded = "%Sample.Struct{list: [], total: \"0.00\", day: 6, id: \"8vfts6\"}"
    "%Sample.Struct{list: [], total: \"0.00\", day: 6, id: \"8vfts6\"}"
    

    First, get the AST from the string, but use the pattern matching to ensure it is a struct you are looking for ({:__aliases__, _, [:Sample, :Struct]}). All other (potentially malicious) code will fail this match:

    iex(2)> {:ok, {:%, _, [{:__aliases__, _, [:Sample, :Struct]}, {:%{}, _, keymap}]} = ast} = Code.string_to_quoted(encoded)
    {:ok,
     {:%, [line: 1],
      [{:__aliases__, [line: 1], [:Sample, :Struct]},
       {:%{}, [line: 1], [list: [], total: "0.00", day: 6, id: "8vfts6"]}]}}
    

    Here you have the full ast for you struct, and the keymap. You may now be tempted to use eval_quoted with the AST, to get the struct you needed:

    iex(3)> {struct, _} = Code.eval_quoted(ast)
    {%Sample.Struct{day: 6, id: "8vfts6", list: [], total: "0.00"}, []}
    iex(4)> struct
    %Sample.Struct{day: 6, id: "8vfts6", list: [], total: "0.00"}
    

    But it is still not safe! Someone may put a function causing side effect into the string, like "%Sample.Struct{list: IO.puts \"Something\"}", which will be executed during the evaluation. So you will need to check the keymap firsts, if it contain safe data.

    Or you may just use keymap directly, without evaluating anyting:

    iex(5)> struct(Sample.Struct, keymap)                                                                                    
    %Sample.Struct{day: 6, id: "8vfts6", list: [], total: "0.00"}