JSON -

# module JSON

## Overview

The JSON module allows parsing and generating JSON documents.

### General type-safe interface

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 either `JSON.mapping` or `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}"

### 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:

## 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 yielded `JSON::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

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

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

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

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

Parses a JSON document as a `JSON::Any`.

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

    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 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.

macro mapping(**_properties_) #

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