JavaRush /Курсы /C# SELF /Настройка сериализации ( Js...

Настройка сериализации ( 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. Игнорирование свойств

Пропускать или не пропускать 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, могут возникать ошибки, например, с регистром имён или форматами.

2
Задача
C# SELF, 45 уровень, 3 лекция
Недоступна
Настройка формата дат в JSON
Настройка формата дат в JSON
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ