Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specify the data types for JSON parsing?

Tags:

crystal-lang

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?

like image 842
Kris Avatar asked Sep 13 '15 17:09

Kris


People also ask

What data type does JSON parse return?

The JSON. parse() method parses a string and returns a JavaScript object. The string has to be written in JSON format.

What is the type of JSON parse?

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.

Which data types are available using JSON?

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.


2 Answers

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

like image 100
Jonne Haß Avatar answered Dec 25 '22 20:12

Jonne Haß


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

like image 32
asterite Avatar answered Dec 25 '22 21:12

asterite