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.cryaml/builder.cr
yaml/enums.cr
yaml/mapping.cr
yaml/serialization.cr
yaml.cr
Class Method Summary
-
.build(io : IO, &block)
Writes YAML into the given
IO
. -
.build(&block)
Returns the resulting String of writing YAML to the yielded
YAML::Builder
. -
.dump(object, io : IO)
Serializes an object to YAML, writing it to io.
-
.dump(object) : String
Serializes an object to YAML, returning it as a
String
. -
.libyaml_version : Tuple(Int32, Int32, Int32)
Returns the used version of
libyaml
. -
.parse(data : String | IO) : Any
Deserializes a YAML document according to the core schema.
-
.parse_all(data : String) : Array(Any)
Deserializes multiple YAML documents according to the core schema.
Macro Summary
-
mapping(_properties_, strict = false)
The
YAML.mapping
macro defines how an object is mapped to YAML. -
mapping(**_properties_)
This is a convenience method to allow invoking
YAML.mapping
with named arguments instead of with a hash/named-tuple literal.
Class Method Detail
Writes YAML into the given IO
. A YAML::Builder
is yielded to the 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"
Serializes an object to YAML, returning it as a String
.
Returns the used version of libyaml
.
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"
# => }
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"}]
Macro Detail
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
. PassingT?
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
andnilable
was not set totrue
. If the default value creates a new instance of an object (for example[1, 2, 3]
orSomeObject.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 areTime::Format
andTime::EpochConverter
forTime
. - 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 anull
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.
This is a convenience method to allow invoking YAML.mapping
with named arguments instead of with a hash/named-tuple literal.