I've got a JSON::Any
, and when I access the "params" key I get a JSON::Any
representing this:
{"a":0.9029593355545088,"b":2,"lh":1000,"ph":10,"pl":1}
I'd like to turn this into a Hash(String, Float64)
and the simplest way I've found to do this is using to_json
and then from_json
since Hash#from_json
doesn't accept the JSON::Any
type.
params = Hash(String, Float64).from_json(json["params"].to_json)
It seems like I'm missing a concept or idiom somewhere. Is there a better way?
PS
If you want to play around with this, here's a snippet:
require "json"
json_string = %({"params": {"a": 1.2, "b": 2}})
json = JSON.parse(json_string)
Hash(String, Float64).from_json(json["params"].to_json)
Please note that in my code I don't have such easy access to json_string
. Because it's buried in a much larger json response, I have to parse it to get down there and once parsed I have a JSON::Any
. Thus it would seem I need to turn it back into a json string before I can turn it into a hash using from_json
.
JSON.parse
returns a JSON::Any
, so you could use its handy casting helpers:
json["params"].as_h.transform_values(&.raw.as(Float64|Int64).to_f)
However if the structure is well defined, it's much better to make use of 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}
Or even better yet:
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)