Опубликован: 11.05.2007 | Доступ: свободный | Студентов: 1597 / 157 | Оценка: 4.36 / 4.25 | Длительность: 16:06:00
Лекция 4:

Сериализация объектов. Способы сериализации в .NET Framework

< Лекция 3 || Лекция 4: 1234 || Лекция 5 >
Аннотация: Рассматривается понятие графа объектов и проблема его передачи между двумя компьютерами. Описывается проблема сериализация графа объектов, приводится классификация методов сериализации. Приводятся описание различных методов сериализации, которые используются в .NET Framework, и их особенностей.

4.1. Сериализация графа объектов

В отличие от приложений на неуправляемом коде, приложения .NET Framework не обязательно выполняются в виде отдельных процессов, а могут существовать в пределах одного процесса операционной системы в своих собственных областях, называемых доменами приложения. Такие области можно рассматривать как некоторые логические процессы виртуальной машины CLR. Использование управляемого кода позволяет при этом гарантировать изоляцию приложений в пределах своих областей. При передаче между доменами приложений некоторого объекта для его класса должна быть определена процедура сериализации, которая позволяет сохранить состояние объекта в некотором внешнем хранилище (например, в файле, или в сообщении транспортного протокола) при помощи потоков ввода-вывода, и процедура десериализации, создающая копию объекта по сохраненному состоянию (рис. 4.1). Следует отметить, что в общем случае это могут быть объекты разных классов, и даже созданные в разных системах разработки приложений.

Сериализация и десериализация объекта

Рис. 4.1. Сериализация и десериализация объекта

Задача сериализации объекта, включающего только поля из элементарных типов значений (наследников класса System.ValueType ) и строк, не представляет принципиальных трудностей. Для такого объекта в ходе сериализации в поток записываются сами значения всех полей объекта. Однако в общем случае объект содержит ссылки на другие объекты, которые, в свою очередь, могут ссылаться друг на друга, образуя так называемый граф объектов ( object graph ). Сами ссылки не могут быть сохранены в потоке ввода-вывода, поэтому основной вопрос сериализации – это способ замены ссылок.

Граф объектов – ориентированный граф G = < V, E > , в котором вершины – это объекты (множество V ), а ребра направлены от объектов, содержащие ссылки, на ссылаемые объекты (рис. 4.2).

public class SampleClass
{
    public SampleClass fieldA = null;
    public SampleClass fieldB = null;
}   

...
    SampleClass root = new SampleClass();
    SampleClass child1 = new SampleClass();
    SampleClass child2 = new SampleClass();
    root.fieldA = child1;
    root.fieldB = child2;
    child1.fieldA = child2;
...
Граф объектов

Рис. 4.2. Граф объектов

Наличие ссылки на объект B в объекте A есть принадлежность пары < A, B > множеству E. При рассмотрении графа все поля объектов, относящиеся к простым типам значениям и строкам, можно исключить из рассмотрения в силу тривиальности их сериализации. Хотя формально строки являются ссылочными типами, особенность реализации строк в CLI не дает возможность изменить уже созданную строку. Эта особенность позволяет тривиально обрабатывать строки при сериализации, сохраняя в потоке их содержимое.

Существует частный случай, когда сериализация выполняется тривиально. Для этого граф должен иметь вид так называемого ориентированного дерева. В каждую вершину дерева, отличную от корня, направлено единственное ребро, и существует единственная вершина, в которую не ведет ни одного ребра, называемая корнем. В таком случае сериализация может вестись от корня методом в глубину, а сериализация полей-ссылок выполняется аналогично сериализации полей значений – при обнаружении ссылки на объект вместо нее в хранилище помещаются значения полей ссылаемого объекта и некоторая дополнительная информация о типе объекта. Например, если существуют ребра < A, B1>, ... < A, B>, функция сериализации S для объекта A может быть определена как S(A) = < V(A), <S(B1),  ... S(Bn)>> , где функция Vзначение полей значений и строк указанного объекта.

Проблема сериализации графа объектов связана с тем, что ссылка на один и тот же объект может быть значением поля у разных объектов (существуют ребра < A1, B> и < A2, B > ). Возможно также наличие циклов вида A -> ...  -> A.

В этом случае в ходе сериализации объектам должны быть поставлены в соответствие некоторые идентификаторы, и в хранилище отдельно сохраняется список объектов, отмеченный идентификаторами, а при сериализации вместо ссылок записываются идентификаторы ссылаемых объектов. Если A1, ... An – все вершины графа, то его образом после сериализации будет множество {<idA1, S(A1)>, ... <idAn, S(An)>} , где S(A) определена как S(A) = < V(A), <idB1, ... idBn> > , где B1, ... Bn – объекты, ссылки на которые непосредственно содержатся в объекте A. В программе роль идентификатора объекта выполняет его адрес, но вместо него обычно удобнее назначить некоторые идентификаторы в процедуре сериализации для более легкого чтения человеком полученного образа. В ходе сериализации нужно ввести список адресов уже записанных объектов, как для ведения списка идентификаторов, так и для обнаружения возможных циклов при обходе графа методом в глубину.

Следует отметить, что результат сериализации дерева легко представим в виде XML, когда содержимое каждого объекта представлено одним элементом с некоторым набором атрибутов и вложенных элементов. Поэтому при рассмотрения проблемы сериализации можно сформулировать рекомендацию, что классы, передаваемые между удаленными компонентами, должны являться корнем дерева объектов. В частном случае это дерево может быть вырожденным, то есть класс не содержит полей-ссылок вообще.

4.2. Методы сериализации в .NET Framework

Microsoft .NET Framework 2.0 поддерживает четыре технологии удаленного взаимодействия – Message Queuing, Enterprise Services, Remoting, Web services. Какая бы технология не была выбрана, в любом случае передача данных между удаленными компонентами является одним из основных моментов удаленного взаимодействия. Данные могу передаваться как сообщение в очереди (MSMQ) или как параметры удаленного вызова и результат вызываемой функции (остальные технологии). В любом случае, для реализации распределенной системы необходимо определить процедуры сериализации и десериализации для передаваемых классов.

Классы, производящие сериализацию и десериализацию в .NET Framework, называются классами форматирования ( formatters ). Не считая служащего для обеспечения совместимости ActiveXMessageFormatter, в .NET Framework выделяется три различных независимых класса форматирования: XmlSerializer, SoapFormatter, BinaryFormatter. Они используются как для записи и чтения объектов из потоков ввода-вывода, так и для построения распределенных систем:

  • технология веб служб ASP.NET использует XmlSerializer ;
  • технология Remoting использует SoapFormatter, BinaryFormatter или созданный пользователем класс;
  • при работе с сообщениями MSMQ используется XmlSerializer (через XMLMessageFormatter ), или BinaryFormatter (через BinaryMessageFormatter ), или созданный пользователем класс;
  • технология Enterprise Services основана на Remoting и использует BinaryFormatter.

Можно провести классификацию возможных методов сериализации по следующим основным признакам.

1. Классификация по виду обрабатываемого графа:

  • универсальные методы: граф произвольного вида с циклами;
  • произвольный ациклический граф;
  • дерево.

2. Классификация по формату хранения информации в хранилище:

  • бинарные методы, использующие двоичный формат хранения данных, который не пригоден для чтения человеком без использования специальных средств;
  • текстовые методы, используют XML или иные текстовые форматы, пригодные для чтения или редактирования человеком при использовании текстового редактора.

3. Классификация по спецификации формата данных, полученного в результате сериализации:

  • закрытые методы: спецификация задается только при помощи интерфейсов всех классов, объекты которых образуют граф; в этом случае обе удаленные компоненты должны быть созданы на одной языковой платформе, причем обе стороны должны иметь как минимум описание интерфейса сериализуемых классов;
  • открытые методы: спецификация может быть задана в виде общепринятого формата, например схемы XSD.

По данной классификации XmlSerializer реализует открытый текстовый неуниверсальный метод, BinaryFormatter – закрытый двоичный универсальный метод, SoapFormatter – текстовый открытый метод. Дополнительной особенностью каждого из классов, за исключением BinaryFormatter, является ограничения на классы, которые подлежат сериализации. Данные ограничения будут рассмотрены далее.

< Лекция 3 || Лекция 4: 1234 || Лекция 5 >