将地图转换为struct

时间:2017-02-01 12:54:33

标签: elixir

我正在尝试将地图转换为结构如下:

我有一张地图:

iex(6)> user                      
%{"basic_auth" => "Basic Ym1hOmphYnJhMTc=", "firstname" => "foo",
  "lastname" => "boo"}

该值应该应用于struct:

iex(7)> a = struct(UserInfo, user)
%SapOdataService.Auth.UserInfo{basic_auth: nil, firstname: nil, lastname: nil}

如您所见,struct的值为nil,为什么?

5 个答案:

答案 0 :(得分:2)

我疯狂的猜测是它不起作用,因为你在地图中将键作为字符串,在结构中作为原子。

答案 1 :(得分:2)

您可以尝试exconstructor。它完全符合您的需求。

这是一个小例子:

<controls:BranchFilter Grid.Row="0" BranchId="0">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="BranchChanged">
            <i:InvokeCommandAction Command="{Binding LoadItems}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</controls:BranchFilter>

链接到文档:http://hexdocs.pm/exconstructor/ExConstructor.html

答案 2 :(得分:2)

要扩展JustMichael的答案,您可以先使用String.to_existing_atom/1将密钥转换为原子,然后使用Kernel.struct/2来构建结构:

user_with_atom_keys = for {key, val} <- user, into: %{} do
  {String.to_existing_atom(key), val}
end

user_struct = struct(UserInfo, user_with_atom_keys)
# %UserInfo{basic_auth: "Basic Ym1hOmphYnJhMTc=", firstname: "foo",
 lastname: "boo"}

请注意,这会使用String.to_existing_atom/1来阻止VM达到全局Atom限制。

答案 3 :(得分:2)

我会使用Ecto的变更集:

defmodule UserInfo do
  use Ecto.Schema

  schema "user_info" do
    field :basic_auth, :string
    field :firstname, :string
    field :lastname, :string
  end
end

然后在你的代码中的某个地方

import Ecto.Changeset

user_struct = cast(%UserInfo{}, 
                   your_user_map, 
                   UserInfo.__schema__(:fields)) 
              |> apply_changes
使用ecto的好处在于你还有额外的好处,比如验证,你不必担心地图中的字段是字符串或原子。

答案 4 :(得分:0)

  %{"basic_auth" => "Basic Ym1hOmphYnJhMTc=", "firstname" => "foo", "lastname" => "boo"}
  |> Poison.encode
  |> (fn {:ok, json} -> json end).()
  |> Poison.decode(as: %SapOdataService.Auth.UserInfo{})

  ~S({"basic_auth":"Basic Ym1hOmphYnJhMTc=","firstname":"foo","lastname":"boo"})
  |> Poison.decode(as: %SapOdataService.Auth.UserInfo{})

请注意,这不会与@enforce_keys上的UserInfo一起编译。