模块 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")

异常处理

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')

异常处理

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"