module YAML

Overview

The YAML module provides serialization and deserialization of YAML version 1.1 to/from native Crystal data structures, with the additional independent types specified in http://yaml.org/type/

NOTE To use YAML, you must explicitly import it with require "yaml"

Parsing with #parse and #parse_all

YAML.parse will return an Any, which is a convenient wrapper around all possible YAML core types, making it easy to traverse a complex YAML structure but requires some casts from time to time, mostly via some method invocations.

require "yaml"

data = YAML.parse <<-YAML
         ---
         foo:
           bar:
             baz:
               - qux
               - fox
         YAML
data["foo"]["bar"]["baz"][1].as_s # => "fox"

YAML.parse can read from an IO directly (such as a file) which saves allocating a string:

require "yaml"

yaml = File.open("path/to/file.yml") do |file|
  YAML.parse(file)
end

Parsing with from_yaml

A type T can be deserialized from YAML by invoking T.from_yaml(string_or_io). For this to work, T must implement new(ctx : YAML::PullParser, node : YAML::Nodes::Node) and decode a value from the given node, using ctx to store and retrieve anchored values (see YAML::PullParser for an explanation of this).

Crystal primitive types, Time, Bytes and Union implement this method. YAML::Serializable can be used to implement this method for user types.

Dumping with YAML.dump or #to_yaml

YAML.dump generates the YAML representation for an object. An IO can be passed and it will be written there, otherwise it will be returned as a string. Similarly, #to_yaml (with or without an IO) on any object does the same.

For this to work, the type given to YAML.dump must implement to_yaml(builder : YAML::Nodes::Builder).

Crystal primitive types, Time and Bytes implement this method. YAML::Serializable can be used to implement this method for user types.

yaml = YAML.dump({hello: "world"})                               # => "---\nhello: world\n"
File.open("foo.yml", "w") { |f| YAML.dump({hello: "world"}, f) } # writes it to the file
# or:
yaml = {hello: "world"}.to_yaml                               # => "---\nhello: world\n"
File.open("foo.yml", "w") { |f| {hello: "world"}.to_yaml(f) } # writes it to the file

Defined in:

yaml_mapping.cr

Macro Summary

Macro Detail

macro mapping(_properties_, strict = false) #

The YAML.mapping macro defines how an object is mapped to YAML.

It takes named arguments, a named tuple literal or a hash literal as argument, in which attributes and types are defined. Once defined, Object#from_yaml populates properties of the class from the YAML document.

require "yaml"

class Employee
  YAML.mapping(
    title: String,
    name: String,
  )
end

employee = Employee.from_yaml("title: Manager\nname: John")
employee.title # => "Manager"
employee.name  # => "John"

employee.name = "Jenny"
employee.name # => "Jenny"

Attributes not mapped with YAML.mapping are not defined as properties. Also, missing attributes raise a ParseException.

employee = Employee.from_yaml("title: Manager\nname: John\nage: 30")
employee.age # undefined method 'age'. (compile error)

Employee.from_yaml("title: Manager") # raises YAML::ParseException

You can also define attributes for each property.

class Employer
  YAML.mapping(
    title: String,
    name: {
      type:    String,
      nilable: true,
      key:     "firstname",
    },
  )
end

Available attributes:

  • type (required) defines its type. In the example above, title: String is a shortcut to title: {type: String}.
  • nilable defines if a property can be a Nil. Passing T? as a type has the same effect.
  • default: value to use if the property is missing in the YAML 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 YAML document is parsed.
  • key defines which key to read from a YAML document. It defaults to the name of the property.
  • converter takes an alternate type for parsing. It requires a #from_yaml method in that class, and returns an instance of the given type. Examples of converters are Time::Format and Time::EpochConverter for Time.
  • 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 YAML::PullParser that reads from it and initializes this type's instance variables.

This macro also declares instance variables of the types given in the mapping.


[View source]
macro mapping(**_properties_) #

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


[View source]