IvanR

IvanR

Nestru: serialize maps to nested structs

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.

There are several possible data validation options:

  • with ex_json_schema applied to the map before passing to Nestru
  • within Nestru gather_fields_map/3 and from_map_hint/3 callbacks
  • with Ecto schemaless changesets applied to the struct
  • with Domo applied to the struct to validate the struct conformance to its @type t() and associated preconditions

See typical usage in Readme.md via:

Run in Livebook

Shortly it looks like:

defmodule Order do
  @derive [
    Nestru.Encoder,
    {Nestru.Decoder, %{items: [LineItem], total: Total}}
  ]
  # Gave a hint to Nestru on how to process the items and total fields
  # others go to a struct as is.

  defstruct [:id, :items, :total]
end

defmodule LineItem do
  @derive [Nestru.Encoder, Nestru.Decoder]
  defstruct [:amount]
end

defmodule Total do
  @derive [Nestru.Encoder, Nestru.Decoder]
  defstruct [:sum]
end

map = %{
  "id" => "A548",
  "items" => [%{"amount" => 150}, %{"amount" => 350}],
  "total" => %{"sum" => 500}
}

{:ok, model} = Nestru.decode_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.encode_to_map(model)

returns:

%{
  id: "A548",
  items: [%{amount: 150}, %{amount: 350}],
  total: %{sum: 500}
}

More information here:

Most Liked

IvanR

IvanR

0.2.0 - August 16, 2022

  • Fix to ensure the module is loaded before checking if it’s a struct
  • Add decode and encode verbs to function names
  • Support [Module] hint in the map returned from from_map_hint to decode the list of structs
  • Support %{one_key: :other_key} mapping configuration for the PreDecoder protocol in @derive attribute.

Now, for most common decoding cases, it’s possible to configure decoding fully in a declarative way by providing param maps when deriving protocols like that:

defmodule Order do
  @derive [
    {Nestru.PreDecoder, %{"attrs" => :total}},
    {Nestru.Decoder, %{total: Total}}
  ]

  defstruct [:id, :total]
end

defmodule Total do
  @derive Nestru.Decoder
  
  defstruct [:sum]
end

so the decoding works like that:

Nestru.decode_from_map(%{"attrs" => %{"sum" => 568}, "id" => "A874"}, Order)

{:ok, %Order{id: "A874", total: %Total{sum: 568}}}
IvanR

IvanR

0.2.1 - August 20, 2022

  • Fix decode_from_map(!)/2/3 to return the error for not a map value
IvanR

IvanR

0.1.1 - November 13, 2021

  • Add has_key?/2 and get/3 map functions that lookup keys in maps both in a binary or an atom form.

  • Add the link to open Readme.md in LiveBook :slightly_smiling_face:

Last Post!

IvanR

IvanR

0.3.1 - November 22, 2022

  • Add :only and :except options for deriving of Nestru.Encoder protocol (hommage for Jason.Encoder approach :slightly_smiling_face:)
  • Add explicit :translate option for deriving of Nestru.PreDecoder protocol
  • Add explicit :hint option for deriving of Nestru.Decoder protocol

See the documentation for examples.

Where Next?

Popular in Announcing Top

zorbash
I created Kitto a framework for dashboards inspired by Dashing. The distributed characteristics of Elixir and the low memory footprint...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story &lt;- Meeseeks.all(html, css("tr.athing")) do...
New
Azolo
Hey everyone, I just released WebSockex which is a Elixir WebSocket client. WebSockex strives to work as a OTP special process, be RFC6...
New
tmbb
PhoenixWS - Websockets over Phoenix Channels Source code on Github here: GitHub - tmbb/phoenix_ws: Websockets implemented over Phoenix Ch...
New
kevinlang
Hey all, We have made an Ecto3 Adapter for SQLite3, ecto_sqlite3! We have successfully on-boarded the full suite of integration tests (...
New
trisolaran
Hi! :waving_hand: I would like to present LiveSelect, a little library that I wrote to easily add a dynamic selection input to your LV f...
198 11220 107
New
type1fool
WebAuthnLiveComponent WebAuthnComponents See this post about renaming the package. Passwordless authentication for Phoenix LiveView app...
New

Other popular topics Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36654 110
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 44532 311
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

We're in Beta

About us Mission Statement