Search code examples
crystal-lang

How to specify the data types for JSON parsing?


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?


Solution

  • 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