Elixir 如何将 Map 结构转换为 Record 结构
Elixir How to convert a Map struct to a Record struct
我有一个 Record 结构和一个 Map 结构,例如:
defmodule Foo.Bar do
defstruct boo: nil, baz: nil
end
defmodule Foo do
require Record
Record.defrecord :bar, Foo.Bar, [boo: nil, baz: nil]
end
我可以像这样将 Record 转换为 Map:
defp update_map({k, v}, map), do: Map.update!(map, k, fn(_) -> v end)
defp rd2map(rd) do
Foo.bar(rd) |> Enum.reduce(%Foo.Bar{}, &update_map/2)
end
但是如何将 Map 转换为 Record?
Elixir Records are deprecated。现在存在于 Elixir 中的 Record
模块仅用于两件事:
- 使用简短的内部数据
- 与 Erlang 记录交互
这意味着除非您尝试从 Erlang 文件中提取记录信息,否则您可能不应该使用它们。
关于您最初的问题,以下是我如何来回转换 Erlang Records 和 Elixir Structs。一旦您意识到结构只是一个包含 __struct__: Foo.Bar
的 Map
,而 Record 只是一个以 {Foo.Bar, ...}
开头的元组,这就非常简单了。唯一棘手的一点是有关记录字段的信息仅在编译时可用。因此,默认情况下没有动态构建记录的方法。据我所知,您只能通过将字段定义存储在某处来解决此问题,并使用它来生成结构和记录定义。稍后,相同的源被重新用于构建具有默认值(即记录)的有序元组。请记住,您真的不应该使用记录。所以,请注意:前面有丑陋的骇客 ;-)
defmodule Foo.Bar do
@fields [boo: nil, baz: nil]
def fields, do: @fields
defstruct @fields
end
defmodule Foo do
require Record
Record.defrecord :bar, Foo.Bar, Foo.Bar.fields
end
defmodule Foo.Utils do
require Foo
def record_to_struct(record) do
[{:__struct__, Foo.Bar} | Foo.bar(record)] |> Enum.into(%{})
end
def struct_to_record(struct) do
map = Map.from_struct(struct)
for {key, default} <- Foo.Bar.fields, into: [Foo.Bar] do
Dict.get(map, key, default)
end |> List.to_tuple
end
end
帕特里克的所有免责声明和信息都是正确的。如果不对字段进行注释,您将无法在运行时解决问题。
但是,如果您从 Erlang 记录转换(这通常是使用它们的唯一原因),则可以在编译时解决此问题。我们在 Elixir 源码上做,将 Erlang 的 #file_info{}
转换成 %File.Stat{}
:
https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/file/stat.ex
我有一个 Record 结构和一个 Map 结构,例如:
defmodule Foo.Bar do
defstruct boo: nil, baz: nil
end
defmodule Foo do
require Record
Record.defrecord :bar, Foo.Bar, [boo: nil, baz: nil]
end
我可以像这样将 Record 转换为 Map:
defp update_map({k, v}, map), do: Map.update!(map, k, fn(_) -> v end)
defp rd2map(rd) do
Foo.bar(rd) |> Enum.reduce(%Foo.Bar{}, &update_map/2)
end
但是如何将 Map 转换为 Record?
Elixir Records are deprecated。现在存在于 Elixir 中的 Record
模块仅用于两件事:
- 使用简短的内部数据
- 与 Erlang 记录交互
这意味着除非您尝试从 Erlang 文件中提取记录信息,否则您可能不应该使用它们。
关于您最初的问题,以下是我如何来回转换 Erlang Records 和 Elixir Structs。一旦您意识到结构只是一个包含 __struct__: Foo.Bar
的 Map
,而 Record 只是一个以 {Foo.Bar, ...}
开头的元组,这就非常简单了。唯一棘手的一点是有关记录字段的信息仅在编译时可用。因此,默认情况下没有动态构建记录的方法。据我所知,您只能通过将字段定义存储在某处来解决此问题,并使用它来生成结构和记录定义。稍后,相同的源被重新用于构建具有默认值(即记录)的有序元组。请记住,您真的不应该使用记录。所以,请注意:前面有丑陋的骇客 ;-)
defmodule Foo.Bar do
@fields [boo: nil, baz: nil]
def fields, do: @fields
defstruct @fields
end
defmodule Foo do
require Record
Record.defrecord :bar, Foo.Bar, Foo.Bar.fields
end
defmodule Foo.Utils do
require Foo
def record_to_struct(record) do
[{:__struct__, Foo.Bar} | Foo.bar(record)] |> Enum.into(%{})
end
def struct_to_record(struct) do
map = Map.from_struct(struct)
for {key, default} <- Foo.Bar.fields, into: [Foo.Bar] do
Dict.get(map, key, default)
end |> List.to_tuple
end
end
帕特里克的所有免责声明和信息都是正确的。如果不对字段进行注释,您将无法在运行时解决问题。
但是,如果您从 Erlang 记录转换(这通常是使用它们的唯一原因),则可以在编译时解决此问题。我们在 Elixir 源码上做,将 Erlang 的 #file_info{}
转换成 %File.Stat{}
:
https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/file/stat.ex