I have a JSON response which is an Array of Hash:
[{"project" => {"id" => 1, "name" => "Internal"},
{"project" => {"id" => 2, "name" => "External"}}]
My code looks like this:
client = HTTP::Client.new(url, ssl: true)
response = client.get("/projects", ssl: true)
projects = JSON.parse(response.body) as Array
This gives me an Array but it seems I need to typecast the elements to actually use them otherwise I get undefined method '[]' for Nil (compile-time type is (Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Type) | Array(JSON::Type)))
.
I tried as Array(Hash)
but this gives me can't use Hash(K, V) as generic type argument yet, use a more specific type
.
How do I specify the type?
The JSON. parse() method parses a string and returns a JavaScript object. The string has to be written in JSON format.
JSON.parse() The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string. An optional reviver function can be provided to perform a transformation on the resulting object before it is returned.
At the granular level, JSON consists of 6 data types. The first four data types (string, number, boolean and null) can be referred to as simple data types. The other two data types (object and array) can be referred to as complex data types.
You have to cast these as you access the elements:
projects = JSON.parse(json).as(Array)
project = projects.first.as(Hash)["project"].as(Hash)
id = project["id"].as(Int64)
http://carc.in/#/r/f3f
But for well structured data like this you're better off with using JSON.mapping
:
class ProjectContainer
JSON.mapping({
project: Project
})
end
class Project
JSON.mapping({
id: Int64,
name: String
})
end
projects = Array(ProjectContainer).from_json(json)
project = projects.first.project
pp id = project.id
http://carc.in/#/r/f3g
You can see a slightly more detailed explanation of this problem at https://github.com/manastech/crystal/issues/982#issuecomment-121156428
You continue casting in every step:
projects = JSON.parse(response.body) as Array
projects.each do |project|
project = project as Hash
project = project["project"] as Hash
id = project["id"] as Int
name = project["name"] as String
end
But if your API response has a well-known structure, I strongly suggest you use JSON.mapping: https://crystal-lang.org/api/0.22.0/JSON.html#mapping-macro
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With