有没有更惯用的方法将 json 转换为 Crystal 中的特定哈希类型?

Is there a more idiomatic way to cast json to a specific Hash type in Crystal?

我有一个 JSON::Any,当我访问“params”键时,我得到一个 JSON::Any 代表这个:

{"a":0.9029593355545088,"b":2,"lh":1000,"ph":10,"pl":1}

我想把它变成 Hash(String, Float64) 我发现最简单的方法是使用 to_json 然后 from_json 因为 Hash#from_json不接受 JSON::Any 类型。

params = Hash(String, Float64).from_json(json["params"].to_json)

我好像漏掉了一个概念或习语。有没有更好的方法?


PS

如果你想玩这个,这里有一个片段:

require "json"
json_string = %({"params": {"a": 1.2, "b": 2}})
json = JSON.parse(json_string)
Hash(String, Float64).from_json(json["params"].to_json)

请注意,在我的代码中,我无法如此轻松地访问 json_string。因为它隐藏在一个更大的 json 响应中,所以我必须解析它才能到达那里,解析后我有一个 JSON::Any。因此,在使用 from_json.

将其转换为散列之前,我似乎需要将其转换回 json 字符串

JSON.parse returns a JSON::Any,所以你可以使用它方便的转换助手:

json["params"].as_h.transform_values(&.raw.as(Float64|Int64).to_f)

然而,如果结构定义明确,最好使用 JSON::Serializable:

record ApiResponse, params : Hash(String, Float64) do
  include JSON::Serializable
end

json_string = %({"params": {"a": 1.2, "b": 2}})
json = ApiResponse.from_json(json_string)
json.params # => {"a" => 1.2, "b" => 2.0}

甚至更好:

record ApiResponse, params : ApiParams do
  include JSON::Serializable
end

record ApiParams, a : Float64, b : Float64 do
  include JSON::Serializable
end

json_string = %({"params": {"a": 1.2, "b": 2}})
json = ApiResponse.from_json(json_string)
json.params # => ApiParams(@a=1.2, @b=2.0)