假设我正在构建一个版本化的API(作为一个例子,让我们使用一个用户对象)。
%User{
id: "b2507407-891b-486e-aaf8-ba262c16d618"
first_name: "John",
last_name: "Doe",
email: "[email protected]"
}
我最初的想法是为不同版本的API设置多个编码器,我将运行不同的Jason编码器。
defimpl Jason.EncoderV1, for: __MODULE__ do
def encode(user, opts) do
Jason.Encode.map(%{name: "#{user.first_name} #{user.last_name}"}, opts)
end
end
defimpl Jason.EncoderV2, for: __MODULE__ do
def encode(user, opts) do
Jason.Encode.map(%{first_name: first_name, last_name: last_name}, opts)
end
end
我在Jason文档中没有看到任何允许这样做的参考资料。
你应该以某种方式告诉 Jason
你想使用什么版本。Jason.Encoder.encode/2
有第二个参数。
defimpl Jason.Encoder, for: __MODULE__ do
def encode(user, opts) do
{v, opts} = Keyword.pop(opts, :version, :v1)
v
|> case do
:v2 -> %{first_name: first_name, last_name: last_name}
_ -> %{name: "#{user.first_name} #{user.last_name}"}
end
|> Jason.Encode.map(opts)
end
end
并像这样调用它 Jason.encode!(any, version: :v2)
.
旁注。 defimpl
期待定义协议的模块作为第一个参数,不能随便传,比如说不存在的。Jason.EncoderV2
在那里,你的示例代码提出了一个问题,更多的是关于Elixir的问题。
你的示例代码提出的问题更多的是关于Elixir的问题。协议但要小心: Jason包定义了自己的协议 不受制于您的版本管理--也就是说,您对它的实现将始终使用 defimpl Jason.Encoder
.
然而,你可以为不同的结构定义不同的实现,例如,你可以为不同的结构定义不同的实现。
defimpl Jason.Encoder, for: UserV1 do
def encode(user, opts) do
Jason.Encode.map(%{name: "#{user.first_name} #{user.last_name}"}, opts)
end
end
defimpl Jason.Encoder, for: UserV2 do
def encode(user, opts) do
Jason.Encode.map(%{first_name: first_name, last_name: last_name}, opts)
end
end
或者,像Aleksei在他的回答中指出的那样,利用选项(第二个参数)。
换句话说,你不能改变这个结构的名称。协议 (因为它不在你的控制范围内),但你可以通过提供不同模块的不同名称来改变功能(这就是 你 做控制)。)
这里简单的解决方案没有协议实现那么华丽。
v1
和 v2
的函数,可将 %User{}
结构的常规映射(包含每个结构所需的任何字段),然后依靠JSON库提供的标准JSON编码(Jason
在你的情况下)和它的编码协议。