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

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
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
pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
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
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
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
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
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
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
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
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
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
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
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement