Elixir同一结构的多个Jason编码器。

问题描述 投票:0回答:1

假设我正在构建一个版本化的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文档中没有看到任何允许这样做的参考资料。

json elixir jason
1个回答
2
投票

你应该以某种方式告诉 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的问题。


0
投票

你的示例代码提出的问题更多的是关于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在他的回答中指出的那样,利用选项(第二个参数)。

换句话说,你不能改变这个结构的名称。协议 (因为它不在你的控制范围内),但你可以通过提供不同模块的不同名称来改变功能(这就是 做控制)。)

这里简单的解决方案没有协议实现那么华丽。

  1. 你可以简单的为你的不同用例定义JSON视图。
  2. 你可以定制 v1v2 的函数,可将 %User{} 结构的常规映射(包含每个结构所需的任何字段),然后依靠JSON库提供的标准JSON编码(Jason 在你的情况下)和它的编码协议。
© www.soinside.com 2019 - 2024. All rights reserved.