Сериализация имеет прямое отношение к сохранению состояния объекта в файле или памяти, а десериализация - к восстановлению состояния объекта соответственно. Примеров, когда это может пригодиться, неимоверное множество. Начиная от сохранения пользовательских настроек, сохранения промежуточного состояния объекта и заканчивая передачей объекта в специальном формате веб-сервису.
Для осуществления магии (де)сериализации .NET предлагает нам 3 родных варианта (не считая самостоятельной реализации механизма сериализации):
- Сериализация в двоичный формат (BinnaryFormatter)
- Сериализация в формат SOAP (SoapFormatter)
- Сериализация в формат xml (XmlSerializer)
У каждого из вариантов есть свои плюсы и минусы, о которых я постараюсь рассказать далее.
Создание класса для экспериментов Чтобы сделать объект сериализируемым нужно снабдить каждый связанный с ним класс или структуру аттрибутом [Serializable]. Если есть поля, которые по какой-то причине нужно исключить из сериализации, их необходимо пометить аттрибутом [NonSerialized].
Прежде чем приступить к рассмотрению имеющихся механизмов сериализации, давайте подготовим для экспериментов парочку простеньких классов:
[Serializable]
public class Human
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
[NonSerialized]
public int age;
}
[Serializable]
public class SuperHuman : Human
{
public bool isEvil { get; set; }
public Ability superpower = new Ability();
private bool hasSecret = true;
public bool getHasSecret()
{
return hasSecret;
}
}
[Serializable]
public class Ability
{
public bool isUnique;
public string[] abilities;
}
Сериализация в двоичный формат с помощью BinaryFormatterBinaryFormatter сохраняет состояние объекта в двоичном формате. Сериализируются все поля, вне зависимости от их области видимости. Исключение составляют поля помеченные аттрибутом [NonSerialized]. Помимо сохранения данных полей, BinaryFormatter также сохраняет полное квалифицированное имя каждого типа, полное имя сборки, где он определен, сюда входит информация об имени, версии, маркере общедоступного ключа (public key) и культуре. Отсюда следует, что BinaryFormatter это идеальный выбор в ситуациях, когда необходимо сохранять полные копии объектов для дальнейшей их передачи по значению между доменами для использования в .NET приложениях. Здесь заключается основной минус BinaryFormatter - данные, сохраненные с его помощью, могут быть воссозданы только в инфраструктуре CLI. Причем каждый, кто будет восстанавливать данные, должен иметь сборку с сериализуемым типом.
Сериализация происходит с помощью двух ключевых методов Serialize() и Deserialize(). Первый сохраняет граф объектов в виде последовательности байт в указанный поток. Второй наоборот - преобразует сохраненную последовательность байт в граф объектов.
Теперь инициализируем описанный выше класс SuperHero и сохраним его состояние с помощью двоичной сериализации:
//Инициализация SuperHuman
SuperHuman superHuman = new SuperHuman();
superHuman.Name = "Invisible Sharpshooter";
superHuman.age = 20;
superHuman.isEvil = false;
superHuman.superpower.abilities = new string[] {"sharp eye", "invisibility"};
superHuman.superpower.isUnique = true;
//Сохраняем состояние объекта superHuman в двоичном формате
BinaryFormatter formatter = new BinaryFormatter();
using(var fStream = new FileStream("./SuperHumanInfo.dat", FileMode.Create, FileAccess.Write, FileShare.None))
{
formatter.Serialize(fStream, superHuman);
}
//Восстановим состояние объекта
using(var fStream = File.OpenRead("./SuperHumanInfo.dat"))
{
SuperHuman newSuperHuman = (SuperHuman)formatter.Deserialize(fStream);
var skills = from abils in newSuperHuman.superpower.abilities select abils;
Console.WriteLine("Name: {0}", newSuperHuman.Name);
Console.WriteLine("age: {0}", newSuperHuman.age);
Console.WriteLine("isEvil: {0}", newSuperHuman.isEvil);
Console.WriteLine("isUnique: {0}", newSuperHuman.superpower.isUnique);
Console.WriteLine("hasSecret: {0}", newSuperHuman.getHasSecret());
Console.WriteLine("\nSkills List:");
foreach (string skill in skills)
{
Console.WriteLine("\t" + skill);
}
}
Вывод на консоль будет следующим: Name: Invisible Sharpshooter age: 0 isEvil: False isUnique: True hasSecret: True Skills List: sharp eye invisibilityИз примера видно, что поле age не сохранилось и все благодаря аттрибуту [NonSerialized]. В то же самое время состояние закрытого поля hasSecret сохранено успешно.
Важно помнить, что метод Deserialize() возвращает объект типа System.Object, поэтому необходимо явно привести объект к нужному типу.
Сериализация в SOAP формат с помощью SoapFormatter
SoapFormatter сохраняет состояние объекта в SOAP формате (Simple Object Access Protocol) и также как и BinaryFormatter сериализирует все поля, вне зависимости от их области видимости, кроме полей помеченных аттрибутом [NonSerialized]. В отличие от BinaryFormatter, платформа и операционная система не влияют на успешное восстановление данных, сериализированных с помощью SoapFormatter.
Как и в случае с BinaryFormatter (де)сериализация происходит с помощью ключевых методов Serialize() и Deserialize().
Сохраним состояние нашего объекта SuperHero в виде SOAP сообщения:
//Сохраняем состояние объекта superHuman в SOAP формате
SoapFormatter soap = new SoapFormatter();
using(var fStream = new FileStream("./SuperHumanInfo.soap", FileMode.Create, FileAccess.Write, FileShare.None))
{
soap.Serialize(fStream, superHuman);
}
В результате выполнения данного кода будет создан файл SuperHumanInfo.soap. Заглянув внутрь которого, можно увидеть XML-элементы, описывающие состояние нашего объекта superHuman с указателями ref, описывающими отношения между объектами в графе.
Вот небольшой кусок содержимого SuperHumanInfo.soap: ... <superpower href="#ref-3"/> <hasSecret>true</hasSecret> <_x003C_isEvil_x003E_k__BackingField>false</_x003C_isEvil_x003E_k__BackingField> <Human_x002B_name id="ref-4">Invisible Sharpshooter</Human_x002B_name> ...Приватное поле hasSecret было также, как и в случае с BinaryFormatter, успешно сохранено.
Из двух механизмов сериализации: BinaryFormatter и SoapFormatter, рекомендуется использовать первый т.к. начиная с версии .NET 3.5, SoapFormatter считается устаревшим.
Сериализация в XML формат с помощью XmlSerializer
XmlSerializer не сохраняет приватные данные. Хотя это можно сделать, инкапсулировав такое поле в общедоступном свойстве:
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
Также XmlSerializer не сохраняет точную информацию о типе (квалифицированное имя, имя сборки и т.д.), что делает его идеальным кандидатом когда необходимо сохранить объект для дальнейшего использования в другом языке программирования, а также на любой платформе, в любой операционной системе.Сериализация с помощью XmlSerializer немного отличается от сериализации с помощью BinaryFormatter и SoapFormatter. XmlSerializer требует указания информации о типе, который нужно сериализовать. Также, если определялись конструкторы отличные от конструктора по умолчанию, то во избежание исключения InvalidOperationException, необходимо добавить конструктор по умолчанию.
А теперь сохраним, уже ставший нам родным объект типа SuperHero в формате XML:
//Сохраняем состояние объекта superHuman в XML формате
XmlSerializer xml = new XmlSerializer(typeof(SuperHuman));
using (var fStream = new FileStream("./SuperHumanInfo.xml", FileMode.Create, FileAccess.Write, FileShare.None))
{
xml.Serialize(fStream, superHuman);
}
Итогом сериализации будет сгенерированный XML файл со следующим содержимым: <?xml version="1.0"?> <SuperHuman xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <age>20</age> <Name>Invisible Sharpshooter</Name> <superpower> <isUnique>true</isUnique> <abilities> <string>sharp eye</string> <string>invisibility</string> </abilities> </superpower> <isEvil>false</isEvil> </SuperHuman>Состояние приватного поля hasSecret сохранить не удалось, в отличие от приватного поля name благодаря наличию общедоступного свойства Name.
Изначально XmlSerializer сохраняет данные объекта как XML элементы. С помощью ряда аттрибутов, которые есть в наличии у класса XmlSerializer, можно управлять генерацией итогового XML документа: сериализовать поле/свойство как XML аттрибут, сериализовать поле/свойство с определенным именем, сконструировать root элемент и т.д.
В заключение
Для сохранения состояния объекта можно воспользоваться одним из доступных в .NET механизмов сериализации:
- BinaryFormatter
- SoapFormatter
- XmlSerializer
Выбор механизма зависит от поставленной задачи. Если нужна скорость, хорошее сжатие данных и вы работает в пределах инфраструктуры CLI, используйте BinaryFormatter.
Для передачи же сложных структур, когда необходимо представить данные в читабельном формате, и вы не хотите зависеть от платформы, языка программирования и т.д используйте XmlSerializer.
Также можно воспользоваться и SoapFormatter, но как я сказал ранее, начиная с версии .NET 3.5 этот класс считается устаревшим (obsolete).
просто, доступно, понятно) спасибо!)
ОтветитьУдалитьСтарался писать, как для себя - то, как стало бы понятно мне, если бы не знал.
Удалить+1
ОтветитьУдалитьлегко, понятно
спасибо.
ОтветитьУдалитьогромное спасибо, очень признателен, все получилось.
ОтветитьУдалитьДякую. Доступно і зрозуміло
ОтветитьУдалитьwhat about Json?
ОтветитьУдалитьБлагодарю! :)
ОтветитьУдалитьДобавлю, что для BinaryFormatter необходимо использовать модуль System.Runtime.Serialization.Formatters.Binary, для SoapFormatter подключить и использовать модуль System.Runtime.Serialization.Formatters.Soap, а для XmlSerializer использовать модуль System.Xml.Serialization.
ОтветитьУдалитьBinaryFormatter создает очень большого размеры файлы. Советую глянуть еще и protobuf.
ОтветитьУдалить