csaintc

csaintc

Dealing with deeply nested structs

Hey friends,

I’m currently writing an integration to an API, and I’m curious of how to deal with deeply nested structs.

I have been writing a bunch of GoLang recently, and I love how all of your data structures end up being extremely well documented because everything is a struct.

I was trying to adopt a similar strategy for my API: Everything being documented and contained in a struct. But! I’m having issues. I end up having to write code like this in all of my modules:

  def to_struct(body = %{"courses" => courses}) do
    
    %__MODULE__{
      courses: Enum.map(courses, fn(course) -> TheApi.Resources.TheStruct.to_struct(course) end)
    }
  end

Having to write boilerplate for constructing complex structs has not been particularly pleasent, and I can’t help but think I’m missing something obvious that would solve my problems.

Thanks a bunch!

Most Liked

michalmuskala

michalmuskala

This sounds like a perfect use case for a data conformance library, such as saul | Hex

IvanR

IvanR

One possible alternative for the nested struct coercion with the following validation of conformance to struct’s @type t() and associated preconditions is with the Jason + Nestru + Domo libraries combo.

Usage example: GitHub - IvanRublev/elixir-decode-validate-json-with-nestru-domo · GitHub

Eiji

Eiji

@csaintc: Here is how you can do it without any extra library:

# example structs:
defmodule Author do
  defstruct contact_email: "", first_name: "", last_name: ""
end
defmodule Comment do
  defstruct author: %Author{}, body: ""
end
defmodule Post do
 defstruct author: %Author{}, body: "", comments: [], title: ""
end

# here is my module with to_struct proposition
defmodule Example do
  # here we are calling to_struct for has_many associations
  def to_struct(list, mod) when is_list(list) and is_atom(mod),
      do: Enum.map(list, &to_struct(&1, mod))
  # here we are calling to_struct for has_one and belongs_to association
  def to_struct(map, key, mod) when is_map(map) and is_atom(key) and is_atom(mod),
      do: Map.update!(map, key, &to_struct(&1, mod))
  # here is custome to_struct for Post, because it has 2 extra relations
  def to_struct(map, Post) do
    Post
    |> struct(map)
    |> to_struct(:author, Author)
    |> to_struct(:comments, Comment)
  end
  # here is custom to_struct for Comment, because it has 1 extra relation
  def to_struct(map, Comment) do
    Comment
    |> struct(map)
    |> to_struct(:author, Author)
  end
  # this applies to any other module - without relation i.e. sub-structs
  def to_struct(map, module) when is_map(map) and is_atom(module),
      do: struct(module, map)
end

# example post data:
post = %{
  author: %{
    contact_email: "michael_smith@example.com",
    first_name: "Michael",
    last_name: "Smith",
  },
  body: "Elixir rules! Elixir is the best! Elixir ...",
  comments: [
    %{
      author: %{
        contact_email: "david_smith@example.com",
        first_name: "David",
        last_name: "Smith",
      },
      body: "Nice article",
    },
    %{
      author: %{
        contact_email: "john_smith@example.com",
        first_name: "John",
        last_name: "Smith",
      },
      body: "John was here!",
    }
  ],
  title: "An awesome title here ...",
}

IO.inspect post
IO.inspect Example.to_struct(post, Post)
# you can also call lists of posts:
Example.to_struct([post], Post)

If I would suggest a library then it would be Ecto, because it also supports validations.

Where Next?

Popular in Questions Top

vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&query=perfume&page=2, I would like to get: ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
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 36432 110
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43806 214
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

We're in Beta

About us Mission Statement