Elixir - 结构体中的大写键

3

我正在尝试使用Elixir编写一个CLI客户端来连接API系统,以便我可以登录API系统,获取所需的数据进行计算,然后注销。我定义了一个Packet.Login结构体作为我的内部数据结构,用于在解析接收到的JSON后最终得到。

我正在使用Poison来解析JSON。问题在于,由于API返回的属性是大写的,所以我无法匹配打印或解析它们,因为Poison将返回具有这些大写键的映射。问题在于似乎无法像这样使用别名。如果我尝试使用另一种语法,

packet[:Token]

它仍然无法工作,反而给了我一个错误。但是这次是关于 Packet.Login 没有实现 Access 行为。我能理解那一部分,但不是第一个问题。而且我试图保持代码非常简单。

defmodule Packet.Login do
  defstruct [:Data, :Token]
end

defimpl String.Chars, for: Packet.Login do
  def to_string(packet) do
    "Packet:\n---Token:\t\t#{packet.Token}\n---Data:\t#{packet.Data}"
  end
end

loginPacket = Poison.decode!(json, as: %Packet.Login{})
IO.puts "#{loginPacket}"

尝试编译上述代码时,我得到了以下错误信息:
** (CompileError) lib/packet.ex:31: invalid alias: "packet.Token". If you wanted to define an alias, an alias must expand to an atom at compile time but it did not, you may use Module.concat/2 to build it at runtime. If instead you wanted to invoke a function or access a field, wrap the function or field name in double quotes
(elixir) expanding macro: Kernel.to_string/1

有没有什么方法可以解决这个问题?我考虑先解析地图并将所有字段小写,但我不想这样做。
为什么我不能为结构体使用大写键?虽然只要不尝试使用它们似乎是可以的。

1
尝试使用 packet."Token"packet."Data" - Dogbert
哇,没想到这个方法可行。那么,这个键是一个字符串吗?我想知道为什么这是必要的。 - Simon
@Dogbert 给出一个答案并解释原因,这样我就可以接受答案(假设解释足够清楚) :) - Simon
1个回答

6
为了访问以大写字母开头的原子地图字段,您需要将键用引号括起来,例如foo."Bar"或使用方括号语法,例如foo[:Bar]。在Elixir中,foo.Bar被解析为别名。对于结构体,您不能使用方括号语法,因此最简单的方法是在字段名称周围使用引号。因此,在您的代码中,您需要更改:

"Packet:\n---Token:\t\t#{packet.Token}\n---Data:\t#{packet.Data}"

到:

"Packet:\n---Token:\t\t#{packet."Token"}\n---Data:\t#{packet."Data"}"

我在任何地方都没有清晰地找到这个记录,但是 Elixir 的源代码中提到了 某些地方 并且还使用了这种语法来访问一些在 Elixir 中不是有效标识符的 :erlang 函数,例如 :erlang."=<"


有趣的事实:您可以在Elixir中定义仅能使用引用语法调用的函数:

iex(1)> defmodule Foo do
...(1)>   def unquote(:"!@#")(), do: :ok
...(1)> end
iex(2)> Foo."!@#"()
:ok

那个有趣的事实真是太奇怪了,哈哈哈。非常感谢您提供如此迅速的答案。我想您在半分钟内就回答了我的评论,这为我节省了很多麻烦。正如您所说,我没有在任何地方找到这个记录。 - Simon
1
我想我是在很久很久以前阅读 kernel.ex 时看到对 :erlang."<=" 的调用时学习了这个语法。 - Dogbert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接