通用哈希作为参数?

我正在尝试实现一个接受通用有效负载的类,然后将其转换为JSON:

require "json"

class Response
  alias Payload = Hash(Symbol | String, String | Bool | Int32 | Int64 | Float32 | Float64 | Nil)
  @payload : Payload

  def initialize(@payload, @method : String = "sendMessage")
  end

  def to_json
    @payload.merge({"method" => @method}).to_json
  end
end

Response.new({
  "chat_id" => update.message.not_nil!.chat.id,
  "text"    => "Crystal is awesome but complicated",
})

但是我得到 instance variable '@payload' of Response must be Hash(String | Symbol, Bool | Float32 | Float64 | Int32 | Int64 | String | Nil), not Hash(String, Int64 | String) 编译器错误 . 我怎么能克服这个? Crystal支持通用哈希吗?为什么即使类型重叠也无法转换?

Response 是分片的一部分,所以我不知道哪些哈希值会作为参数传递,但 Payload 必须足够 .

回答(1)

2 years ago

您的有效负载散列的类型为 Hash(String, Int32 | String)

typeof({
  "chat_id" => update.message.not_nil!.chat.id,
  "text"    => "Crystal is awesome but complicated",
}) # => Hash(String, Int32 | String)

但构造函数需要 Hash(Symbol | String, String | Bool | Int32 | Int64 | Float32 | Float64 | Nil) . 这些是不同的类型,不能神奇地铸造 . 您需要确保您的有效负载具有正确的类型 .

一种方法是显式声明散列文字的类型:

Payload{
  "chat_id" => update.message.not_nil!.chat.id,
  "text"    => "Crystal is awesome but complicated",
}

这当然不是很好,但根据您的用例,它可能就足够了 .

如果你想拥有一个允许接收任何类型哈希的通用接口,你需要将它转换为 Payload 类型 . 这意味着将数据复制到该类型的新哈希中:

def self.new(hash, method : String = "sendMessage")
  payload = Payload.new
  hash.each do |key, value|
    payload[key] = value
  end
  new(payload, method)
end

对于一个真实的例子,我在Crinja中使用这种方法将许多不同的类型变体转换为匹配的变体 .