модуль Psych
Обзор
Psych — это парсер и генератор YAML. Psych использует libyaml [домашняя страница: pyyaml.org/wiki/LibYAML] или [репозиторий git: github.com/yaml/libyaml] для разбора и генерации YAML. Помимо обёртки над libyaml, Psych также умеет сериализовать и десериализовать большинство объектов Ruby в формат YAML и обратно.
МНЕ НУЖНО ПРЯМО СЕЙЧАС РАЗОБРАТЬ ИЛИ СГЕНЕРИРОВАТЬ YAML!
# Parse some YAML
Psych.load("--- foo") # => "foo"
# Emit some YAML
Psych.dump("foo") # => "--- foo\n...\n"
{ :a => 'b'}.to_yaml # => "---\n:a: b\n"
Есть время? Читайте дальше!
Разбор YAML
Psych предоставляет различные интерфейсы для разбора документа YAML — от низкоуровневого до высокоуровневого, в зависимости от ваших потребностей. Самый низкий уровень — событийный парсер. Средний уровень предоставляет доступ к исходному AST YAML, а самый высокий позволяет преобразовывать YAML в объекты Ruby.
Генерация YAML
Psych предоставляет интерфейсы разных уровней для создания документов YAML. Подобно интерфейсам разбора YAML, Psych предоставляет на самом низком уровне событийную систему, на среднем — построение AST YAML, а на самом высоком — преобразование объекта Ruby непосредственно в документ YAML.
Высокоуровневый API
Разбор
Высокоуровневый парсер YAML, предоставляемый Psych, принимает на вход YAML и возвращает структуру данных Ruby. Сведения об использовании высокоуровневого парсера см. в разделе Psych.load.
Чтение из строки
Psych.safe_load("--- a") # => 'a'
Psych.safe_load("---\n - a\n - b") # => ['a', 'b']
# From a trusted string:
Psych.load("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n") # => 0..42
Чтение из файла
Psych.safe_load_file("data.yml", permitted_classes: [Date])
Psych.load_file("trusted_database.yml")
Обработка исключений
begin
# The second argument changes only the exception contents
Psych.parse("--- `", "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
Генерация
Высокоуровневый генератор имеет самый простой интерфейс. Psych принимает структуру данных Ruby и преобразует её в документ YAML. Дополнительные сведения о сериализации структуры данных Ruby см. в разделе Psych.dump.
Запись в строку
# Dump an array, get back a YAML string Psych.dump(['a', 'b']) # => "---\n- a\n- b\n" # Dump an array to an IO object Psych.dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890> # Dump an array with indentation set Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n" # Dump an array to an IO with indentation set Psych.dump(['a', ['b']], StringIO.new, :indentation => 3)
Запись в файл
В настоящее время прямого API для записи структуры Ruby в файл нет:
File.open('database.yml', 'w') do |file|
file.write(Psych.dump(['a', 'b']))
end
Среднеуровневый API
Разбор
Psych предоставляет доступ к AST, полученному при разборе документа YAML. Это дерево строится с помощью Psych::Parser и Psych::TreeBuilder. AST можно свободно изучать и изменять. Дополнительные сведения о работе с синтаксическими деревьями YAML см. в разделах Psych::parse_stream, Psych::Nodes и Psych::Nodes::Node.
Чтение из строки
# Returns Psych::Nodes::Stream
Psych.parse_stream("---\n - a\n - b")
# Returns Psych::Nodes::Document
Psych.parse("---\n - a\n - b")
Чтение из файла
# Returns Psych::Nodes::Stream
Psych.parse_stream(File.read('database.yml'))
# Returns Psych::Nodes::Document
Psych.parse_file('database.yml')
Обработка исключений
begin
# The second argument changes only the exception contents
Psych.parse("--- `", "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
Генерация
На среднем уровне выполняется построение AST. Этот AST в точности совпадает с AST, используемым при разборе документа YAML. Пользователи могут создавать AST вручную, а AST умеет генерировать из себя документ YAML. Дополнительные сведения о построении AST YAML см. в разделах Psych::Nodes, Psych::Nodes::Node и Psych::TreeBuilder.
Запись в строку
# We need Psych::Nodes::Stream (not Psych::Nodes::Document)
stream = Psych.parse_stream("---\n - a\n - b")
stream.to_yaml # => "---\n- a\n- b\n"
Запись в файл
# We need Psych::Nodes::Stream (not Psych::Nodes::Document)
stream = Psych.parse_stream(File.read('database.yml'))
File.open('database.yml', 'w') do |file|
file.write(stream.to_yaml)
end
Низкоуровневый API
Разбор
Парсер самого низкого уровня следует использовать, когда входные данные YAML уже известны и разработчик не хочет тратить ресурсы на построение AST или автоматическое определение и преобразование в объекты Ruby. Дополнительные сведения об использовании событийного парсера см. в разделе Psych::Parser.
Чтение в структуру
parser = Psych::Parser.new(TreeBuilder.new) # => #<Psych::Parser>
parser = Psych.parser # it's an alias for the above
parser.parse("---\n - a\n - b") # => #<Psych::Parser>
parser.handler # => #<Psych::TreeBuilder>
parser.handler.root # => #<Psych::Nodes::Stream>
Получение потока событий
recorder = Psych::Handlers::Recorder.new
parser = Psych::Parser.new(recorder)
parser.parse("---\n - a\n - b")
recorder.events # => [list of [event, args] lists]
# event is one of: Psych::Handler::EVENTS
# args are the arguments passed to the event
Генерация
Генератор самого низкого уровня представляет собой событийную систему. События передаются объекту Psych::Emitter. Этот объект умеет преобразовывать события в документ YAML. Этот интерфейс следует использовать, когда формат документа известен заранее или важна скорость. Дополнительные сведения см. в разделе Psych::Emitter.
Запись в структуру Ruby
Psych.parser.parse("--- a") # => #<Psych::Parser>
parser.handler.first # => #<Psych::Nodes::Stream>
parser.handler.first.to_ruby # => ["a"]
parser.handler.root.first # => #<Psych::Nodes::Document>
parser.handler.root.first.to_ruby # => "a"
# You can instantiate an Emitter manually
Psych::Visitors::ToRuby.new.accept(parser.handler.root.first)
# => "a"
Константы
Общедоступные методы класса
# File ext/psych/lib/psych.rb, line 515
def self.dump o, io = nil, options = {}
if Hash === io
options = io
io = nil
end
visitor = Psych::Visitors::YAMLTree.create options
visitor << o
visitor.tree.yaml io, options
end Сериализует объект Ruby o в строку YAML. Для управления форматом вывода можно передать необязательные options. Если передан объект IO, данные YAML будут записаны в этот объект IO.
В настоящее время поддерживаются следующие параметры:
:indentation-
Количество пробелов для отступа. Допустимое значение должно находиться в диапазоне
0..9, иначе параметр игнорируется.По умолчанию:
2. :line_width-
Максимальное количество символов в строке перед переносом. Для неограниченной ширины строки используйте
-1.По умолчанию:
0(то есть перенос на 81-м символе). :canonical-
Записывать «каноническую» форму
YAML(очень многословную, но строго формальную).По умолчанию:
false. :header-
Записывать
%YAML [version]в начале документа.По умолчанию:
false. :stringify_names-
Записывать ключи-символы в объектах
Hashв виде строк.По умолчанию:
false.
Пример:
# Dump an array, get back a YAML string
Psych.dump(['a', 'b']) # => "---\n- a\n- b\n"
# Dump an array to an IO object
Psych.dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890>
# Dump an array with indentation set
Psych.dump(['a', ['b']], indentation: 3) # => "---\n- a\n- - b\n"
# Dump an array to an IO with indentation set
Psych.dump(['a', ['b']], StringIO.new, indentation: 3)
# Dump hash with symbol keys as string
Psych.dump({a: "b"}, stringify_names: true) # => "---\na: b\n"
# File ext/psych/lib/psych.rb, line 613
def self.dump_stream *objects
visitor = Psych::Visitors::YAMLTree.create({})
objects.each do |o|
visitor << o
end
visitor.tree.yaml
end Сериализует список объектов в отдельные документы потока документов.
Пример:
Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n"
static VALUE libyaml_version(VALUE module)
{
int major, minor, patch;
VALUE list[3];
yaml_get_version(&major, &minor, &patch);
list[0] = INT2NUM(major);
list[1] = INT2NUM(minor);
list[2] = INT2NUM(patch);
return rb_ary_new4((long)3, list);
} Возвращает используемую версию libyaml
# File ext/psych/lib/psych.rb, line 369
def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true
safe_load yaml, permitted_classes: permitted_classes,
permitted_symbols: permitted_symbols,
aliases: aliases,
filename: filename,
fallback: fallback,
symbolize_names: symbolize_names,
freeze: freeze,
strict_integer: strict_integer,
parse_symbols: parse_symbols
end Загружает yaml в структуру данных Ruby. Если передано несколько документов, возвращается объект из первого документа. filename будет использоваться в сообщении об исключении, если при разборе возникнет исключение. Если yaml пуст, возвращается указанное значение fallback, которое по умолчанию равно nil.
При обнаружении синтаксической ошибки YAML возникает исключение Psych::SyntaxError.
Пример:
Psych.load("--- a") # => 'a'
Psych.load("---\n - a\n - b") # => ['a', 'b']
begin
Psych.load("--- `", filename: "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
Если необязательный именованный аргумент symbolize_names имеет истинное значение, ключи в объектах Hash возвращаются как символы (по умолчанию — строки).
Psych.load("---\n foo: bar") # => {"foo"=>"bar"}
Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
Если параметр ‘yaml` равен NilClass, возникает исключение TypeError. Этот метод похож на `safe_load`, но по умолчанию допускает объекты `Symbol`.
# File ext/psych/lib/psych.rb, line 716
def self.load_file filename, **kwargs
File.open(filename, 'r:bom|utf-8') { |f|
self.load f, filename: filename, **kwargs
}
end Загружает документ, содержащийся в filename. Возвращает содержимое filename в виде объекта Ruby. Если файл пуст, возвращает указанное значение fallback, которое по умолчанию равно nil. Параметры см. в описании load.
# File ext/psych/lib/psych.rb, line 644
def self.load_stream yaml, filename: nil, fallback: [], **kwargs
result = if block_given?
parse_stream(yaml, filename: filename) do |node|
yield node.to_ruby(**kwargs)
end
else
parse_stream(yaml, filename: filename).children.map { |node| node.to_ruby(**kwargs) }
end
return fallback if result.is_a?(Array) && result.empty?
result
end Загружает несколько документов, переданных в yaml. Возвращает разобранные документы в виде списка. Если передан блок, каждый документ преобразуется в Ruby и передаётся блоку в процессе разбора.
Пример:
Psych.load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar']
list = []
Psych.load_stream("--- foo\n...\n--- bar\n...") do |ruby|
list << ruby
end
list # => ['foo', 'bar']
# File ext/psych/lib/psych.rb, line 400
def self.parse yaml, filename: nil
parse_stream(yaml, filename: filename) do |node|
return node
end
false
end Разбирает строку YAML в yaml. Возвращает Psych::Nodes::Document. filename используется в сообщении об исключении, если возникает Psych::SyntaxError.
При обнаружении синтаксической ошибки YAML возникает исключение Psych::SyntaxError.
Пример:
Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Document:0x00>
begin
Psych.parse("--- `", filename: "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
Дополнительные сведения об AST YAML см. в разделе Psych::Nodes.
# File ext/psych/lib/psych.rb, line 412
def self.parse_file filename, fallback: false
result = File.open filename, 'r:bom|utf-8' do |f|
parse f, filename: filename
end
result || fallback
end Разбирает файл по адресу filename. Возвращает Psych::Nodes::Document.
При обнаружении синтаксической ошибки YAML возникает исключение Psych::SyntaxError.
# File ext/psych/lib/psych.rb, line 454
def self.parse_stream yaml, filename: nil, &block
if block_given?
parser = Psych::Parser.new(Handlers::DocumentStream.new(&block))
parser.parse yaml, filename
else
parser = self.parser
parser.parse yaml, filename
parser.handler.root
end
end Разбирает строку YAML в yaml. Возвращает Psych::Nodes::Stream. Этот метод может обрабатывать несколько документов YAML, содержащихся в yaml. filename используется в сообщении об исключении, если возникает Psych::SyntaxError.
Если передан блок, узел Psych::Nodes::Document будет передан блоку по мере разбора.
При обнаружении синтаксической ошибки YAML возникает исключение Psych::SyntaxError.
Пример:
Psych.parse_stream("---\n - a\n - b") # => #<Psych::Nodes::Stream:0x00>
Psych.parse_stream("--- a\n--- b") do |node|
node # => #<Psych::Nodes::Document:0x00>
end
begin
Psych.parse_stream("--- `", filename: "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
Если передано значение NilClass, возникает исключение TypeError.
Дополнительные сведения об AST YAML см. в разделе Psych::Nodes.
# File ext/psych/lib/psych.rb, line 421 def self.parser Psych::Parser.new(TreeBuilder.new) end
Возвращает парсер по умолчанию
# File ext/psych/lib/psych.rb, line 596
def self.safe_dump o, io = nil, options = {}
if Hash === io
options = io
io = nil
end
visitor = Psych::Visitors::RestrictedYAMLTree.create options
visitor << o
visitor.tree.yaml io, options
end Безопасно сериализует объект Ruby o в строку YAML. Для управления форматом вывода можно передать необязательные options. Если передан объект IO, данные YAML будут записаны в этот объект IO. По умолчанию сериализовать разрешено только экземпляры следующих классов:
Чтобы разрешить сериализацию произвольных классов, добавьте их в именованный аргумент permitted_classes. Список дополняется. Например, чтобы разрешить сериализацию Date:
Psych.safe_dump(yaml, permitted_classes: [Date])
Теперь можно сериализовать класс Date в дополнение к перечисленным выше классам.
Если объект содержит класс, отсутствующий в списке permitted_classes, будет вызвано исключение Psych::DisallowedClass.
В настоящее время поддерживаются следующие параметры:
:indentation-
Количество пробелов для отступа. Допустимое значение должно находиться в диапазоне
0..9, иначе параметр игнорируется.По умолчанию:
2. :line_width-
Максимальное количество символов в строке перед переносом. Для неограниченной ширины строки используйте
-1.По умолчанию:
0(то есть перенос на 81-м символе). :canonical-
Записывать «каноническую» форму
YAML(очень многословную, но строго формальную).По умолчанию:
false. :header-
Записывать
%YAML [version]в начале документа.По умолчанию:
false. :stringify_names-
Записывать ключи-символы в объектах
Hashв виде строк.По умолчанию:
false.
Пример:
# Dump an array, get back a YAML string
Psych.safe_dump(['a', 'b']) # => "---\n- a\n- b\n"
# Dump an array to an IO object
Psych.safe_dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890>
# Dump an array with indentation set
Psych.safe_dump(['a', ['b']], indentation: 3) # => "---\n- a\n- - b\n"
# Dump an array to an IO with indentation set
Psych.safe_dump(['a', ['b']], StringIO.new, indentation: 3)
# Dump hash with symbol keys as string
Psych.dump({a: "b"}, stringify_names: true) # => "---\na: b\n"
# File ext/psych/lib/psych.rb, line 323
def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true
result = parse(yaml, filename: filename)
return fallback unless result
class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s),
permitted_symbols.map(&:to_s))
scanner = ScalarScanner.new class_loader, strict_integer: strict_integer, parse_symbols: parse_symbols
visitor = if aliases
Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
else
Visitors::NoAliasRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
end
result = visitor.accept result
result
end Безопасно загружает строку yaml из yaml. По умолчанию десериализовать разрешено только экземпляры следующих классов:
Рекурсивные структуры данных по умолчанию запрещены. Чтобы разрешить произвольные классы, добавьте их в именованный аргумент permitted_classes. Список дополняется. Например, чтобы разрешить десериализацию Date:
Psych.safe_load(yaml, permitted_classes: [Date])
Теперь можно загружать класс Date в дополнение к перечисленным выше классам.
Псевдонимы можно явно разрешить, изменив именованный аргумент aliases. Например:
x = [] x << x yaml = Psych.dump x Psych.safe_load yaml # => raises an exception Psych.safe_load yaml, aliases: true # => loads the aliases
Если yaml содержит класс, отсутствующий в списке permitted_classes, будет вызвано исключение Psych::DisallowedClass.
Если yaml содержит псевдонимы, а именованный аргумент aliases имеет значение false, будет вызвано исключение Psych::AliasesNotEnabled.
filename будет использоваться в сообщении об исключении, если при разборе возникнет исключение.
Если необязательный именованный аргумент symbolize_names имеет истинное значение, ключи в объектах Hash возвращаются как символы (по умолчанию — строки).
Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
# File ext/psych/lib/psych.rb, line 705
def self.safe_load_file filename, **kwargs
File.open(filename, 'r:bom|utf-8') { |f|
self.safe_load f, filename: filename, **kwargs
}
end Безопасно загружает документ, содержащийся в filename. Возвращает содержимое filename в виде объекта Ruby. Если файл пуст, возвращает указанное значение fallback, которое по умолчанию равно nil. Параметры см. в разделе safe_load.
# File ext/psych/lib/psych.rb, line 671
def self.safe_load_stream yaml, filename: nil, permitted_classes: [], aliases: false
documents = parse_stream(yaml, filename: filename).children.map do |child|
stream = Psych::Nodes::Stream.new
stream.children << child
safe_load(stream.to_yaml, permitted_classes: permitted_classes, aliases: aliases)
end
if block_given?
documents.each { |doc| yield doc }
nil
else
documents
end
end Загружает несколько документов, переданных в yaml. Возвращает разобранные документы в виде списка.
Пример:
Psych.safe_load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar']
list = []
Psych.safe_load_stream("--- foo\n...\n--- bar\n...") do |ruby|
list << ruby
end
list # => ['foo', 'bar']
# File ext/psych/lib/psych.rb, line 623 def self.to_json object visitor = Psych::Visitors::JSONTree.create visitor << object visitor.tree.yaml end
Сериализует объект Ruby object в строку JSON.
# File ext/psych/lib/psych.rb, line 272 def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true result = parse(yaml, filename: filename) return fallback unless result result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: parse_symbols) end
Загружает yaml в структуру данных Ruby. Если передано несколько документов, возвращается объект из первого документа. filename будет использоваться в сообщении об исключении, если при разборе возникнет исключение. Если yaml пуст, возвращается указанное значение fallback, которое по умолчанию равно false.
При обнаружении синтаксической ошибки YAML возникает исключение Psych::SyntaxError.
Пример:
Psych.unsafe_load("--- a") # => 'a'
Psych.unsafe_load("---\n - a\n - b") # => ['a', 'b']
begin
Psych.unsafe_load("--- `", filename: "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
Если необязательный именованный аргумент symbolize_names имеет истинное значение, ключи в объектах Hash возвращаются как символы (по умолчанию — строки).
Psych.unsafe_load("---\n foo: bar") # => {"foo"=>"bar"}
Psych.unsafe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
Если параметр ‘yaml` равен NilClass, возникает исключение TypeError.
ПРИМЕЧАНИЕ: этот метод *не следует* использовать для разбора ненадёжных документов, например документов YAML, полученных от пользователя. Вместо этого используйте метод load или метод safe_load.
# File ext/psych/lib/psych.rb, line 694
def self.unsafe_load_file filename, **kwargs
File.open(filename, 'r:bom|utf-8') { |f|
self.unsafe_load f, filename: filename, **kwargs
}
end Загружает документ, содержащийся в filename. Возвращает содержимое filename в виде объекта Ruby. Если файл пуст, возвращает указанное значение fallback, которое по умолчанию равно false.
ПРИМЕЧАНИЕ: этот метод *не следует* использовать для разбора ненадёжных документов, например документов YAML, полученных от пользователя. Вместо этого используйте метод safe_load_file.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.