модуль Marshal
Библиотека маршалинга преобразует коллекции объектов Ruby в поток байтов, позволяя хранить их вне текущего активного скрипта. Эти данные впоследствии можно прочитать и восстановить исходные объекты.
Данные Marshal содержат номера основной и дополнительной версии, сохранённые вместе с информацией об объекте. При обычном использовании маршалинг может загружать только данные, записанные с тем же номером основной версии и равным или меньшим номером дополнительной версии. Если установлен флаг Ruby «verbose» (обычно с помощью -d, -v, -w или –verbose), номера основной и дополнительной версий должны совпадать точно. Версионирование Marshal не зависит от номеров версий Ruby. Версию можно извлечь, прочитав первые два байта данных Marshal.
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 загружаются из ненадёжного источника.
Поэтому 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 с аргументом типа Integer, который указывает максимальную глубину сбрасываемых объектов (значение -1 означает, что проверку глубины следует отключить). Метод _dump должен возвращать String, содержащую информацию, необходимую для восстановления объекта.
Метод класса _load должен принимать String и использовать её для возврата объекта того же класса.
Пример:
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 не может сбрасывать следующие объекты:
-
анонимные Class/Module.
-
объекты, связанные с системой (например,
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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.