序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将数据流转换为对象。这两个过程结合起来,就使得数据能够被轻松地存储和传输。
为什么需使用序列化?有两个重要的原因:一个原因是将对象的状态保存在存储介质中,以便在以后重新创建精确的副本;另一个原因是可以将对象从一个应用程序发送到另一个应用程序中,远程处理还可以使用序列化将对象从一台机器上的应用程序传递到另一台机器上的应用程序中。
一般程序在运行时产生对象,这些对象随着程序的停止运行而消失,我们可以通过序列化将对象保存下来,这样在程序终止运行后这些对象仍然存在。以后可以在程序再次运行时通过读取数据重建这些对象,也可以在在其他程序中利用这些保存下来的对象。这种情况下我们通过使用对象的序列化和反序列化来完成这样的功能。
使用序列化 二进制数据保存在 Ruby 中,序列化也称为 marshaling ,使用 Marshal 类 R 可以把一个对象转换成字节流,并把它存在应用程序外。这样保存的对象可以在以后被其它的实例或者其它的程序读取使用。
可以使用 Marshal 类的 dump 方法保存对象,以后使用 load 可以读取以保存的这个对象。
class Rectangle
def initialize(length, width)
@length= length
@width = width
@area = length * width
end
def to_s
"#@length #@width #@area"
end
end
obj = Rectangle.new(10,20)
puts "Before: obj = #{obj}"
data = Marshal.dump(obj)
obj = Marshal.load(data)
puts "After: obj = #{obj}"
puts obj.inspect
执行结果为:
Before: obj = 10 20 200
After: obj = 10 20 200
#<Rectangle:0x2ac 7928 @length=10, @area=200, @width=20>
YAML 数据保存我们也可以使用 YAML 来实现序列化:
require 'yaml'
class Rectangle
def initialize(length, width)
@length= length
@width = width
@area = length * width
end
def to_s
"#@length #@width #@area"
end
end
obj = Rectangle.new(10,20)
puts "Before: obj = #{obj}"
data = YAML.dump(obj)
obj = YAML.load(data)
puts "After: obj = #{obj}"
puts obj.inspect
执行结果为:
Before: obj = 10 20 200
After: obj = 10 20 200
#<Rectangle:0x2ebbe08 @length=10, @area=200, @width=20>
定制序列化 二进制数据保存并不是所有的数据都可以被序列化和适合序列化,有时候你想自己决定哪些东西被序列化, Marshal 类提供了二个方法来实现这样的功能,一个是 marshal_dump ,一个是 marshal_load 。当使用 Marshal.load 对一个对象进行序列化的时候,如果这个对象定义了 marshal_dump 或 marshal_load 方法,那么这个方法将会被调用。
class Rectangle
def initialize(length, width)
@length= length
@width = width
@area = length * width
end
def marshal_dump
[ @length, @width ]
end
def marshal_load(variables)
@length = variables[0]
@width = variables[1]
@area = "unknown"
end
def to_s
"#@length #@width #@area"
end
end
obj = Rectangle.new(10, 20)
puts "Before: obj = #{obj}"
data = Marshal.dump(obj)
obj = Marshal.load(data)
puts "After: obj = #{obj}"
执行结果为:
Before: obj = 10 20 200
After: obj = 10 20 unknown
一些对象不能被保存,如果将要被保存的对象包括其它程序调用,方法体, IO 实例等,或者单独的一个对象,或者当你保存一个匿名的类或者方法时,一个 TypeError 错误就会产生。
YAML 数据保存使用 Marshal 模块实现序列化将对象保存为二进制数据,这样的做法有一个显著的缺点,由于保存的数据是二进制形式,如果解释器发生了重大改变且 Marshal 的二进制数据流处理格式和方式也发生了变化,那么就不保证原有已经 dump 的文件还可以继续使用。
如果使用文本格式的文件来保存序列化数据,那么就不用担心文件是否可读,在 Ruby1.8 种,可以使用 YAML 来实现这样的功能。
对于 marshaling ,除了二进制格式,也可以采用 YAML 的方法,它是一种纯文字又易懂的格式,很有趣而且比起 XML 简单多了。
修改一下上一节的例子,我们需要定义 to_yaml_properties 方法,
require 'yaml'
class Rectangle
def initialize(length, width)
@length= length
@width = width
@area = length * width
end
def to_yaml_properties
%w{ @length @width }
end
def to_s
"#@length #@width #@area"
end
end
obj = Rectangle.new(10,20)
puts "Before: obj = #{obj}"
data = YAML.dump(obj)
obj = YAML.load(data)
puts "After: obj = #{obj}"
执行结果为:
Before: obj = 10 20 200
After: obj = 10 20
我们可以看看 YAML 究竟把数据保存成什么样的格式,
obj = Rectangle.new(10,20)
puts YAML.dump(obj)
执行结果为 :
-- !ruby/object:Rectangle
length: 10
width: 20
看了上一节,大家或许会问,“为什么要命名为 YAML ?”已经有许多工具采用了招人喜欢的“ YA* ”形式的首字母缩略词,来表示“还有另一种 XXX ( Yet Another XXX )”。在开放源码这个充满智慧的领域中, YAML 没有使用其名称所暗示的首字母缩略词,而是采用了循环的“ YAML 不是标记语言( YAML Ain't Markup Language )”的缩写。然而对此产生的某种感觉是: YAML 确实可以做标记语言所做的工作,却不需要任何标记。
尽管 YAML 与 XML 一样普通,但在阅读、编辑、修改和产生方面,它比 XML 简单得多。可以用 XML 表示的任何东西,都可以用 YAML 表示,而且几乎总是更紧凑。
XML 是一个典型的由委员会驱动的庞然大物,它试图成为一种文档格式、数据格式、消息包格式、安全的 RPC 通道( SOAP )以及一种对象数据库。而且, XML 为每一类型的访问和操作都提供了大量的 API : DOM 、 SAX 、 XSLT 、 XPATH 、 JDOM 以及许多不太常见的接口层。非常了不起的是 XML 完成了所有这些工作;令人失望的是没有一项工作是完美无缺的。
YAML 的关注面则比较窄,它只是清晰地表示在动态编程语言(如 Perl 、 Python 、 Ruby 等)中所遇到的数据结构以及数据类型。
YAML 的优点:
适合人们阅读
适合与脚本语言交流
使用宿主语言的内部数据结构
拥有一致性的信息模型
使基于流式数据的处理成为可能
富于表达和可扩展
容易实现
YAML 做为一个可移植的对象序列化方案,可以在不同的 Ruby 进程中将对象以普通文本传递,此外也适合 ruby 与那些支持 YAML 的语言之间交换数据用。
YAML 中数据主要由序列 (sequence) , map (有的也叫做 hash )和标量( scalar )来表示。语法比较简单,易于人们阅读。
注释由 # 开头
序列由 "-" 开头
map 用 key:value 的格式
"---" 表示一个 yaml 文档的开始
list 和 hash 可以嵌套
block 的概念:一个 block 是一段文本。
Inline Collections :数据都写在一行
集合类型 序列基本序列
可以在每一个新行前加“ - ”构成一个序列。
YAML 中,
- Henry
- Mike
- Tom
在 Ruby 中,
[‘Henry’, ‘Mike’, ‘Tom’]
嵌套序列
使用位于空行上的“ - ”,你可以在一个序列内包含另一个序列。
-
- Henry
- Mike
- Tom
在 Ruby 中,
[[‘Henry’, ‘Mike’, ‘Tom’]]
使用缩进表示不同的层次,序列也可以表示更深层次的嵌套
-
-
- Henry
- Mike
- Tom
在 Ruby 中,
[[[‘Henry’, ‘Mike’, ‘Tom’]]]
l 混合嵌套序列
在序列中也可以嵌套其他的 YAML 数据结构:
- Henry
-
- 13
- male
- Mike
- Henry
在 Ruby 中,
[‘Henry’, [‘13’ , ‘male’], ‘Mike’, ‘Henry’]
表你可以使用 Key:value 的形式组织数据,每个 key:value 位于单独的一行,这样的数据结构称为表,也叫做哈希表或字典。
l 基本表
Grant:student
Tom:teacher
在 Ruby 中,
{‘Grant’ => ‘student’, ‘Tom’ => ‘teacher’ }
含有序列的表
Grant:student
Tom:
- teacher
- male
在 Ruby 中,
{‘Grant’ => ‘student’, ‘Tom’ => [‘teacher’, ‘male’ }
嵌套表
teacher: Mike
students:
Grant:13
Henry:14
在 Ruby 中,
{ ‘teacher’ => ‘Mike’,
‘students’ => {
‘Grant’ => ‘13’ ,
‘Henry’ => ‘14’
}
}
混合表
grade: 1
teachers:
Mike:math
Jane:english
classes :
-
Grant:13
Henry:14
-
Jack:13
Rose:13
在 Ruby 中,
{ ‘grade’ => ‘1’ ,
‘teachers’ =>
{
‘Mike’ => ‘math’,
‘Jane’ => ‘English’
}
‘classes’ => [
{
‘Grant’ => ‘13’ ,
‘Henry’ => ‘14’
},
{
‘Jack’ => ‘13’ ,
‘Rose’ => ‘13’
}
]
}
序列中嵌套表的缩写
当在序列中添加一个表时,可以
- worked on test.py:
- This is a test
在 Ruby 中,
[ {‘worked on test.py’ => [‘This is a test’] } ]
表中嵌套序列的缩写
当在表中嵌套一个序列时,可以不需要缩进。
Students:
- ‘Grant’
- ‘Henry’
在 Ruby 中,
{‘Students’ => [‘Grant’, ‘Henry’] }
插入键 (Merge key)
可以使用插入键在一个表中插入另一个表
{ information:
Name: Jane
course: English
<<:
Age: 25
}
在 Ruby 中 ,
{‘information’ => {
‘name’ => ‘Jane’,
‘course’ => ‘English’,
‘age’ => ‘25’
}
}
单行集合类型单行序列
单行映射
基本类型String
Scalar
NULL
Boolean
Integer
Float
Time
Date
块 别名和锚 ( Aliases and Anchors ) 文档Ruby 中 YAML 的使用
Ruby 中的 YAML 支持
Ruby1.8 已经包含了 YAML 支持了。只需要 require 进来就行了。
require 'yaml'
class Person
attr_accessor :name, :sex, :age
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
end
# 创建一个对象
person=Person.new("Tom", "male", 15)
# 打印序列化之后的结果
puts person.to_yaml
结果为:
--- !ruby/object:Person
age: 15
gender: male
name: Tom
假设现在有一个 person.xml 文件内容为:
--- !ruby/object:Person
age: 25
gender: male
name: Henry
如下代码:
# 从 person.yml 创建对象
person2 = YAML::load( File.open('d:\\person.yml') )
puts person2.inspect
puts person2.class # 结果是 Person
puts person2.name
执行结果为:
#<Person:0x2ebbf70 @name="Henry", @age=25, @gender="male">
Person
Henry