NET, Налаштування серіалізації SOAP та двійкової серіалізації
C# і .NET --- Багатопоточність та файли --- Налаштування серіалізації SOAP та двійкової серіалізації
У більшості випадків схема серіалізації за умовчанням, яку надає платформа .NET, цілком підходить. Потрібно лише застосувати атрибут [Serializable] до зв'язаних типів і передати дерево об'єктів обраному форматеру для обробки.
Однак у деяких випадках може знадобитися втрутитися у процес конструювання дерева та процес серіалізації. Наприклад, може існувати бізнес-правило, яке свідчить, що всі поля даних повинні зберігатися в певному форматі, або необхідно додати додаткові дані в потік, які безпосередньо не відображаються на поля об'єкта, що зберігається (тимчасові мітки, унікальні ідентифікатори і т.п.) .
У просторі імен System.Runtime.Serialization передбачено кілька типів, які дозволяють втрутитися у процес серіалізації об'єктів. У таблиці описані деякі з основних типів:
| ISerializable | Цей інтерфейс може бути реалізований на типі [Serializable] для керування його серіалізацією та десеріалізацією |
| ObjectIDGenerator | Цей тип генерує ідентифікатори для членів графа об'єктів |
| [OnDeserialized] | Цей атрибут дозволяє вказати метод, який буде викликаний негайно після десеріалізації об'єкта |
| [OnDeserializing] | Цей атрибут дозволяє вказати метод, який буде викликаний перед процесом десеріалізації. |
| [OnSerialized] | Цей атрибут дозволяє вказати метод, який буде викликаний негайно після того, як серіалізований об'єкт |
| [OnSerializing] | Цей атрибут дозволяє вказати метод, який буде викликанийперед процесом серіалізації |
| [OptionalField] | Цей атрибут дозволяє визначити поле типу, яке може бути пропущено у вказаному потоці |
| SerializationInfo | Фактично це "мішок властивостей", який підтримує пари "ім'я/значення", що становлять стан об'єкта під час процесу серіалізації |
Перш ніж приступити до вивчення різних способів налаштування процесу серіалізації, корисно уважніше придивитися до того, що відбувається за лаштунками. Коли BinaryFormatter серіалізує графік об'єктів, він відповідає за передачу наступної інформації у вказаний потік:
повністю кваліфіковане ім'я об'єкта у графі (наприклад, МуАрр. JamesBondCar);
ім'я складання, що визначає граф об'єктів (наприклад, МуАрр.ехе);
екземпляр класу SerializationInfo, що містить усі дані стану, які підтримуються членами графа об'єктів.
Під час процесу десеріалізації BinaryFormatter використовує ту саму інформацію для побудови ідентичної копії об'єкта із застосуванням інформації, вилученої з потоку-джерела. Процес, що виконується SoapFormatter, дуже схожий.
Згадайте, що для забезпечення максимальної мобільності об'єкта XmlSerializer не зберігає повністю кваліфіковане ім'я типу або ім'я складання, в якому він міститься. Цей тип може зберігати лише доступні дані. Крім переміщення необхідних даних у потік і назад, форматери також аналізують члени графа об'єктів щодо перелічених нижче частин інфраструктури:
Перевірка позначки об'єкта атрибутом [Serializable]. Якщо об'єкт не помічений, генерується виняток SerializationException.
Якщо об'єкт позначений атрибутом [Serializable], виконується перевірка, чи реалізує об'єкт інтерфейсISerializable. Якщо так, то викликається метод GetObjectData() у цьому об'єкті.
Якщо об'єкт не реалізує ISerializable інтерфейс, використовується процес серіалізації за замовчуванням, який обробляє всі поля, не помічені [NonSerialized].
На додаток до визначення того, чи підтримує тип ISerializable, форматери також відповідають за дослідження типів щодо підтримки членів, які оснащені атрибутами [OnSerializing], [OnSerialized], [OnDeserializing] або [OnDeserialized]. Ми розглянемо призначення цих атрибутів трохи пізніше, а спочатку давайте подивимося на призначення ISerializable.
Налаштування серіалізації з використанням інтерфейсу ISerializable
Об'єкти, позначені атрибутом [Serializable], мають опцію реалізації інтерфейсу ISerializable. Реалізація цього інтерфейсу дозволяє втрутитись у процес серіалізації та виконати необхідне форматування даних до та після серіалізації.
Після виходу версії .NET 2.0 переважний спосіб налаштування процесу серіалізації почав передбачати використання атрибутів серіалізації. Тим не менш, знання інтерфейсу ISerializable важливо для супроводу систем, які вже існують. Інтерфейс ISerializable досить простий, враховуючи, що в ньому визначено єдиний методGetObjectData().
Метод GetObjectData() викликається автоматично заданим форматором під час серіалізації. Реалізація цього методу заповнює вхідний параметр SerializationInfo послідовністю пар "ім'я/значення", які (зазвичай) відображають дані полів об'єкта, що зберігається. У SerializationInfo визначено численні варіації перевантаженого методу AddValue(), а також невеликий набір властивостей, які дозволяють встановлювати та отримувати ім'я типу, визначати збірку та лічильник членів.
Типи, що реалізують інтерфейс ISerializable, також повинні визначати спеціальний конструктор з наступною сигнатурою:
Зауважте, що видимість конструктора вказана як protected. Це прийнятно з огляду на те, що форматер матиме доступ до цього члена незалежно від його видимості. Такі спеціальні конструктори зазвичай роблять protected (або private), тим самим гарантуючи, що недбалий користувач об'єкта ніколи не створить об'єкт подібним чином. Як бачите, першим параметром конструктора є екземпляр типу SerializationInfo.
Другий параметр цього спеціального конструктора має типStreamingContext і містить інформацію щодо джерела або місця призначення даних, що передаються. Найбільш інформативним членом StreamingContext є властивість State, що представляє значення з переліку StreamingContextStates.
Значення цього перерахування є базовою композицією поточного потоку. Якщо тільки не планується реалізувати якісь низькорівневі служби віддаленої взаємодії, то працювати з цим перерахуванням безпосередньо потрібно рідко (подробиці шукайте в документації .NET Framework 4.0 SDK).
Налаштування серіалізації з використанням атрибутів
Хоча реалізація інтерфейсу ISerializable є одним з можливих способів налаштування процесу серіалізації, з моменту виходу версії. Використання цих атрибутів дає менш громіздкий код, ніж реалізація інтерфейсу ISerializable, враховуючи, що не доводиться вручну взаємодіяти з параметром SerializationInfo. Натомість можна безпосередньо модифікуватидані стану, коли форматор працює з вашим типом.
Ці атрибути серіалізації визначені у просторі іменSystem.Runtime.Serialization.
У разі застосування цих атрибутів, метод повинен бути визначений так, щоб приймати параметр StreamingContext і не повертати нічого (інакше буде згенеровано виключення часу виконання). Зверніть увагу, що застосовувати кожен із атрибутів серіалізації не обов'язково, а можна просто втрутитися у ті стадії процесу серіалізації, які цікавлять. Для ілюстрації нижче наведено новий тип [Serializable]:
Виконавши серіалізацію цього нового типу, ви виявите, що дані зберігаються у верхньому регістрі, а десеріалізуються – у нижньому.