模块 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 文档的接口,从低级到高级,具体取决于您的解析需求。在最低级别,是基于事件的解析器。中级是访问原始 YAML AST,在最高级别,是将 YAML 反序列化为 Ruby 对象的能力。

YAML 生成

Psych 提供了一系列从低级到高级的接口,用于生成 YAML 文档。与 YAML 解析接口非常相似,Psych 在最低级别提供了基于事件的系统,中级是构建 YAML AST,最高级别是将 Ruby 对象直接转换为 YAML 文档。

高级 API

解析

Psych 提供的高级 YAML 解析器只需将 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")

Exception 处理

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 提供对从解析 YAML 文档生成的 AST 的访问。此树是使用 Psych::ParserPsych::TreeBuilder 构建的。可以自由地检查和操作 AST。有关处理 YAML 语法树的更多信息,请参见 Psych::parse_streamPsych::NodesPsych::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')

Exception 处理

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 与解析 YAML 文档时使用的 AST 完全相同。用户可以手动构建 AST,并且 AST 知道如何将自身作为 YAML 文档发出。有关构建 YAML AST 的更多信息,请参见 Psych::NodesPsych::Nodes::NodePsych::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

读取到 Psych::Nodes::Stream 结构

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"

常量

DEFAULT_SNAKEYAML_VERSION
LIBYAML_VERSION

Psych 使用的 libyaml 版本

VERSION

您正在使用的 Psych 版本

公共类方法

dump(o) → yaml 字符串 点击以切换源代码
dump(o, options) → yaml 字符串
dump(o, io) → 传入的 io 对象
dump(o, io, options) → 传入的 io 对象

将 Ruby 对象 o 转换为 YAML 字符串。可以传入可选的 options 来控制输出格式。如果传入 IO 对象,则 YAML 将被转储到该 IO 对象。

当前支持的选项是

:indentation

用于缩进的空格字符数。可接受的值应在 0..9 范围内,否则选项将被忽略。

默认值:2

:line_width

换行符的最大字符数。

默认值:0(表示“在 81 处换行”)。

:canonical

写入“规范” YAML 形式(非常详细,但严格正式)。

默认值:false

:header

在文档开头写入 %YAML [version]

默认值: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)
# File ext/psych/lib/psych.rb, line 505
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
dump_stream(*objects) 点击以切换源代码

将对象列表作为单独的文档转储到文档流中。

示例

Psych.dump_stream("foo\n  ", {}) # => "--- ! \"foo\\n  \"\n--- {}\n"
# File ext/psych/lib/psych.rb, line 595
def self.dump_stream *objects
  visitor = Psych::Visitors::YAMLTree.create({})
  objects.each do |o|
    visitor << o
  end
  visitor.tree.yaml
end
libyaml_version 点击以切换源代码

返回正在使用的 libyaml 版本

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);
}
load(yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false) 点击以切换源代码

yaml 加载到 Ruby 数据结构中。如果提供了多个文档,则将返回第一个文档中包含的对象。filename 将在任何异常在解析时引发时用于异常消息。如果 yaml 为空,则返回指定的 fallback 返回值,该值默认为 false

当检测到 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 368
def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false
  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
end
load_file(filename, **kwargs) 点击切换源代码

加载 filename 中包含的文档。将 filename 中包含的 yaml 返回为 Ruby 对象,如果文件为空,则返回指定的 fallback 返回值,默认值为 false。有关选项,请参见 load。

# File ext/psych/lib/psych.rb, line 669
def self.load_file filename, **kwargs
  File.open(filename, 'r:bom|utf-8') { |f|
    self.load f, filename: filename, **kwargs
  }
end
load_stream(yaml, filename: nil, fallback: [], **kwargs) { |to_ruby(**kwargs)| ... } 点击切换源代码

加载 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 626
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
parse(yaml, filename: nil) 点击切换源代码

解析 yaml 中的 YAML 字符串。返回 Psych::Nodes::Document。如果引发 Psych::SyntaxError,则 filename 用于异常消息。

当检测到 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

有关 YAML AST 的更多信息,请参见 Psych::Nodes

# File ext/psych/lib/psych.rb, line 398
def self.parse yaml, filename: nil
  parse_stream(yaml, filename: filename) do |node|
    return node
  end

  false
end
parse_file(filename, fallback: false) 点击切换源代码

解析 filename 处的文件。返回 Psych::Nodes::Document

当检测到 YAML 语法错误时,会引发 Psych::SyntaxError

# File ext/psych/lib/psych.rb, line 410
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
parse_stream(yaml, filename: nil, &block) 点击切换源代码

解析 yaml 中的 YAML 字符串。返回 Psych::Nodes::Stream。此方法可以处理 yaml 中包含的多个 YAML 文档。如果引发 Psych::SyntaxError,则 filename 用于异常消息。

如果给出了代码块,则在解析时,将向代码块生成 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

有关 YAML AST 的更多信息,请参见 Psych::Nodes

# File ext/psych/lib/psych.rb, line 452
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
parser() 点击切换源代码

返回默认解析器

# File ext/psych/lib/psych.rb, line 419
def self.parser
  Psych::Parser.new(TreeBuilder.new)
end
safe_dump(o) → yaml 字符串 点击切换源代码
safe_dump(o, options) → yaml 字符串
safe_dump(o, io) → 传入的 io 对象
safe_dump(o, io, options) → 传入的 io 对象

安全地将 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

换行符的最大字符数。

默认值:0(表示“在 81 处换行”)。

:canonical

写入“规范” YAML 形式(非常详细,但严格正式)。

默认值:false

:header

在文档开头写入 %YAML [version]

默认值: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)
# File ext/psych/lib/psych.rb, line 578
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
safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false) 点击切换源代码

安全地加载 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 322
def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false
  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
  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
safe_load_file(filename, **kwargs) 点击切换源代码

安全地加载 filename 中包含的文档。将 filename 中包含的 yaml 作为 Ruby 对象返回,如果文件为空,则返回指定的 fallback 返回值,默认值为 false。有关选项,请参阅 safe_load

# File ext/psych/lib/psych.rb, line 658
def self.safe_load_file filename, **kwargs
  File.open(filename, 'r:bom|utf-8') { |f|
    self.safe_load f, filename: filename, **kwargs
  }
end
to_json(object) 点击切换源代码

将 Ruby object 转换为 JSON 字符串。

# File ext/psych/lib/psych.rb, line 605
def self.to_json object
  visitor = Psych::Visitors::JSONTree.create
  visitor << object
  visitor.tree.yaml
end
unsafe_load(yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false) 点击切换源代码

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 271
def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false
  result = parse(yaml, filename: filename)
  return fallback unless result
  result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer)
end
unsafe_load_file(filename, **kwargs) 点击切换源代码

加载 filename 中包含的文档。将 filename 中包含的 yaml 作为 Ruby 对象返回,如果文件为空,则返回指定的 fallback 返回值,默认值为 false

注意:此方法 *不应* 用于解析不受信任的文档,例如通过用户输入提供的 YAML 文档。请改用 safe_load_file 方法。

# File ext/psych/lib/psych.rb, line 647
def self.unsafe_load_file filename, **kwargs
  File.open(filename, 'r:bom|utf-8') { |f|
    self.unsafe_load f, filename: filename, **kwargs
  }
end