The Nestru library can convert a map of any shape into a model of nested structs according to given hints. And It can convert any nested struct into a map.
The library’s primary purpose is to serialize between JSON map and an application’s model that is a struct having other structs nested in it.
It comes from the idea to have no DSL and less custom code to parse JSONs as an alternative to full-fledged Ecto with schemaless changesets.
Works good in Jason <-> Nestru
pair. The validation of the input can be done with pattern matching in Nestru callbacks. Or automated with using Domo library that validates the struct conformance to its @type t()
and associated precondition.
See typical usage in Readme.md via:
Shortly it looks like:
defmodule Order do
@derive Nestru.Encoder
defstruct [:id, :items, :total]
# Giving a hint to Nestru how to process the items list of structs
# and the total struct, other fields go to struct as is.
defimpl Nestru.Decoder do
def from_map_hint(_value, _context, _map) do
{:ok, %{
items: &Nestru.from_list_of_maps(&1, LineItem),
total: Total
}}
end
end
end
defmodule LineItem do
@derive [Nestru.Decoder, Nestru.Encoder]
defstruct [:amount]
end
defmodule Total do
@derive [Nestru.Decoder, Nestru.Encoder]
defstruct [:sum]
end
map = %{
"id" => "A548",
"items" => [%{"amount" => 150}, %{"amount" => 350}],
"total" => %{"sum" => 500}
}
{:ok, model} = Nestru.from_map(map, Order)
returns:
{:ok,
%OrderA{
id: "A548",
items: [%LineItemA{amount: 150}, %LineItemA{amount: 350}],
total: %Total{sum: 500}
}}
And going back to the map is as simple as that:
map = Nestru.to_map(model)
returns:
%{
id: "A548",
items: [%{amount: 150}, %{amount: 350}],
total: %{sum: 500}
}
More information here: