Reputation: 19948
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?
Upvotes: 7
Views: 3345
Reputation: 2926
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
Upvotes: 3
Reputation: 4857
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)
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
You can see a slightly more detailed explanation of this problem at https://github.com/manastech/crystal/issues/982#issuecomment-121156428
Upvotes: 10