module JSON
Overview
The JSON module allows parsing and generating JSON documents.
Parsing and generating with JSON.mapping
Use JSON.mapping
to define how an object is mapped to JSON, making it
the recommended easy, type-safe and efficient option for parsing and generating
JSON. Refer to that module's documentation to learn about it.
Parsing with 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 but is slower than using JSON.mapping
.
Generating with 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]}>
Generating with 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.mapping
.
Defined in:
json.crjson/builder.cr
json/mapping.cr
json/serialization.cr
Class Method Summary
-
.build(io : IO, indent = nil, &block)
Writes JSON into the given
IO
. -
.build(indent = nil, &block)
Returns the resulting
String
of writing JSON to the yieldedJSON::Builder
. -
.parse(input : String | IO) : Any
Parses a JSON document as a
JSON::Any
.
Macro Summary
-
mapping(_properties_, strict = false)
The
JSON.mapping
macro defines how an object is mapped to JSON. -
mapping(**_properties_)
This is a convenience method to allow invoking
JSON.mapping
with named arguments instead of with a hash/named-tuple literal.
Class Method Detail
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]}>
Macro Detail
The JSON.mapping
macro defines how an object is mapped to JSON.
Example
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}}])
Usage
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:
- type: (required) the type described above (you can use
JSON::Any
too) - key: the property name in the JSON document (as opposed to the property name in the Crystal code)
- nilable: if
true
, the property can beNil
. PassingT?
as a type has the same effect. - default: value to use if the property is missing in the JSON document, or if it's
null
andnilable
was not set totrue
. If the default value creates a new instance of an object (for example[1, 2, 3]
orSomeObject.new
), a different instance will be used each time a JSON document is parsed. - emit_null: if
true
, emits anull
value for nilable properties (by default nulls are not emitted) - converter: specify an alternate type for parsing and generation. The converter must define
from_json(JSON::PullParser)
andto_json(value, JSON::Builder)
as class methods. Examples of converters areTime::Format
andTime::EpochConverter
forTime
. - root: assume the value is inside a JSON object with a given key (see
Object.from_json(string_or_io, root)
) - setter: if
true
, will generate a setter for the variable,true
by default - getter: if
true
, will generate a getter for the variable,true
by default - presence: if
true
, a{{key}}_present?
method will be generated when the key was present (even if it has anull
value),false
by default
This 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.
This is a convenience method to allow invoking JSON.mapping
with named arguments instead of with a hash/named-tuple literal.