модуль Marshal
Библиотека маршалинга преобразует коллекции объектов Ruby в поток байтов, позволяя их хранить вне текущего активного скрипта. Эти данные впоследствии могут быть прочитаны и исходные объекты восстановлены.
Маршализованные данные содержат главные и второстепенные номера версий вместе с информацией об объекте. При обычном использовании маршалинг может загружать только данные, написанные с тем же главным номером версии и равным или меньшим второстепенным номером версии. Если флаг «verbose» Ruby установлен (обычно с помощью -d, -v, -w или –verbose), главные и второстепенные номера должны точно совпадать. Marshal версия не зависит от версий Ruby. Вы можете извлечь версию, прочитав первые два байта маршализованных данных.
str = Marshal.dump("thing")
RUBY_VERSION #=> "1.9.0"
str[0].ord #=> 4
str[1].ord #=> 8
Некоторые объекты не могут быть сохранены: если сохраняемые объекты включают связи, процедуры или методы, экземпляры класса IO или одиночные объекты, будет поднято исключение TypeError.
Если ваш класс имеет особые потребности в сериализации (например, если вы хотите сериализовать в определённом формате) или если он содержит объекты, которые в противном случае не могут быть сериализованы, вы можете реализовать собственную стратегию сериализации.
Существует два способа сделать это: ваш объект может определить либо marshal_dump и marshal_load, либо _dump и _load. marshal_dump будет иметь приоритет над _dump, если оба определены. marshal_dump может привести к более коротким Marshal строкам.
Учёт безопасности
По дизайну, Marshal.load может десериализовать практически любой класс, загруженный в процесс Ruby. Во многих случаях это может привести к выполнению удалённого кода, если маршализованные данные загружаются из ненадежного источника.
В результате, Marshal.load не подходит в качестве общего формата сериализации, и вы никогда не должны расмаршаливать пользовательский ввод или другие ненадежные данные.
Если вам нужно десериализовать ненадежные данные, используйте JSON или другой формат сериализации, который может загружать только простые «примитивные» типы, такие как String, Array, Hash и т. д. Никогда не позволяйте пользователю указывать произвольные типы для десериализации.
marshal_dump и marshal_load
При сохранении объекта вызывается метод marshal_dump. marshal_dump должен вернуть результат, содержащий информацию, необходимую marshal_load для восстановления объекта. Результатом может быть любой объект.
При загрузке объекта, сохраненного с помощью marshal_dump, объект сначала выделяется, а затем вызывается marshal_load с результатом от marshal_dump. marshal_load должен воссоздать объект из информации в результате.
Пример:
class MyObj
def initialize name, version, data
@name = name
@version = version
@data = data
end
def marshal_dump
[@name, @version]
end
def marshal_load array
@name, @version = array
end
end
_dump и _load
Используйте _dump и _load, когда вам нужно выделить объект, который вы восстанавливаете самостоятельно.
При сохранении объекта вызывается метод экземпляра _dump с целым числом, которое указывает максимальную глубину объектов для сохранения (значение -1 означает, что вы должны отключить проверку глубины). _dump должен вернуть строку, содержащую информацию, необходимую для восстановления объекта.
Метод класса _load должен принять строку и использовать ее для возвращения объекта того же класса.
Пример:
class MyObj
def initialize name, version, data
@name = name
@version = version
@data = data
end
def _dump level
[@name, @version].join ':'
end
def self._load args
new(*args.split(':'))
end
end
Поскольку Marshal.dump выводит строку, вы можете сделать так, чтобы _dump возвращал Marshal строку, которую Marshal.loaded загружает в _load для сложных объектов.
Константы
- MAJOR_VERSION
-
главная версия
- MINOR_VERSION
-
второстепенная версия
Публичные методы класса
static VALUE
marshal_dump(int argc, VALUE *argv, VALUE _)
{
VALUE obj, port, a1, a2;
int limit = -1;
port = Qnil;
rb_scan_args(argc, argv, "12", &obj, &a1, &a2);
if (argc == 3) {
if (!NIL_P(a2)) limit = NUM2INT(a2);
if (NIL_P(a1)) io_needed();
port = a1;
}
else if (argc == 2) {
if (FIXNUM_P(a1)) limit = FIX2INT(a1);
else if (NIL_P(a1)) io_needed();
else port = a1;
}
return rb_marshal_dump_limited(obj, port, limit);
} Сериализует obj и все дочерние объекты. Если anIO указан, сериализованные данные будут записаны в него, в противном случае данные будут возвращены как String. Если limit указан, обход дочерних объектов будет ограничен этой глубиной. Если limit отрицателен, проверка глубины не будет производиться.
class Klass
def initialize(str)
@str = str
end
def say_hello
@str
end
end
(не производит вывода)
o = Klass.new("hello\n")
data = Marshal.dump(o)
obj = Marshal.load(data)
obj.say_hello #=> "hello\n"
Marshal не может сохранить следующие объекты:
-
анонимный класс/модуль.
-
объекты, связанные с системой (например:
Dir,File::Stat,IO,File,Socketи так далее) -
экземпляр
MatchData,Data,Method,UnboundMethod,Proc,Thread,ThreadGroup,Continuation -
объекты, которые определяют одиночные методы
# File marshal.rb, line 33 def self.load(source, proc = nil, freeze: false) Primitive.marshal_load(source, proc, freeze) end
Возвращает результат преобразования сериализованных данных в source в объект Ruby (возможно, с ассоциированными подчиненными объектами). source может быть либо экземпляром IO, либо объектом, который отвечает на to_str. Если proc указан, каждый объект будет передан в proc, когда он десериализуется.
Никогда не передавайте ненадежные данные (включая пользовательский ввод) в этот метод. Подробности см. в обзоре.
Если передан аргумент freeze: true, десериализованный объект будет глубоко заморожен. Обратите внимание, что это может привести к более эффективному использованию памяти из-за дедупликации замороженных строк:
serialized = Marshal.dump(['value1', 'value2', 'value1', 'value2']) deserialized = Marshal.load(serialized) deserialized.map(&:frozen?) # => [false, false, false, false] deserialized.map(&:object_id) # => [1023900, 1023920, 1023940, 1023960] -- 4 different objects deserialized = Marshal.load(serialized, freeze: true) deserialized.map(&:frozen?) # => [true, true, true, true] deserialized.map(&:object_id) # => [1039360, 1039380, 1039360, 1039380] -- only 2 different objects, object_ids repeating
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.