The YAML::Serializable module automatically generates methods for YAML serialization when included.
require "yaml"
class Location
include YAML::Serializable
@[YAML::Field(key: "lat")]
property latitude : Float64
@[YAML::Field(key: "lng")]
property longitude : Float64
end
class House
include YAML::Serializable
property address : String
property location : Location?
end
house = House.from_yaml(%({"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_yaml # => "---\naddress: Crystal Road 1234\nlocation:\n lat: 12.3\n lng: 34.5\n"
houses = Array(House).from_yaml("---\n- address: Crystal Road 1234\n location:\n lat: 12.3\n lng: 34.5\n")
houses.size # => 1
houses.to_yaml # => "---\n- address: Crystal Road 1234\n location:\n lat: 12.3\n lng: 34.5\n" Including YAML::Serializable will create #to_yaml and self.from_yaml methods on the current class, and a constructor which takes a YAML::PullParser. By default, these methods serialize into a yaml 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_yaml and a constructor taking a YAML::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 YAML::Field can be placed on the instance variable. Annotating property, getter and setter macros is also allowed.
require "yaml" class A include YAML::Serializable @[YAML::Field(key: "my_key", emit_null: true)] getter a : Int32? end
YAML::Field properties:
true skip this field in serialization and deserialization (by default false)false). The value can be any Crystal expression and is evaluated at runtime.true skip this field in deserialization (by default false)from_yaml(YAML::ParseContext, YAML::Nodes::Node) and to_yaml(value, YAML::Nodes::Builder). Examples of converters are a Time::Format instance and Time::EpochConverter for Time.true, a @{{key}}_present instance variable will be generated when the key was present (even if it has a null value), false by defaulttrue, emits a null value for nilable property (by default nulls are not emitted)Deserialization also respects default values of variables:
require "yaml"
struct A
include YAML::Serializable
@a : Int32
@b : Float64 = 1.0
end
A.from_yaml("---\na: 1\n") # => A(@a=1, @b=1.0) NOTE YAML::Serializable defines an internal constructor on any including type, which means the default constructor (def initialize; end) is absent unless explicitly defined by the user, even when all instance variables have a default initializer.
YAML::Serializable::Strict and YAML::Serializable::Unmapped.If the YAML::Serializable::Strict module is included, unknown properties in the YAML document will raise a parse exception. By default the unknown properties are silently ignored. If the YAML::Serializable::Unmapped module is included, unknown properties in the YAML document will be stored in a Hash(String, YAML::Any). On serialization, any keys inside yaml_unmapped will be serialized appended to the current yaml object.
require "yaml"
struct A
include YAML::Serializable
include YAML::Serializable::Unmapped
@a : Int32
end
a = A.from_yaml("---\na: 1\nb: 2\n") # => A(@yaml_unmapped={"b" => 2}, @a=1)
a.yaml_unmapped["b"].raw.class # => Int64
a.to_yaml # => "---\na: 1\nb: 2\n" YAML::Serializable::Options
supported properties:
true, emits a null value for all nilable properties (by default nulls are not emitted)require "yaml" @[YAML::Serializable::Options(emit_nulls: true)] class A include YAML::Serializable @a : Int32? end
A very common YAML 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 YAML::Serializable.use_yaml_discriminator for this use case.
after_initialize method#after_initialize is a method that runs after an instance is deserialized from YAML. It can be used as a hook to post-process the initialized object.
Example:
require "yaml"
class Person
include YAML::Serializable
getter name : String
def after_initialize
@name = @name.upcase
end
end
person = Person.from_yaml "---\nname: Jane\n"
person.name # => "JANE" Tells this class to decode YAML by using a field as a discriminator.
Tells this class to decode YAML by using a field as a discriminator.
For example:
require "yaml"
abstract class Shape
include YAML::Serializable
use_yaml_discriminator "type", {point: Point, circle: Circle}
property type : String
end
class Point < Shape
property x : Int32
property y : Int32
end
class Circle < Shape
property x : Int32
property y : Int32
property radius : Int32
end
Shape.from_yaml(%(
type: point
x: 1
y: 2
)) # => #<Point:0x10373ae20 @type="point", @x=1, @y=2>
Shape.from_yaml(%(
type: circle
x: 1
y: 2
radius: 3
)) # => #<Circle:0x106a4cea0 @type="circle", @x=1, @y=2, @radius=3>
© 2012–2026 Manas Technology Solutions.
Licensed under the Apache License, Version 2.0.
https://crystal-lang.org/api/1.19.0/YAML/Serializable.html