csaintc

csaintc

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!

Showing Posts 1 to 9

OvermindDL1

OvermindDL1

There are libraries out for that, one of the ones I like is ExConstructor. You still have to write the constructor with it, but it is nice. :slight_smile:

You can always write a macro for your specific case though to auto-generate struct mapping code too.

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.

michalmuskala

michalmuskala

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

csaintc

csaintc OP

The solution I’ve come to is using the nested struct coersion that is built into Poison.

It looks like this:

def foo_bar(foo_id, bar_id) do
    foo_id
    |> TheApi.Resources.Bar.url(bar_id)
    |> Poison(%TheApi.Resources.Foo{as: bar_list: [%Bar{}]})
end

And it works exactly like I want it to.

Saul looks great. I’ll 100% be looking into it.

csaintc

csaintc OP

ExConstructor looks nice, but it does not currently support nested coersion, from what I can tell.

csaintc

csaintc OP

Quick question about Saul: Will it convert my maps to named structs?

I like structs because they are NAMED data structured, so my fellow developers know what properties to expect on these maps.

michalmuskala

michalmuskala

I don’t think there’s one built-in, but it’s rather easy to extend it.

OvermindDL1

OvermindDL1

Not by default, but fixable via a macro. :slight_smile:

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

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews