JavaRush /Курсы /C# SELF /Сериализация с System.Text...

Сериализация с System.Text.Json

C# SELF
44 уровень , 3 лекция
Открыта

1. Введение

Долгое время в экосистеме .NET главным инструментом для работы с JSON был замечательный сторонний пакет Newtonsoft.Json (он же Json.NET). Он мощный, гибкий, и до сих пор широко используется. Но с приходом новых версий .NET 9 и C# 14, Microsoft решила, что пора иметь свой, встроенный, высокопроизводительный JSON-сериализатор. Так появился System.Text.Json.

Зачем нужен новый подход? System.Text.Json разработан с учётом современных реалий и решает проблемы, которые накопились за годы использования сторонних библиотек. Он оптимизирован для максимальной скорости и безопасности, идеально подходит для асинхронных сценариев и веб-API, и — самое приятное — не требует установки через NuGet: всё уже в платформе.

Конечно, Newtonsoft.Json никуда не делся, и мы рассмотрим его позже. Но для большинства новых проектов System.Text.Json — выбор по умолчанию. Приготовьтесь, сейчас мы будем учить наши объекты говорить на JSON-языке!

2. Основы работы с System.Text.Json

Простая сериализация объекта

Ну что ж, начнём с базовой магии. Сериализуем наш объект в JSON-строку.

using System;
using System.Text.Json; // Не забудьте добавить!

public class Player
{
    public string Name { get; set; }
    public int Health { get; set; }
    public bool IsAlive { get; set; }
}

// Где-нибудь в вашей программе:
Player player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };

// Сериализация:
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // Выведет: {"Name":"Aragorn","Health":100,"IsAlive":true}

Комментарий: Если вы только начали изучать сериализацию, этот пример показывает, насколько всё просто: JsonSerializer.Serialize — и готово.

Десериализация объекта

Оживим объект из JSON-строки:

string incomingJson = "{\"Name\":\"Legolas\",\"Health\":88,\"IsAlive\":true}";

Player player2 = JsonSerializer.Deserialize<Player>(incomingJson);
Console.WriteLine(player2.Name);   // Legolas
Console.WriteLine(player2.Health); // 88
Console.WriteLine(player2.IsAlive); // true

Комментарий: Если структура JSON совпадает с вашим классом — всё работает как часы. Если нет — возможны исключения или значения по умолчанию.

3. Принципы работы и устройство сериализации

Как происходит сопоставление

System.Text.Json по умолчанию использует те же имена свойств, что и в классе. Регистр учитывается! Если в JSON написано health вместо Health, десериализация не сработает — свойство останется со значением по умолчанию (0, false или null).

Например:

// JSON со строчными ключами:
string badJson = "{\"name\":\"Gimli\",\"health\":120,\"isAlive\":true}";
Player player3 = JsonSerializer.Deserialize<Player>(badJson);
Console.WriteLine(player3.Name);    // пусто
Console.WriteLine(player3.Health);  // 0
Console.WriteLine(player3.IsAlive); // false

Забавный факт: Многие API пишут ключи в camelCase (health), а в C# принято PascalCase (Health). Это решается настройками (ниже).

4. Управление сериализацией — опции и настройки

Форматирование JSON: «человекочитаемый» вывод

Иногда хочется не компактный, а красиво отформатированный JSON — для конфигов или логов.

var options = new JsonSerializerOptions
{
    WriteIndented = true // Добавить отступы
};

string prettyJson = JsonSerializer.Serialize(player, options);
Console.WriteLine(prettyJson);
/*
{
  "Name": "Frodo",
  "Health": 50,
  "IsAlive": true,
  "Inventory": [
    "Ring",
    "Bread",
    "Torch"
  ],
  "Position": {
    "X": 5,
    "Y": 15
  }
}
*/

Задействуется свойство WriteIndented, и сериализатор добавляет отступы и переводы строк.

Управление стилем имён (CamelCase vs PascalCase)

Если вы работаете с веб-API, где все ключи в camelCase, включите политику имён:

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

string camelCaseJson = JsonSerializer.Serialize(player, options);
// {"name":"Frodo","health":50,"isAlive":true,"inventory":["Ring","Bread","Torch"],"position":{"x":5,"y":15}}

Тогда и при десериализации такие ключи будут корректно сопоставляться:

string apiJson = "{\"name\":\"Bilbo\",\"health\":40,\"isAlive\":true,\"inventory\":[\"Mug\"],\"position\":{\"x\":10,\"y\":5}}";
Player bilbo = JsonSerializer.Deserialize<Player>(apiJson, options);
Console.WriteLine(bilbo.Name); // Bilbo

5. Полезные нюансы

Использование атрибута [JsonIgnore]

Иногда не все свойства нужно сериализовать — например, приватные данные или временные вычисляемые значения.

using System.Text.Json.Serialization;

public class Player
{
    public string Name { get; set; }
    public int Health { get; set; }

    [JsonIgnore] // Это свойство не попадёт в JSON
    public bool IsSecretCharacter { get; set; }
}

Теперь при сериализации:

var player = new Player { Name = "Boromir", Health = 80, IsSecretCharacter = true };
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // {"Name":"Boromir","Health":80}

При обратной десериализации IsSecretCharacter получит значение по умолчанию (false).

Использование [JsonPropertyName("...")]

Допустим, в коде свойство называется IsAlive, а в JSON должно быть "status":

using System.Text.Json.Serialization;

public class Player
{
    public string Name { get; set; }
    public int Health { get; set; }

    [JsonPropertyName("status")]
    public bool IsAlive { get; set; }
}

Сериализация теперь будет выглядеть так:

var player = new Player { Name = "Pippin", Health = 60, IsAlive = false };
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // {"Name":"Pippin","Health":60,"status":false}

И при десериализации ключ "status" корректно заполнит свойство IsAlive.

Встроенные ограничения и особенности безопасности

  • По умолчанию сериализуются только публичные свойства с геттерами/сеттерами; приватные поля/свойства игнорируются.
  • При циклических ссылках выбрасывается исключение: "A possible object cycle was detected".
  • В отличие от Newtonsoft.Json, стандартный сериализатор меньше полагается на «магические» трюки с типами — зато безопаснее и быстрее для типовых сценариев.

6. Частые ошибки и подводные камни

Вы случайно меняете имя свойства в JSON и забываете обновить код — в результате свойство получает значение по умолчанию (null, 0, false).

В JSON отсутствует нужное поле — соответствующее свойство объекта будет дефолтным (см. документацию).

У свойства нет публичного сеттера — при десериализации оно не заполнится.

Изменили структуру вложенных классов или коллекций — десериализация может сломаться или дать неожиданные результаты.

Одинаковые имена на разных уровнях вложенности (в родителе и дочернем объекте) вводят в заблуждение и мешают отладке.

Иногда причина в версии платформы: старые версии System.Text.Json хуже работали с некоторыми типами (например, Dictionary, DateTime, enum), но в .NET 7/8/9 многое исправлено.

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