Эликсир декодируется ядом

Я получаю эту строку как результат запроса из моей базы данных:

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

Есть ли способ преобразовать это обратно в карту? Я получаю эту ошибку, расшифровывая ее с помощью яда

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

Я не могу исправить способ добавления данных в базу данных, я должен найти правильный способ для маршрута ключ/значение, чтобы легко извлекать данные из него. (это всего лишь образец для более сложного результата)


person Razinar    schedule 29.11.2017    source источник
comment
Это не JSON. Вы можете использовать eval (Code.eval_string), но обычно это плохая идея. Как вы генерируете эту вещь? Используете inspect? Строка, возвращаемая inspect, не обязательно содержит правильный синтаксис Elixir, хотя в этом примере он есть.   -  person Dogbert    schedule 29.11.2017
comment
Если вы хотите сериализовать структуру или карту Elixir, чтобы сохранить ее в базе данных, лучше использовать пару или :erlang.term_to_binary и :erlang.binary_to_term. В вашем примере вам нужно eval_string это, что, как сказал @Dogbert, не очень хорошая идея по соображениям безопасности (и не только).   -  person Grych    schedule 29.11.2017
comment
Поэтому, если вы не можете исправить способ хранения в базе данных, вам нужно написать синтаксический анализатор для вашей структуры.   -  person Grych    schedule 29.11.2017


Ответы (1)


Как упоминалось в комментариях, вы не должны использовать Code.eval_string. Но есть способ безопасно преобразовать ваш код в структуру Elixir, используя модуль Code:

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

Во-первых, получите AST из строки, но используйте сопоставление с образцом, чтобы убедиться, что это именно та структура, которую вы ищете ({:__aliases__, _, [:Sample, :Struct]}). Весь другой (потенциально вредоносный) код не будет соответствовать этому совпадению:

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"]}]}}

Здесь у вас есть полная структура ast для вас, и keymap. Теперь у вас может возникнуть соблазн использовать eval_quoted с AST, чтобы получить нужную вам структуру:

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"}

Но это все еще небезопасно! Кто-то может поместить в строку функцию, вызывающую побочный эффект, например "%Sample.Struct{list: IO.puts \"Something\"}", которая будет выполняться во время оценки. Поэтому вам нужно сначала проверить keymap, если они содержат безопасные данные.

Или вы можете просто использовать keymap напрямую, ничего не оценивая:

iex(5)> struct(Sample.Struct, keymap)                                                                                    
%Sample.Struct{day: 6, id: "8vfts6", list: [], total: "0.00"}
person Grych    schedule 29.11.2017
comment
Следует отметить, что если код содержит карты или кортежи, простой передачи проанализированного AST будет недостаточно, поскольку их представление в кавычках отличается от их представления без кавычек, например. %{a: {}} становится {:%{}, [line: 1], [a: {:{}, [line: 1], []}]}. - person Dogbert; 30.11.2017
comment
Конечно. Это просто пример для этого конкретного случая, если у него есть данные, отличные от этой конкретной структуры, ему нужно будет сопоставить их отдельно. - person Grych; 30.11.2017