module JSON::Serializable


The JSON::Serializable module automatically generates methods for JSON serialization when included.


require "json"

class Location
  include JSON::Serializable

  @[JSON::Field(key: "lat")]
  property latitude : Float64

  @[JSON::Field(key: "lng")]
  property longitude : Float64

class House
  include JSON::Serializable
  property address : String
  property location : Location?

house = House.from_json(%({"address": "Crystal Road 1234", "location": {"lat": 12.3, "lng": 34.5}}))
house.address  # => "Crystal Road 1234"
house.location # => #<Location:0x10cd93d80 @latitude=12.3, @longitude=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}}])


Including JSON::Serializable will create #to_json and self.from_json methods on the current class, and a constructor which takes a JSON::PullParser. By default, these methods serialize into a json object containing the value of every instance variable, the keys being the instance variable name. Most primitives and collections supported as instance variable values (string, integer, array, hash, etc.), along with objects which define to_json and a constructor taking a JSON::PullParser. Union types are also supported, including unions with nil. If multiple types in a union parse correctly, it is undefined which one will be chosen.

To change how individual instance variables are parsed and serialized, the annotation JSON::Field can be placed on the instance variable. Annotating property, getter and setter macros is also allowed.

require "json"

class A
  include JSON::Serializable

  @[JSON::Field(key: "my_key", emit_null: true)]
  getter a : Int32?

JSON::Field properties:

  • ignore: if true skip this field in serialization and deserialization (by default false)
  • key: the value of the key in the json object (by default the name of the instance variable)
  • root: assume the value is inside a JSON object with a given key (see Object.from_json(string_or_io, root))
  • 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.
  • presence: if true, a @{{key}}_present instance variable will be generated when the key was present (even if it has a null value), false by default
  • emit_null: if true, emits a null value for nilable property (by default nulls are not emitted)

Deserialization also respects default values of variables:

require "json"

struct A
  include JSON::Serializable
  @a : Int32
  @b : Float64 = 1.0

A.from_json(%<{"a":1}>) # => A(@a=1, @b=1.0)

Extensions: JSON::Serializable::Strict and JSON::Serializable::Unmapped.

If the JSON::Serializable::Strict module is included, unknown properties in the JSON document will raise a parse exception. By default the unknown properties are silently ignored. If the JSON::Serializable::Unmapped module is included, unknown properties in the JSON document will be stored in a Hash(String, JSON::Any). On serialization, any keys inside json_unmapped will be serialized and appended to the current json object.

require "json"

struct A
  include JSON::Serializable
  include JSON::Serializable::Unmapped
  @a : Int32

a = A.from_json(%({"a":1,"b":2})) # => A(@json_unmapped={"b" => 2_i64}, @a=1)
a.to_json                         # => {"a":1,"b":2}

Class annotation JSON::Serializable::Options

supported properties:

  • emit_nulls: if true, emits a null value for all nilable properties (by default nulls are not emitted)
require "json"

@[JSON::Serializable::Options(emit_nulls: true)]
class A
  include JSON::Serializable
  @a : Int32?

Discriminator field

A very common JSON serialization strategy for handling different objects under a same hierarchy is to use a discriminator field. For example in GeoJSON each object has a "type" field, and the rest of the fields, and their meaning, depend on its value.

You can use JSON::Serializable.use_json_discriminator for this use case.

Defined in:



Instance Method Summary

Macro Summary

Constructor Detail

def self.new(*, __pull_for_json_serializable pull : JSON::PullParser)Source

Instance Method Detail

def to_json(json : JSON::Builder)Source

Macro Detail

macro use_json_discriminator(field, mapping)Source

Tells this class to decode JSON by using a field as a discriminator.

  • field must be the field name to use as a discriminator
  • mapping must be a hash or named tuple where each key-value pair maps a discriminator value to a class to deserialize

For example:

require "json"

abstract class Shape
  include JSON::Serializable

  use_json_discriminator "type", {point: Point, circle: Circle}

  property type : String

class Point < Shape
  property x : Int32
  property y : Int32

class Circle < Shape
  property x : Int32
  property y : Int32
  property radius : Int32

Shape.from_json(%({"type": "point", "x": 1, "y": 2}))               # => #<Point:0x10373ae20 @type="point", @x=1, @y=2>
Shape.from_json(%({"type": "circle", "x": 1, "y": 2, "radius": 3})) # => #<Circle:0x106a4cea0 @type="circle", @x=1, @y=2, @radius=3>

© 2012–2020 Manas Technology Solutions.
Licensed under the Apache License, Version 2.0.