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/

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 <<-END
         ---
         foo:
           bar:
             baz:
               - qux
               - fox
         END
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.mapping 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.mapping 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/any.cr
yaml/builder.cr
yaml/enums.cr
yaml/mapping.cr
yaml/serialization.cr
yaml.cr

Class Method Summary

Macro Summary

Class Method Detail

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

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


[View source]
def self.build(&block) #

Returns the resulting String of writing YAML to the yielded YAML::Builder.

require "yaml"

string = YAML.build do |yaml|
  yaml.mapping do
    yaml.scalar "foo"
    yaml.sequence do
      yaml.scalar 1
      yaml.scalar 2
    end
  end
end
string # => "---\nfoo:\n- 1\n- 2\n"

[View source]
def self.dump(object, io : IO) #

Serializes an object to YAML, writing it to io.


[View source]
def self.dump(object) : String #

Serializes an object to YAML, returning it as a String.


[View source]
def self.parse(data : String | IO) : Any #

Deserializes a YAML document according to the core schema.

# ./foo.yml
data:
  string: "foobar"
  array:
    - John
    - Sarah
  hash: {key: value}
  paragraph: |
    foo
    bar
require "yaml"

YAML.parse(File.read("./foo.yml"))
# => {
# => "data" => {
# => "string" => "foobar",
# => "array" => ["John", "Sarah"],
# => "hash" => {"key" => "value"},
# => "paragraph" => "foo\nbar\n"
# => }

[View source]
def self.parse_all(data : String) : Array(Any) #

Deserializes multiple YAML documents according to the core schema.

# ./foo.yml
foo: bar
---
hello: world
require "yaml"

YAML.parse_all(File.read("./foo.yml"))
# => [{"foo" => "bar"}, {"hello" => "world"}]

[View source]

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 #

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


[View source]