How to get struct from map - elixir?

ecto
maps
structs
phoenix

#1

Lets say i have map like this fetching from my database

%{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => "XXXX@gmail.com", "name" => "Hariharasudhan", "password_hash" => "XXXX", "phone" => "XXXX"}

and i have struct like follow
> @primary_key{:id, :binary_id, autogenerate: true}
schema “users” do
field :name, :string
field :email, :string
field :phone, :string
field :password_hash, :string
end’

what is best way to convert Map to User Struct,

note:
i need to do this convertion on each request after authentication , so i need effective way to achieve this?


#2

That’s what https://hexdocs.pm/elixir/Kernel.html#struct/2 does.


#3

@manukall If i try struct(%User{}, %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => "XXXX@gmail.com", "name" => "Hariharasudhan", "password_hash" => "XXXX", "phone" => "XXXX"} ) am getting output like

%User{meta: #Ecto.Schema.Metadata<:built, “users”>, countryCode: nil, email: nil, id: nil, name: nil, password_hash: nil, phone: nil}

it doesn’t set any value from map to struct


#4

Keys from the map must be atoms, as only atoms are allowed in struct definition.


#5

@matteosister yes, but map returned from db has string keys, Is there is any way to conevrt??


#7

I use this code.

with original version coming from:
https://groups.google.com/forum/#!msg/elixir-lang-talk/6geXOLUeIpI/L9einu4EEAAJ


#8

Wow, I’m pretty surprised that this isn’t built in, or at least available as part of Phoenix since incoming json data is not exactly an unexpected occurence in that context. Sure, it’s simple to manually add, and I understand the point about using strings instead of atoms for external data. Still, from an ergonomic point of view it’s super weird because it forces you to repeat your attributes (and makes refactoring harder).

Or maybe I’m missing something? I’m still very much a newb when it comes to Elixir.


#9

Poison let’s you do this ergonomically:

Poison.decode!(~s({"name": "Devin Torres", "age": 27}), as: %Person{})

#10

Many thanks! :pray:t2:

I knew I was missing something.


#11

Ah, as it turns out that won’t work for my purposes. I’m not trying to grab the values from a literal and when if I use inspect to get a string dump of the map, it’s obviously not going to come out as proper JSON. Ah well. I’ll write a clever macro at some point, I reckon.


#12

Untested, but it seems like cast and apply_changes should do what you want:

%User{} |> Ecto.Changeset.cast(map, User.__schema__(:fields)) |> Ecto.Changeset.apply_changes()


#13

I cannot figure it out which solution is better, the one presented or the one José Valim created? Seems they are doing slightly different things.


#14

Or you could use the Morphix library.