如何扩展Elixir中的现有协议?

时间:2017-12-19 21:45:26

标签: elixir

如何在Elixir中正确扩展现有协议?

例如:00:00:00.000返回" null"。

Poison.encode!(:null)协议为例。我想为Poison.Encoder的议定书增加一个内容。但如果不重新定义本议定书中的所有内容,我不知道如何做到这一点。

Atom

背景故事

嗨,我遇到了JSON编码的一个特殊问题,这引出了我的问题。

我希望所有defimpl Poison.Encoder, for: Atom do alias Poison.Encoder def encode(nil, _), do: "null" def encode(true, _), do: "true" def encode(false, _), do: "false" # /------------------------------------------------------ # V My addition - What is the correct way of doing this? def encode(:null, _), do: "null" def encode(atom, options) do Encoder.BitString.encode(Atom.to_string(atom), options) end end 原子在JSON中编码为:null,而不是默认的null(作为字符串)。我使用的JSON库是"null"

现在,上述工作,但它吐出警告,如:

Poison

2 个答案:

答案 0 :(得分:2)

无法在Elixir中扩展现有协议。可能的解决方法是:

1。考虑使用类似%Null{}而不是:null atom的内容,并为此特定结构实施Poison.Encoder(我无法使用{{3}为此目的):

defmodule Null do
  # @derive [Poison.Encoder] # that won’t work
  defstruct [json: "null"]

  def encode(%Null{json: json}), do: json
end
defimpl Poison.Encoder, for: Null do
  def encode(%Null{} = null), do: Null.encode(null)
end

2。在加载模块之前,有人可能会使用Erlang的@derive:code.delete/1(不推荐)强制重新加载Poison.Encoder来源{[1}} ]用:code.purge/1加强整合:

:code.ensure_loaded(Poison.Encoder)
Protocol.assert_protocol!(Poison.Encoder)
:code.ensure_loaded(Poison.Encoder.Atom)
:code.delete(Poison.Encoder.Atom)
:code.purge(Poison.Encoder.Atom)

# your implementation
defimpl Poison.Encoder, for: Atom do
  ...
end

答案 1 :(得分:2)

重新实现内置类型的协议并不是一个好主意。您正在使用的其他软件包可能依赖于实现的原始行为。我将使用递归替换所有:nullnil的函数,然后将其传递给Poison.encode。您可以创建一个包装函数来执行此转换,然后根据需要调用Poison.encode。以下是此基本实现:

defmodule A do
  def null_to_nil(:null), do: nil
  def null_to_nil(list) when is_list(list) do
    Enum.map(list, &null_to_nil/1)
  end
  def null_to_nil(map) when is_map(map) do
    Enum.map(map, fn {k, v} -> {k, null_to_nil(v)} end) |> Map.new
  end
  def null_to_nil(term), do: term
end

IO.inspect A.null_to_nil([1, 2, :null, %{a: :null, b: %{b: [:null, :null]}}])

输出:

[1, 2, nil, %{a: nil, b: %{b: [nil, nil]}}]