1. Вступ
Усе гаразд, якщо ваші потреби збігаються із задумом за замовчуванням. Але доволі часто замовник або сторонній бекенд вимагає суворо визначений формат: скрізь camelCase, дати — ISO 8601, пропуск значень null, власні конвертери та інші вимоги інтеграції.
Саме тут у пригоді стає наш новий друг — клас JsonSerializerOptions.
Якби серіалізація була кухнею, цей клас — ваші спеції, каструлі та секретні інгредієнти, якими ви можете приправити процес на свій смак.
Як використовувати JsonSerializerOptions
Щоб налаштувати процес серіалізації або десеріалізації, просто передайте об’єкт налаштувань методам серіалізатора:
using System.Text.Json;
var options = new JsonSerializerOptions
{
// Тут будуть ваші налаштування
};
string json = JsonSerializer.Serialize(myObject, options);
var obj = JsonSerializer.Deserialize<MyType>(json, options);
Такий підхід трапляється повсюди: ви створюєте JsonSerializerOptions, налаштовуєте їх під свої потреби (див. далі) і передаєте в методи серіалізатора Serialize/Deserialize.
Важливо: Один і той самий об’єкт JsonSerializerOptions цілком можна, і навіть варто, повторно використовувати для багатьох серіалізацій.
2. Основні налаштування JsonSerializerOptions
Розгляньмо найпоширеніші опції та перемикачі цього класу.
Форматування JSON — WriteIndented
Якщо вас дратує однорядковий JSON, увімкніть охайне форматування з відступами.
var options = new JsonSerializerOptions
{
WriteIndented = true // Зручний для читання JSON
};
var person = new Person { Name = "Anna", Age = 30 };
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json);
Вивід:
{
"Name": "Anna",
"Age": 30
}
Без WriteIndented = true ви б отримали нудний {"Name":"Anna","Age":30}. У продакшні частіше використовують «мініфікований» JSON (він менше важить), а для зневадження та журналів — відформатований.
Стиль іменування властивостей — PropertyNamingPolicy
Часто API вимагає, щоб імена властивостей були в стилі camelCase (наприклад, firstName замість FirstName). У .NET властивості зазвичай називають у PascalCase, і тут стане в пригоді властивість PropertyNamingPolicy:
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var person = new Person { Name = "Oleg", Age = 25 };
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json); // {"name":"Oleg","age":25}
Якщо вам потрібен інший стиль (наприклад, усіма великими літерами), можна написати власну реалізацію JsonNamingPolicy. Зазвичай достатньо camelCase.
3. Ігнорування властивостей
Пропускати чи ні значення null — DefaultIgnoreCondition
Іноді потрібно, щоб властивості з порожніми (null) значеннями взагалі не з’являлися в JSON. Причина — компактніший JSON або зовнішня система не працює з null.
Для цього використовуємо властивість DefaultIgnoreCondition:
using System.Text.Json.Serialization;
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
var person = new Person { Name = null, Age = 22 };
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json); // {"Age":22}
Якщо ви хочете ігнорувати не лише null, а й значення за замовчуванням (наприклад, int=0), використовуйте WhenWritingDefault:
options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault;
Серіалізація полів і приватних властивостей — IncludeFields і [JsonInclude]
За замовчуванням серіалізуються лише публічні властивості з відкритим сетером. Якщо потрібно серіалізувати ще й поля (fields), увімкніть:
var options = new JsonSerializerOptions
{
IncludeFields = true
};
Приклад класу:
public class Item
{
public string Name;
public int Id { get; set; }
}
var item = new Item { Name = "Book", Id = 12 };
string json = JsonSerializer.Serialize(item, options);
// {"Name":"Book","Id":12}
Для серіалізації приватних полів і властивостей можна використати атрибут [JsonInclude], але це — тема окремої лекції.
4. Конвертація значень
Форматування дат — Converters
За замовчуванням System.Text.Json серіалізує дати суто у форматі ISO 8601 (наприклад, "2023-12-27T15:30:45.123Z"), що майже завжди зручно. Але інколи формат дати/часу має бути особливим.
Для цього використовують власні конвертери. Базовий приклад:
using System.Text.Json;
using System.Text.Json.Serialization;
public class CustomDateTimeConverter : JsonConverter<DateTime>
{
private string _format = "yyyyMMdd";
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var date = reader.GetString();
return DateTime.ParseExact(date, _format, null);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(_format));
}
}
// Використовуємо конвертер
var options = new JsonSerializerOptions();
options.Converters.Add(new CustomDateTimeConverter());
var dateObj = new { Date = new DateTime(2022, 1, 5) };
string json = JsonSerializer.Serialize(dateObj, options); // {"Date":"20220105"}
5. Корисні нюанси
Не зважати на регістр імен властивостей під час десеріалізації — PropertyNameCaseInsensitive
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var json = "{\"name\":\"Vasya\",\"age\":33}";
var person = JsonSerializer.Deserialize<Person>(json, options);
// person.Name == "Vasya"
Керування вкладеністю та максимальним рівнем об’єктів — MaxDepth
var options = new JsonSerializerOptions
{
MaxDepth = 32 // За замовчуванням 64
};
Але якщо ваші об’єкти можуть бути нескінченними за вкладеністю — наприклад, дерево з посиланнями на батьків і дітей — краще спроєктувати модель інакше.
Коментарі в JSON — ReadCommentHandling (десеріалізація)
JSON офіційно не підтримує коментарі, але інколи трапляються «творчі» файли з // коментарями.
var options = new JsonSerializerOptions
{
ReadCommentHandling = JsonCommentHandling.Skip
};
string json = "{\n \"Name\": \"Ivan\", // Імʼя користувача\n \"Age\": 30\n}";
var person = JsonSerializer.Deserialize<Person>(json, options);
Підтримка спеціальних символів — Encoder
Іноді треба контролювати, як серіалізатор екранує символи (наприклад, щоб не перетворювати кирилицю на \uXXXX). Можна вказати свій енкодер:
using System.Text.Encodings.Web;
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
Використовуйте обережно, інакше ваші емодзі/ієрогліфи можуть несподівано перетворитися на Unicode‑послідовності!
6. Типові помилки під час налаштування серіалізації
Помилка №1: відсутній публічний сетер. Поле або властивість не серіалізується, бо немає публічного сетера або член приватний.
Помилка №2: некоректне ігнорування null-значень. За ввімкненого DefaultIgnoreCondition властивості з null не потраплять у JSON, хоча ви цього не очікували.
Помилка №3: неправильний формат дати. Дата серіалізується не так, як треба — під’єднайте кастомний конвертер (JsonConverter).
Помилка №4: очікування фіксованого порядку властивостей. Стандарт JSON не гарантує порядок полів. Якщо API вимагає строгий порядок, доведеться або змінювати модель, або використовувати сторонні бібліотеки.
Помилка №5: різні налаштування для серіалізації та десеріалізації. Якщо ви не використовуєте однакові JsonSerializerOptions для запису й читання JSON, можуть виникати помилки, наприклад, з регістром імен або форматами.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ