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.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.cr
json/builder.cr
json/mapping.cr

Class Method Summary

Macro Summary

Class Method Detail

def self.build(io : IO, indent = nil, &block) #

Writes JSON into the given IO. A JSON::Builder is yielded to the block.


[View source]
def self.build(indent = nil, &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]}>

[View source]
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 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 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. Passing T? 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 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, JSON::Builder) as class methods. Examples of converters are Time::Format and Time::EpochConverter for Time.
  • 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 a null 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.


[View source]
macro mapping #

This is a convenience method to allow invoking JSON.mapping with named arguments instead of with a hash/named-tuple literal.


[View source]