1. Вступ
У багатьох старих підручниках і прикладах із C# можна натрапити на серіалізацію за допомогою класу BinaryFormatter. Колись це був стандарт: зручно, швидко, мінімум клопоту. Приклад з тих часів виглядав десь так:
// Небезпечний олдскульний код! Таке в нових проєктах не використовуйте!
var bf = new BinaryFormatter();
using (var stream = File.Open("player.dat", FileMode.Create))
{
bf.Serialize(stream, playerObject);
}
Але в сучасному .NET 9 ви вже не зможете навіть скомпілювати такий код: BinaryFormatter видалено із платформи.
Шлях від героя до вигнанця
Спершу BinaryFormatter створили як універсальний механізм серіалізації та десеріалізації .NET‑обʼєктів, здатний зберігати складні графи обʼєктів «як є». Згодом виявилися серйозні проблеми з безпекою та сумісністю, і тепер він лишився в минулому — приблизно як касетні магнітофони в епоху стримінгу.
2. Головні причини відмови від BinaryFormatter
Величезна дірка в безпеці
BinaryFormatter уміє не лише серіалізувати дані, а й відновлювати типи з вхідного потоку. Якщо бездумно десеріалізувати неперевірені дані (наприклад, отримані з мережі), зловмисник може вбудувати шкідливе навантаження, що призведе до віддаленого виконання коду (RCE). Саме тому Microsoft багато років поспіль попереджала: «Не використовуйте BinaryFormatter». Починаючи з .NET 5 його позначили як застарілий і небезпечний ([Obsolete]), а в .NET 9 — видалили.
Залежність від внутрішньої структури класів
Формат BinaryFormatter тягне за собою деталі реалізації типів: поля, пакування, версії. Найменші зміни в класах (додали/видалили поле) порушують сумісність із уже збереженими файлами. Це робить такий формат непридатним для довготривалого зберігання та міжверсійного обміну.
Складнощі з кросплатформністю
Дані, збережені через BinaryFormatter, привʼязані до внутрішнього подання .NET‑обʼєктів. Прочитати їх на іншій платформі чи мові або навіть іншій версії .NET часто неможливо.
3. Як виявити «старий код» і що з ним робити?
Ознаки використання BinaryFormatter виглядають так:
using System.Runtime.Serialization.Formatters.Binary; // <-- підозріло!
BinaryFormatter bf = new BinaryFormatter(); // <-- небезпечно!
bf.Serialize(...);
bf.Deserialize(...);
У сучасних версіях .NET такий код не компілюється: інструменти повідомлять про помилку «Тип або імʼя не знайдено». У легасі‑проєктах на .NET Framework — принаймні суворі попередження.
Якщо натрапили на такий код, замінюйте BinaryFormatter на сучасні, безпечні альтернативи.
Які формати та класи використовувати сьогодні?
Для JSON
- System.Text.Json — швидкий, безпечний, вбудований у .NET. Офіційна документація
- Newtonsoft.Json — популярна стороння бібліотека для складних випадків.
Для XML
- XmlSerializer — серіалізація у XML. Документація
Для бінарних форматів
- System.Formats.Cbor (CBOR)
- Protobuf.Net (Protocol Buffers), MessagePack for C#
Для специфічних завдань підбирайте спеціалізовані формати й бібліотеки — «універсального» бінарного серіалізатора в стилі BinaryFormatter більше немає, і це добре.
4. Переписуємо код із BinaryFormatter
Для чого не можна використовувати BinaryFormatter
Не можна:
- Зберігати користувацькі налаштування та важливі дані.
- Передавати дані мережею (клієнт ↔ сервер).
- Десеріалізувати дані з неперевірених джерел.
Натомість:
- Для повсякденних завдань — System.Text.Json.
- Для конфігурацій — JSON, XML тощо.
- Для міжмовного обміну — формати з кросплатформною сумісністю (JSON, XML, Protobuf, MessagePack).
Який код тепер писати?
Серіалізація в JSON (рекомендований спосіб)
using System.Text.Json;
// Ваш клас:
public class Player
{
public string Name { get; set; } = "";
public int Health { get; set; }
public bool IsAlive { get; set; }
}
// Обʼєкт для серіалізації
var player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };
// Перетворюємо обʼєкт на рядок JSON
string json = JsonSerializer.Serialize(player);
// Зберігаємо у файл — безпечно!
File.WriteAllText("player.json", json);
// Для відновлення обʼєкта з файла:
string loadedJson = File.ReadAllText("player.json");
Player loadedPlayer = JsonSerializer.Deserialize<Player>(loadedJson)!;
Серіалізація в XML (якщо потрібен строгий формат)
using System.Xml.Serialization;
// Клас має мати публічний конструктор без параметрів
var player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };
var serializer = new XmlSerializer(typeof(Player));
using (var fs = File.Create("player.xml"))
{
serializer.Serialize(fs, player);
}
Візуальна схема «старе vs нове»
| Завдання | Старий спосіб (BinaryFormatter) | Сучасний спосіб |
|---|---|---|
| Серіалізація у файл | |
|
| Десеріалізація з файлу | |
|
| Безпека | Вразливий до атак (RCE) | Безпечні парсери та суворі моделі |
| Кросплатформність | Ні | Так (JSON/XML/Protobuf) |
| Швидкість | Швидко, але небезпечно | Швидко і безпечно |
5. Типові помилки під час роботи з XmlSerializer
Помилка № 1: приватні властивості або поля. XmlSerializer серіалізує лише public властивості; навіть protected призведе до помилки.
Помилка № 2: відсутність конструктора за замовчуванням. Потрібен публічний конструктор без параметрів, інакше серіалізація/десеріалізація не запрацює.
Помилка № 3: невідповідність між типами даних. XmlSerializer погано працює із Dictionary, interface та delegate — використовуйте обгортки або інші формати.
Помилка № 4: циклічні посилання (circular reference). XmlSerializer не підтримує графи виду A → B → A; для таких випадків частіше беруть JSON з відповідними налаштуваннями.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ