【问题标题】:Elixir decode with PoisonElixir 用 Poison 解码
【发布时间】:2018-05-13 10:54:27
【问题描述】:

我正在从我的数据库中获取这个字符串作为查询结果:

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

我无法修复将数据添加到数据库的方式,我必须找到一种合适的方式让键/值路由轻松地从中检索数据。 (这只是一个更复杂结果的示例)

【问题讨论】:

  • 这不是 JSON。您可以使用 eval (Code.eval_string) 但这通常是个坏主意。你是如何产生这个东西的?使用inspect? inspect 返回的字符串不一定包含有效的 Elixir 语法,但在本例中包含。
  • 如果你想序列化 Elixir 结构或映射以将其保存在数据库中,更好的方法是使用 pair 或 :erlang.term_to_binary:erlang.binary_to_term。在您的示例中,您需要eval_string 它,正如@Dogbert 所说,出于安全原因(以及更多),这不是一个好主意。
  • 因此,如果您无法修复它在数据库中的存储方式,则需要为您的结构编写解析器。

标签: json elixir decode elixir-poison


【解决方案1】:

正如 cmets 中提到的,您不应该使用 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"}

【讨论】:

  • 需要注意的一点是,如果代码包含映射或元组,仅传递已解析的 AST 是不够的,因为它们的引用表示与未引用的表示不同,例如%{a: {}} 变为 {:%{}, [line: 1], [a: {:{}, [line: 1], []}]}
  • 当然。这只是这个特殊情况的一个例子,如果他有这个特定结构以外的数据,他需要单独匹配它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-31
  • 1970-01-01
  • 2018-06-10
  • 1970-01-01
  • 2016-09-24
  • 1970-01-01
  • 2016-11-19
相关资源
最近更新 更多