How to specify data types for parsing JSON?
I have a JSON response, which is a hash array:
[{"project" => {"id" => 1, "name" => "Internal"}, {"project" => {"id" => 2, "name" => "External"}}] My code is as follows:
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 to me that I need to order the elements to use them, otherwise I get the 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 it gives me can't use Hash(K, V) as generic type argument yet, use a more specific type .
How to specify type?
You must use them when accessing 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, you'd better use 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
You continue casting at 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 recommend you use JSON.mapping: https://crystal-lang.org/api/0.22.0/JSON.html#mapping-macro