The JSON module allows parsing and generating JSON documents.
The general type-safe interface for parsing JSON is to invoke T.from_json
on a target type T
and pass either a String
or IO
as an argument.
require "json" json_text = %([1, 2, 3]) Array(Int32).from_json(json_text) # => [1, 2, 3] json_text = %({"x": 1, "y": 2}) Hash(String, Int32).from_json(json_text) # => {"x" => 1, "y" => 2}
Serializing is achieved by invoking to_json
, which returns a String
, or to_json(io : IO)
, which will stream the JSON to an IO
.
require "json" [1, 2, 3].to_json # => "[1,2,3]" {"x" => 1, "y" => 2}.to_json # => "{\"x\":1,\"y\":2}"
Most types in the standard library implement these methods. For user-defined types you can define a self.new(pull : JSON::PullParser)
for parsing and to_json(builder : JSON::Builder)
for serializing. The following sections show convenient ways to do this using JSON::Serializable
.
NOTE JSON object keys are always strings but they can still be parsed and deserialized to other types. To deserialize, define a T.from_json_object_key?(key : String) : T?
method, which can return nil
if the string can't be parsed into that type. To serialize, define a to_json_object_key : String
method can be serialized that way. All integer and float types in the standard library can be deserialized that way.
require "json" json_text = %({"1": 2, "3": 4}) Hash(Int32, Int32).from_json(json_text) # => {1 => 2, 3 => 4} {1.5 => 2}.to_json # => "{\"1.5\":2}"
JSON.parse
JSON.parse
will return an Any
, which is a convenient wrapper around all possible JSON types, making it easy to traverse a complex JSON structure but requires some casts from time to time, mostly via some method invocations.
require "json" value = JSON.parse("[1, 2, 3]") # : JSON::Any value[0] # => 1 typeof(value[0]) # => JSON::Any value[0].as_i # => 1 typeof(value[0].as_i) # => Int32 value[0] + 1 # Error, because value[0] is JSON::Any value[0].as_i + 10 # => 11
JSON.parse
can read from an IO
directly (such as a file) which saves allocating a string:
require "json" json = File.open("path/to/file.json") do |file| JSON.parse(file) end
Parsing with JSON.parse
is useful for dealing with a dynamic JSON structure.
JSON.build
Use JSON.build
, which uses JSON::Builder
, to generate JSON by emitting scalars, arrays and objects:
require "json" string = JSON.build do |json| json.object do json.field "name", "foo" json.field "values" do json.array do json.number 1 json.number 2 json.number 3 end end end end string # => %<{"name":"foo","values":[1,2,3]}>
to_json
to_json
, to_json(IO)
and to_json(JSON::Builder)
methods are provided for primitive types, but you need to define to_json(JSON::Builder)
for custom objects, either manually or using JSON::Serializable
.
Writes JSON into the given IO
.
Returns the resulting String
of writing JSON to the yielded JSON::Builder
.
Parses a JSON document as a JSON::Any
.
The JSON.mapping
macro defines how an object is mapped to JSON.
DEPRECATED use JSON::Serializable instead (the legacy behaviour is also available in a shard at github:crystal-lang/json_mapping.cr)
This is a convenience method to allow invoking JSON.mapping
with named arguments instead of with a hash/named-tuple literal.
DEPRECATED use JSON::Serializable instead (the legacy behaviour is also available in a shard at github:crystal-lang/json_mapping.cr)
Writes JSON into the given IO
. A JSON::Builder
is yielded to the block.
Returns the resulting String
of writing JSON to the yielded JSON::Builder
.
require "json" string = JSON.build do |json| json.object do json.field "name", "foo" json.field "values" do json.array do json.number 1 json.number 2 json.number 3 end end end end string # => %<{"name":"foo","values":[1,2,3]}>
The JSON.mapping
macro defines how an object is mapped to JSON.
require "json" class Location JSON.mapping( lat: Float64, lng: Float64, ) end class House JSON.mapping( address: String, location: {type: Location, nilable: true}, ) end house = House.from_json(%({"address": "Crystal Road 1234", "location": {"lat": 12.3, "lng": 34.5}})) house.address # => "Crystal Road 1234" house.location # => #<Location:0x10cd93d80 @lat=12.3, @lng=34.5> house.to_json # => %({"address":"Crystal Road 1234","location":{"lat":12.3,"lng":34.5}}) houses = Array(House).from_json(%([{"address": "Crystal Road 1234", "location": {"lat": 12.3, "lng": 34.5}}])) houses.size # => 1 houses.to_json # => %([{"address":"Crystal Road 1234","location":{"lat":12.3,"lng":34.5}}])
JSON.mapping
must receive a series of named arguments, or a named tuple literal, or a hash literal, whose keys will define Crystal properties.
The value of each key can be a type. Primitive types (numbers, string, boolean and nil) are supported, as well as custom objects which use JSON.mapping
or define a new
method that accepts a JSON::PullParser
and returns an object from it. Union types are supported, if multiple types in the union can be mapped from the JSON, it is undefined which one will be chosen.
The value can also be another hash literal with the following options:
JSON::Any
too)true
, the property can be Nil
. Passing T?
as a type has the same effect.null
and nilable
was not set to true
. If the default value creates a new instance of an object (for example [1, 2, 3]
or SomeObject.new
), a different instance will be used each time a JSON document is parsed.true
, emits a null
value for nilable properties (by default nulls are not emitted)from_json(JSON::PullParser)
and to_json(value, JSON::Builder)
as class methods. Examples of converters are Time::Format
and Time::EpochConverter
for Time
.Object.from_json(string_or_io, root)
)true
, will generate a setter for the variable, true
by defaulttrue
, will generate a getter for the variable, true
by defaulttrue
, a {{key}}_present?
method will be generated when the key was present (even if it has a null
value), false
by defaultThis macro by default defines getters and setters for each variable (this can be overrided with setter and getter). The mapping doesn't define a constructor accepting these variables as arguments, but you can provide an overload.
The macro basically defines a constructor accepting a JSON::PullParser
that reads from it and initializes this type's instance variables. It also defines a to_json(JSON::Builder)
method by invoking to_json(JSON::Builder)
on each of the properties (unless a converter is specified, in which case to_json(value, JSON::Builder)
is invoked).
This macro also declares instance variables of the types given in the mapping.
If strict is true
, unknown properties in the JSON document will raise a parse exception. The default is false
, so unknown properties are silently ignored.
DEPRECATED use JSON::Serializable instead (the legacy behaviour is also available in a shard at github:crystal-lang/json_mapping.cr)
This is a convenience method to allow invoking JSON.mapping
with named arguments instead of with a hash/named-tuple literal.
DEPRECATED use JSON::Serializable instead (the legacy behaviour is also available in a shard at github:crystal-lang/json_mapping.cr)
© 2012–2020 Manas Technology Solutions.
Licensed under the Apache License, Version 2.0.
https://crystal-lang.org/api/0.35.1/JSON.html