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

问题描述 投票:3回答:2

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

例如:Poison.encode!(:null)返回“null”。

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

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

Backstory

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

我希望所有:null Atoms都在JSON中编码为null,而不是默认的"null"(作为字符串)。我使用的JSON库是Poison

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

warning: redefining module Poison.Encoder.Atom (current version loaded from _build/dev/lib/poison/ebin/Elixir.Poison.Encoder.Atom.beam)
  lib/atom_encoder.ex:19
elixir
2个回答
2
投票

重新实现内置类型的协议不是一个好主意。您正在使用的其他软件包可能依赖于实现的原始行为。我将使用一个函数,用:null递归替换所有nil值,然后将其传递给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]}}]

2
投票

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

1.考虑使用像%Null{}而不是:null原子这样的东西,并为这个特殊结构实现Poison.Encoder(我无法使用@derive用于此目的):

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的Poison.Encoder:code.delete/1(不推荐)从你的来源强制重装:code.purge/1,并[可选]加强与Protocol.consolidate/2的整合:

: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
© www.soinside.com 2019 - 2024. All rights reserved.