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

The above is useful for dealing with a dynamic JSON structure but is slower than using JSON#mapping.

Generating with JSON::Builder

Use JSON::Builder to generate JSON on the fly by directly emitting data to an IO.

Generating with to_json

to_json and to_json(IO) methods are provided for primitive types, but you need to define to_json(IO) for custom objects, either manually or using JSON#mapping.

Defined in:

json/json.cr
json/mapping.cr

Class Method Summary

Macro Summary

Class Method Detail

def self.parse(input : String | IO) : Any #

Parses a JSON document as a JSON::Any.


[View source]
def self.parse_raw(input : String | IO) : Type #

Parses a JSON document as a JSON::Type.


[View source]

Macro Detail

macro mapping(properties, strict = false) #

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}})

Usage

JSON.mapping must receive a hash literal whose keys will define Crystal properties.

The value of each key can be a single type (not a union 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.

The value can also be another hash literal with the following options:

  • type: (required) the single 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 be Nil
  • default: value to use if the property is missing in the JSON document, or if it's 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.
  • emit_null: if true, emits a null 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) and to_json(value, IO) as class methods. Examples of converters are Time::Format and Time::EpochConverter for Time.

The mapping also automatically defines Crystal properties (getters and setters) for each of the keys. It doesn't define a constructor accepting those 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(IO) method by invoking to_json(IO) on each of the properties (unless a converter is specified, in which case to_json(value, IO) 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.


[View source]