JavaRush /Курси /C# SELF /Налаштування серіалізації (

Налаштування серіалізації ( JsonSerializerOptions)

C# SELF
Рівень 45 , Лекція 3
Відкрита

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. Ігнорування властивостей

Пропускати чи ні значення nullDefaultIgnoreCondition

Іноді потрібно, щоб властивості з порожніми (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, можуть виникати помилки, наприклад, з регістром імен або форматами.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ