jeromedoyle

jeromedoyle

JSON API response to Structs using Ecto Schema

Does anyone have an example of using Ecto Schemas for parsing JSON api responses?
I’m curious how you can convert this

{
    "id": 283230,
    "fname": "John",
    "lname": "Smith",
    "email": "john.smith@example.com",
    "createdOn": "2016-03-31T10:49:11.386-05:00",
    "updatedOn": "2016-10-31T16:36:12.968-05:00",
    "_embedded": {
      "addresses": [
        {
          "city": "Minneapolis",
          "state": "MN"
        },
        {
          "city": "New Orleans",
          "state": "LA"
        }
      ]
    }
}

into this

%User{
  id: 283230,
  fname: "John",
  lname: "Smith",
  email: "john.smith@example.com",
  createdOn: "2016-03-31T10:49:11.386-05:00",
  updatedOn: "2016-10-31T16:36:12.968-05:00",
  addresses: [
    %Address{
      "city": "Minneapolis",
      "state": "MN"
    },
    %Address{
      "city": "New Orleans",
      "state": "LA"
    }
  ]
}

Most Liked

IvanR

IvanR

One alternative valid way is to use Jason and plain structs with @type t :: ... definitions plus Domo for validation.

Considering MapShaper module from this example app, the structs can be defined like:

defmodule Address do
  @moduledoc false

  use Domo

  defstruct [city: "", state: ""]

  @type t :: %__MODULE__{city: String.t(), state: String.t()}
end

defmodule User do
  @moduledoc false

  use Domo

  today = NaiveDateTime.utc_now()
  defstruct [id: 0, fname: "", lname: "", email: "", createdOn: today, updatedOn: today, addresses: [%Address{}]]

  @type t :: %__MODULE__{
    id: non_neg_integer(),
    fname: String.t(),
    lname: String.t(),
    email: String.t(),
    createdOn: NaiveDateTime.t(),
    updatedOn: NaiveDateTime.t(),
    addresses: [Address.t()]
  }

  defimpl MapShaper.Target do
    def translate_source_map(_value, map) do
      addresses = get_in(map, ["_embedded", "addresses"])
      created_on = map |> get_in(["createdOn"]) |> parse_date()
      updated_on = map |> get_in(["updatedOn"]) |> parse_date()

      map
      |> Map.put("addresses", addresses)
      |> Map.put("createdOn", created_on)
      |> Map.put("updatedOn", updated_on)
    end

    defp parse_date(str) do
      str
      |> NaiveDateTime.from_iso8601()
      |> then(fn
        {:ok, date_time} -> date_time
        _ -> nil
      end)
    end
  end
end

And parsing + validation that looks like:

with {:ok, binary} <- File.read("user.json"),
      {:ok, map} <- Jason.decode(binary),
      user = MapShaper.from_map(%User{}, map),
      {:ok, user} <- User.ensure_type_ok(user) do
  user
end

Returns the following nested struct for the valid input:

%User{
  id: 283230,
  fname: "John",
  lname: "Smith",
  email: "john.smith@example.com",
  createdOn: ~N[2016-03-31 10:49:11.386],
  updatedOn: ~N[2016-10-31 16:36:12.968],
  addresses: [
    %Address{city: "Minneapolis", state: "MN"},
    %Address{city: "New Orleans", state: "LA"}
  ]
}

Where Next?

Popular in Questions Top

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
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
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
Kagamiiiii
Student &amp; New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New

Other popular topics Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
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
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New

We're in Beta

About us Mission Statement