Введение
Процесс преобразования компьютерных данных, кодирование структуры данных.
data structure encoding
В информатике сериализация (или сериализация) — это процесс преобразования структуры данных или состояния объекта в формат, который можно сохранить (например, в файлы на внешних устройствах хранения или в буферы данных на внутренних устройствах хранения) или передать (например, в виде потоков данных по компьютерным сетям) и впоследствии восстановить (возможно, в другой вычислительной среде). При повторном чтении полученной последовательности битов в соответствии с форматом сериализации, её можно использовать для создания семантически идентичной копии исходного объекта. Для многих сложных объектов, особенно активно использующих ссылки, этот процесс нетривиален. Сериализация объектов не включает в себя методы, связанные с ними ранее. Этот процесс сериализации объекта также называют маршалингом объекта в некоторых случаях. Обратная операция — извлечение структуры данных из последовательности байтов — называется десериализацией (также известной как несериализацией или разбором).
Недостатки
Сериализация нарушает сокрытность абстрактного типа данных, потенциально раскрывая детали внутренней реализации. Тривиальные реализации, сериализующие все члены данных, могут нарушить инкапсуляцию. Чтобы затруднить создание совместимых продуктов конкурентами, разработчики проприетарного программного обеспечения часто держат в секрете детали форматов сериализации своих программ. Некоторые намеренно запутывают или даже шифруют сериализованные данные. Однако, для обеспечения взаимодействия требуется, чтобы приложения могли понимать форматы сериализации друг друга. Поэтому в архитектурах удалённого вызова процедур, таких как CORBA, форматы сериализации определяются детально. Многие организации, такие как архивы и библиотеки, стремятся обеспечить долгосрочную сохранность своих резервных копий – особенно дампов баз данных – путём хранения их в относительно удобочитаемом сериализованном формате.
Форматы сериализации
Технология Xerox Network Systems Courier в начале 1980-х годов оказала влияние на первый широко принятый стандарт. Sun Microsystems опубликовала External Data Representation (XDR) в 1987 году. XDR является открытым форматом и стандартизирован как STD 67 (RFC 4506). В конце 1990-х годов возникло стремление предоставить альтернативу стандартным протоколам сериализации: XML, являясь подмножеством SGML, использовался для создания кодировки в виде удобочитаемого текста. Такая кодировка может быть полезна для постоянных объектов, которые могут быть прочитаны и поняты людьми или переданы другим системам независимо от языка программирования. Она имеет недостаток – потерю более компактной кодировки на основе байтового потока, но к тому времени возросшие объемы хранилищ и пропускная способность сетей сделали размер файла менее критичным, чем в ранние дни компьютерных технологий. В 2000-х годах XML часто использовался для асинхронной передачи структурированных данных между клиентом и сервером в веб-приложениях Ajax. XML является открытым форматом и стандартизирован как рекомендация W3C. JSON – это легковесная альтернатива XML в виде простого текста, которая также широко используется для клиент-серверного взаимодействия в веб-приложениях. JSON основан на синтаксисе JavaScript, но не зависит от него и поддерживается во многих других языках программирования. JSON является открытым форматом, стандартизированным как STD 90, ECMA 404 и ISO/IEC 21778:2017. YAML является строгим надмножеством JSON и включает в себя дополнительные возможности, такие как теги типов данных, поддержка циклических структур данных, синтаксис, чувствительный к отступам, и различные способы заключения скалярных данных в кавычки. YAML – это открытый формат. Списки свойств используются для сериализации в фреймворках NeXTSTEP, GNUstep, macOS и iOS. Список свойств, или p-list, не относится к единому формату сериализации, а включает в себя несколько различных вариантов, некоторые из которых читаемы человеком, а один – бинарный. Для больших объемов научных данных, таких как спутниковые данные и результаты численного моделирования климата, погоды или океана, были разработаны специальные стандарты двоичной сериализации, например HDF, netCDF и более старый GRIB.
Поддержка языков программирования
Несколько объектно-ориентированных языков программирования напрямую поддерживают сериализацию объектов (или сохранение объектов), либо с помощью синтаксического сахара, либо предоставляя стандартный интерфейс для этого. К таким языкам относятся Ruby, Smalltalk, Python, PHP, Objective C, Delphi, Java и семейство языков .NET. Также существуют библиотеки, добавляющие поддержку сериализации языкам, в которых она изначально отсутствует.
C и C++
C и C++ не предоставляют сериализацию как высокоуровневую конструкцию, однако оба языка поддерживают запись любых встроенных типов данных, а также простых структур данных в виде двоичных данных. Поэтому написание пользовательских функций сериализации обычно является тривиальной задачей. Более того, решения, основанные на компиляторе, такие как система ODB ORM для C++ и инструментарий gSOAP для C и C++, способны автоматически генерировать код сериализации с минимальными или вообще без изменений в объявлениях классов. Другими популярными фреймворками сериализации являются Boost.Serialization из Boost Framework, S11n Framework и Cereal. MFC (Microsoft) также предоставляет методику сериализации как часть своей архитектуры Document View.
CFML
CFML позволяет сериализовать структуры данных в WDDX с помощью тега <cfwddx> и в JSON с помощью функции SerializeJSON.
Дельфийские
Delphi предоставляет встроенный механизм сериализации компонентов (также называемых постоянными объектами), который полностью интегрирован с её IDE. Содержимое компонента сохраняется в DFM-файл и загружается во время выполнения.
Иди .
Go нативно поддерживает разбор и сериализацию данных в форматах JSON и XML. Также существуют сторонние модули, поддерживающие YAML и Protocol Buffers. Go также поддерживает Gobs.
Хаскелл
В Haskell сериализация поддерживается для типов, являющихся членами классов типов Read и Show. Каждый тип, являющийся членом класса типа Read, определяет функцию для извлечения данных из строкового представления сериализованных данных. Класс типа Show, в свою очередь, содержит функцию show, с помощью которой можно получить строковое представление объекта. Программисту не требуется определять эти функции явно — достаточно указать, что тип реализует Read или Show, или оба сразу, чтобы компилятор сгенерировал соответствующие функции во многих случаях (но не во всех: например, функции не могут автоматически реализовывать Show или Read). Автоматически сгенерированный экземпляр для Show также производит корректный исходный код, поэтому одно и то же значение Haskell можно получить, выполнив код, сгенерированный функцией show, например, в интерпретаторе Haskell. Для более эффективной сериализации существуют библиотеки Haskell, позволяющие выполнять высокоскоростную сериализацию в двоичном формате, например, библиотека binary.
Ява
Java обеспечивает автоматическую сериализацию, которая требует, чтобы объект был помечен путем реализации интерфейса `Serializable`. Реализация этого интерфейса помечает класс как "пригодный для сериализации", а Java затем обрабатывает сериализацию внутри. Интерфейс `Serializable` не определяет методов сериализации, но сериализуемый класс может дополнительно определять методы с определенными специальными именами и сигнатурами, которые будут вызываться как часть процесса сериализации/десериализации, если они определены. Язык также позволяет разработчику более тщательно переопределить процесс сериализации, реализовав другой интерфейс – `Externalizable`, который включает в себя два специальных метода, используемых для сохранения и восстановления состояния объекта. Существует три основные причины, по которым объекты не сериализуются по умолчанию и должны реализовать интерфейс `Serializable` для доступа к механизму сериализации Java. Во-первых, не все объекты содержат полезную семантику в сериализованном состоянии. Например, объект `Thread` привязан к состоянию текущей JVM и десериализованный объект `Thread` не будет поддерживать полезную семантику в каком-либо контексте. Во-вторых, сериализованное состояние объекта является частью контракта совместимости его класса. Поддержание совместимости между версиями сериализуемых классов требует дополнительных усилий и внимания. Поэтому создание сериализуемого класса должно быть осознанным дизайнерским решением, а не состоянием по умолчанию. В-третьих, сериализация предоставляет доступ к непереходным (non-transient) приватным членам класса, которые в противном случае были бы недоступны. Классы, содержащие конфиденциальную информацию (например, пароль), не должны быть сериализуемыми или экстернализируемыми. Стандартный метод кодирования использует рекурсивный граф, основанный на преобразовании дескриптора класса объекта и сериализуемых полей в байтовый поток. Примитивные типы, а также непереходные и нестатические объекты, на которые имеются ссылки, кодируются в этот поток. Каждый объект, на который ссылается сериализуемый объект через поле, не отмеченное как `transient`, также должен быть сериализован; и если какой-либо объект в полном графе ссылок на непереходные объекты не сериализуем, то сериализация завершится неудачно. Разработчик может повлиять на это поведение, помечая объекты как `transient`, или переопределяя сериализацию для объекта так, чтобы часть графа ссылок была усечена и не сериализована. Java не использует конструкторы для сериализации объектов. Возможно сериализовать Java-объекты через JDBC и хранить их в базе данных. Хотя компоненты Swing реализуют интерфейс `Serializable`, их переносимость между различными версиями виртуальной машины Java не гарантируется. Таким образом, компонент Swing или любой компонент, который от него наследуется, может быть сериализован в байтовый поток, но нет гарантии, что его можно будет восстановить на другой машине.
Язык JavaScript
С ECMAScript 5.1 JavaScript включает встроенный объект JSON и его методы JSON.parse и JSON.stringify. Хотя JSON изначально основан на подмножестве JavaScript, существуют пограничные случаи, когда JSON не является допустимым JavaScript. В частности, JSON допускает использование символов Unicode конца строки (U+2028 и U+2029) без экранирования в строках в кавычках, в то время как ECMAScript 2018 и более ранние версии этого не допускают. Подробности смотрите в основной статье о JSON.
Джулия
Julia реализует сериализацию с помощью модулей `serialize` и `deserialize`, предназначенных для работы в пределах одной версии Julia и/или экземпляра одного и того же системного образа. Пакет `HDF5.jl` предлагает более стабильную альтернативу, использующую документированный формат и общую библиотеку с обёртками для различных языков программирования, в то время как формат сериализации по умолчанию, предположительно, был разработан с акцентом на максимальную производительность при сетевой передаче данных.
Липс
Как правило, структура данных Lisp может быть сериализована с помощью функций "read" (читать) и "print" (печатать). Переменная foo, содержащая, например, список массивов, будет выведена с помощью (print foo). Аналогично, объект может быть прочитан из потока с именем s с помощью (read s). Эти две части реализации Lisp называются принтером и ридером. Вывод "print" предназначен для чтения человеком; он использует списки, заключенные в круглые скобки, например: Во многих типах Lisp, включая Common Lisp, принтер не может представить все типы данных, поскольку неясно, как это сделать. Например, в Common Lisp принтер не может печатать объекты CLOS. Вместо этого программист может определить метод для обобщенной функции print object, который будет вызываться при печати объекта. Это в некоторой степени похоже на подход, используемый в Ruby. Сам код Lisp написан в синтаксисе ридера, который называется синтаксисом чтения. Большинство языков используют отдельные парсеры для обработки кода и данных, в то время как Lisp использует только один. Файл, содержащий код Lisp, может быть прочитан в память как структура данных, преобразован другой программой, а затем, возможно, выполнен или записан, например, в цикле "read-eval-print". Не все ридеры/райтеры поддерживают циклические, рекурсивные или совместно используемые структуры.
.NET Framework (англ.) (англ.)
В .NET Framework есть несколько сериализаторов, разработанных Microsoft. Существует также множество сериализаторов от сторонних разработчиков. Здесь обсуждается и тестируется более десятка сериализаторов, а здесь – тоже.
ОКамм
Стандартная библиотека OCaml предоставляет механизм маршаллинга через модуль Marshal. PHP может сериализовать любые типы данных, за исключением ресурсов (указателей файлов, сокетов и т.д.). Встроенная функция `unserialize` часто небезопасна при использовании с полностью недоверенными данными. Для объектов существуют два "магических метода", которые можно реализовать в классе – `__sleep` и `__wakeup` – вызываемые функциями `serialize` и `unserialize` соответственно, для очистки и восстановления состояния объекта. Например, может потребоваться закрыть соединение с базой данных при сериализации и восстановить его при десериализации; эту функциональность можно реализовать в этих двух магических методах. Они также позволяют объекту определять, какие свойства следует сериализовать. Начиная с PHP 5.1, для объектов доступен объектно-ориентированный механизм сериализации, реализуемый через интерфейс `Serializable`.
Пролог
Структура терминов Prolog, которая является единственной структурой данных языка, может быть сериализована с помощью встроенного предиката `write_term/3` и десериализована с помощью встроенных предикатов `read/1` и `read_term/2`. Получаемый поток представляет собой несжатый текст (в некоторой кодировке, определяемой конфигурацией целевого потока), при этом любые свободные переменные в термине представлены именами-заполнителями переменных. Предикат `write_term/3` стандартизован в спецификации ISO для Prolog (ISO/IEC 13211-1) на страницах 59 и далее ("Запись терма, § 7.10.5"). Поэтому ожидается, что термины, сериализованные одной реализацией, могут быть успешно десериализованы другой без неоднозначности или неожиданных результатов. На практике, реализации-специфичные расширения (например, словари SWI-Prolog) могут использовать нестандартные структуры терминов, что может нарушить совместимость в особых случаях. Примеры можно найти в соответствующих разделах документации для SWI-Prolog, SICStus Prolog и GNU Prolog. Вопрос о том, проверяются ли и каким образом сериализованные термины, полученные по сети, на соответствие спецификации (после десериализации из потока символов), остается на усмотрение разработчика. На этом этапе можно использовать встроенные в Prolog детерминированные грамматики предложений.
Python (англ.)
Основным механизмом сериализации является модуль стандартной библиотеки `pickle`, отсылающий к термину "pickling" в системах баз данных для описания сериализации данных (и "unpickling" для десериализации). `Pickle` использует простую виртуальную машину на основе стека, которая записывает инструкции, необходимые для восстановления объекта. Это кросс-версионный, настраиваемый, но небезопасный (не защищенный от ошибочных или злонамеренных данных) формат сериализации. Искаженные или злонамеренно созданные данные могут привести к тому, что десериализатор импортирует произвольные модули и создаст экземпляры любых объектов. Стандартная библиотека также включает модули для сериализации в стандартные форматы данных: `json` (со встроенной поддержкой базовых скалярных и коллекционных типов и возможностью поддержки произвольных типов через хуки кодирования и декодирования), `plistlib` (с поддержкой как двоичных, так и XML форматов списков свойств) и `xdrlib` (с поддержкой стандарта внешнего представления данных (XDR), описанного в RFC 1014). Кроме того, рекомендуется, чтобы представление объекта (`repr`) можно было вычислить в соответствующей среде, что делает его приблизительным аналогом функции печати объектов в Common Lisp. Не все типы объектов могут быть сериализованы автоматически, особенно те, которые содержат ресурсы операционной системы, такие как файловые дескрипторы, но пользователи могут регистрировать собственные функции "редукции" и построения для поддержки сериализации и десериализации произвольных типов. Изначально `pickle` был реализован как модуль `pickle` на чистом Python, но в версиях Python до 3.0 модуль `cPickle` (также встроенный) обеспечивает повышенную производительность (вплоть до 1000 раз быстрее).
R. В
R имеет функцию dput, которая записывает текстовое представление объекта R в формате ASCII в файл или соединение. Это представление можно прочитать из файла с помощью dget. Более конкретно, функция serialize сериализует объект R в соединение, результатом чего является вектор raw, закодированный в шестнадцатеричном формате. Функция unserialize позволяет прочитать объект из соединения или вектора raw.
РЕБОЛ
REBOL сериализует данные в файл (save/all) или в строку! (mold/all). Строки и файлы могут быть десериализованы с помощью полиморфной функции load. RProtoBuf обеспечивает кросс-языковую сериализацию данных в R, используя Protocol Buffers.
Рубины
Ruby включает стандартный модуль Marshal с двумя методами: `dump` и `load`, аналогичными стандартным утилитам Unix `dump` и `restore`. Эти методы сериализуют данные в стандартный класс String, то есть, по сути, преобразуют их в последовательность байтов. Некоторые объекты не могут быть сериализованы (при попытке это сделать будет вызвано исключение TypeError): окружения, объекты процедур, экземпляры класса IO, одиночные объекты и интерфейсы. Если классу требуется пользовательская сериализация (например, ему необходимо выполнить определенные действия очистки при сериализации/десериализации), это можно реализовать, определив два метода: `dump` и `load`. Метод экземпляра `dump` должен возвращать объект String, содержащий всю информацию, необходимую для восстановления объектов этого класса и всех связанных с ними объектов до максимальной глубины, заданной в виде целочисленного параметра (значение 1 означает, что проверка глубины должна быть отключена). Классовый метод `load` должен принимать строку и возвращать объект этого класса.
Ржавчина
Serde — наиболее широко используемая библиотека или крейт для сериализации в Rust.
- Небольшой разговор .
В общем, нерекурсивные и не разделяемые объекты могут храниться и извлекаться в удобочитаемой форме с использованием протокола storeOn:/readFrom:. Метод storeOn: генерирует текст выражения Smalltalk, который при вычислении с помощью readFrom: воссоздает исходный объект. Эта схема особенна тем, что использует процедурное описание объекта, а не сами данные. Поэтому она очень гибкая, позволяя классам определять более компактные представления. Однако в исходном виде она не обрабатывает циклические структуры данных и не сохраняет идентичность общих ссылок (то есть две ссылки на один и тот же объект будут восстановлены как ссылки на две равные, но не идентичные копии). Для этого существуют различные переносимые и непереносимые альтернативы. Некоторые из них специфичны для конкретной реализации Smalltalk или библиотеки классов. В Squeak Smalltalk существует несколько способов сериализации и хранения объектов. Самыми простыми и часто используемыми являются storeOn:/readFrom: и двоичные форматы хранения, основанные на сериализаторах SmartRefStream. Кроме того, объединенные объекты могут храниться и извлекаться с помощью ImageSegments. Оба предоставляют так называемый "фреймворк хранения бинарных объектов", который поддерживает сериализацию в компактную бинарную форму и извлечение из нее. Оба обрабатывают циклические, рекурсивные и разделяемые структуры, хранение/извлечение информации о классе и метаклассе, а также включают механизмы для "динамической" миграции объектов (то есть для преобразования экземпляров, которые были записаны более старой версией класса с другим представлением объекта). API похожи (storeBinary/readBinary), но детали кодирования отличаются, что делает эти два формата несовместимыми. Тем не менее, код Smalltalk/X является открытым исходным кодом и бесплатным, и его можно загрузить в другие реализации Smalltalk для обеспечения обмена объектами между различными диалектами. Сериализация объектов не входит в спецификацию ANSI Smalltalk. В результате код для сериализации объекта различается в зависимости от реализации Smalltalk. Полученные бинарные данные также различаются. Например, сериализованный объект, созданный в Squeak Smalltalk, нельзя восстановить в Ambrai Smalltalk. Следовательно, различные приложения, работающие на нескольких реализациях Smalltalk и использующие сериализацию объектов, не могут обмениваться данными между этими различными реализациями. К таким приложениям относятся объектная база данных MinneStore и некоторые пакеты RPC. Решением этой проблемы является SIXX – пакет для нескольких Smalltalk, использующий XML-формат для сериализации.
Стрелец
Стандартная библиотека Swift предоставляет два протокола, Encodable и Decodable (объединенные вместе как Codable), которые позволяют экземплярам соответствующих типов сериализовать себя в JSON, списки свойств или другие форматы, а также десериализовать из них. Компилятор может генерировать реализации по умолчанию для этих протоколов для типов, чьи хранимые свойства также являются Encodable или Decodable.
Windows PowerShell (полное обновление)
Windows PowerShell реализует сериализацию с помощью встроенного cmdlet Export-CliXml. Export-CliXml сериализует объекты .NET и сохраняет полученный XML в файл. Для восстановления объектов используйте cmdlet Import-CliXml, который генерирует десериализованный объект из XML в экспортированном файле. Десериализованные объекты, часто называемые "коллекциями свойств", не являются активными объектами; это снимки, которые имеют свойства, но не методы. Двумерные структуры данных также могут быть (де)сериализованы в формате CSV с использованием встроенных cmdlet Import-Csv и Export-Csv.