Elixir - Nested JSON parsing to structs

2020-06-03 02:47发布

Disclaimer: I've checked the question here and it does not answer mine.

I am trying to come up with a way for nested struct parsing of JSONs. Example:

{"name": "blah blah", "address": {"street": "smthing"}}

I want to reach this result:

%User{name: "blah blah", address: %Address{street: "smthing"}}

Because then it would be easier to plug validation (using Vex for exapmle).

I know that Poison supports the "as struct" option but it does not provide nesting. The above would be parsed:

%User{name: "blah blah", address: %{"street" => "smthing"}}. 

I know I could write a decoder implementation for module User but I guess that is not the intended use case and it would not be generic.

When wondering about an implementation I could not find a way to tell if an atom is a module... maybe I have to go with :code.is_loaded(module_name)?

Anyway, before trying an implementation I would like to know if there is something I am not seeing.

标签: json elixir
2条回答
别忘想泡老子
2楼-- · 2020-06-03 03:00

I believe the above is now possible with Poison:

defmodule User do
  @derive [Poison.Encoder]
  defstruct [:address]
end

defmodule Address do
  @derive [Poison.Encoder]
  defstruct [:street]
end

Poison.decode(response, as: %User{address: %Address{}})
查看更多
3楼-- · 2020-06-03 03:18

Currently, the only option that I am aware of is to provide your own implementation for the Poison.Decoder (note the trailing r) protocol. This has the added benefit that you can have these conversions in one place and just need to write as: User to get the address converted properly. For example:

defimpl Poison.Decoder, for: User do
  def decode(task_list, options) do
    Map.update! task_list, :address, fn address ->
      Poison.Decode.decode(address, Keyword.merge(options, as: [Address]))
    end
  end
end

Note that the inner call to decode is not from Poison.Decoder, but Poison.Decode, without the trailing r in the module name. You will not get an error if you do use the wrong one, it just won't work, which can be a pain to debug.

In the long run, I think a bit of work needs to be done in Poison to make all of this more fun to use. Maybe a macro that will make it easier to write Poison.Decoder implementations for custom structs. Maybe also support for nested struct options like as: %User{address: Address}, that should not be too hard to implement. Also I think the module names Decode and Decoder are too easy to confuse and one of them should be renamed. If I have the time, maybe I will make those suggestions and set up a PR with Poison.

查看更多
登录 后发表回答