Введение
JSON (JavaScript Object Notation) — это легковесный формат обмена данными, который стал стандартом де-факто для взаимодействия между сервером и клиентом, а также для хранения конфигураций. В экосистеме C# (.NET) работа с JSON реализована на высшем уровне: разработчику доступны как встроенные библиотеки, так и мощные сторонние решения. В этой статье мы на практических примерах разберем, как выполнять парсинг, сериализацию и десериализацию JSON в C#, используя современные подходы.
Мы рассмотрим два основных инструмента: System.Text.Json (встроенная библиотека с .NET Core 3.0+) и Newtonsoft.Json (популярная сторонняя библиотека). Вы узнаете, как преобразовывать объекты C# в JSON и обратно, как работать с вложенными структурами и как обрабатывать ошибки.
Основы сериализации и десериализации в C#
Прежде чем углубляться в код, давайте определимся с терминами. Сериализация — это процесс преобразования объекта C# (например, экземпляра класса) в строку JSON. Десериализация — обратный процесс: из JSON-строки мы восстанавливаем объект C#. В C# для этого используются атрибуты и специальные классы-сериализаторы.
1. Работа с System.Text.Json (встроенная библиотека)
Начиная с .NET Core 3.0, Microsoft представила собственную библиотеку для работы с JSON — System.Text.Json. Она быстрее Newtonsoft.Json и не требует установки дополнительных пакетов.
Рассмотрим простой класс Person:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
Сериализация в JSON:
using System.Text.Json;
var person = new Person
{
Name = "Алексей",
Age = 30,
Email = "alex@example.com"
};
string json = JsonSerializer.Serialize(person);
Console.WriteLine(json);
// Вывод: {"Name":"Алексей","Age":30,"Email":"alex@example.com"}
Десериализация из JSON:
string jsonString = @"{""Name"":""Мария"",""Age"":25,""Email"":""maria@example.com""}";
Person? person = JsonSerializer.Deserialize<Person>(jsonString);
Console.WriteLine(person?.Name); // Мария
Обратите внимание на оператор ? (nullable) — десериализация может вернуть null при ошибке.
2. Работа с Newtonsoft.Json (Json.NET)
Несмотря на появление System.Text.Json, Newtonsoft.Json остается популярным выбором благодаря своей гибкости и обширным возможностям настройки. Для его использования установите NuGet-пакет Newtonsoft.Json.
Пример с тем же классом Person:
using Newtonsoft.Json;
// Сериализация
var person = new Person { Name = "Иван", Age = 35, Email = "ivan@example.com" };
string json = JsonConvert.SerializeObject(person);
Console.WriteLine(json);
// Десериализация
string jsonInput = @"{'Name':'Ольга','Age':28,'Email':'olga@example.com'}";
Person? personDeserialized = JsonConvert.DeserializeObject<Person>(jsonInput);
Console.WriteLine(personDeserialized?.Name); // Ольга
Newtonsoft.Json по умолчанию поддерживает как двойные кавычки, так и одинарные, что делает его более толерантным к "грязному" JSON.
Продвинутые сценарии: работа с вложенными объектами и коллекциями
В реальных проектах JSON редко бывает плоским. Рассмотрим пример с вложенными данными.
Допустим, у нас есть класс Company, содержащий список сотрудников:
public class Employee
{
public string Name { get; set; }
public string Position { get; set; }
}
public class Company
{
public string CompanyName { get; set; }
public List<Employee> Employees { get; set; }
}
Сериализация (System.Text.Json):
var company = new Company
{
CompanyName = "TechCorp",
Employees = new List<Employee>
{
new Employee { Name = "Дмитрий", Position = "Разработчик" },
new Employee { Name = "Елена", Position = "Тестировщик" }
}
};
string json = JsonSerializer.Serialize(company, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);
Результат будет отформатирован с отступами:
{
"CompanyName": "TechCorp",
"Employees": [
{
"Name": "Дмитрий",
"Position": "Разработчик"
},
{
"Name": "Елена",
"Position": "Тестировщик"
}
]
}
Десериализация вложенного JSON:
string complexJson = @"{""CompanyName"":""SoftLab"",""Employees"":[{""Name"":""Анна"",""Position"":""Аналитик""}]}";
Company? companyDeserialized = JsonSerializer.Deserialize<Company>(complexJson);
Console.WriteLine(companyDeserialized?.Employees[0].Name); // Анна
Настройка сериализации: атрибуты и кастомные конвертеры
Часто требуется изменить поведение сериализации по умолчанию. Например, переименовать поля в JSON или игнорировать null-значения.
Использование атрибутов в System.Text.Json
using System.Text.Json.Serialization;
public class Product
{
[JsonPropertyName("product_id")]
public int Id { get; set; }
[JsonIgnore]
public string InternalCode { get; set; }
public string Name { get; set; }
}
// Сериализация
var product = new Product { Id = 1, InternalCode = "SECRET", Name = "Ноутбук" };
string json = JsonSerializer.Serialize(product, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
Console.WriteLine(json);
// Вывод: {"product_id":1,"Name":"Ноутбук"}
Атрибут [JsonPropertyName] меняет имя ключа, [JsonIgnore] исключает свойство из JSON, а опция DefaultIgnoreCondition убирает null-поля.
Кастомный конвертер для сложных типов
Если нужно сериализовать, например, DateTime в нестандартном формате, создайте свой конвертер:
public class CustomDateTimeConverter : JsonConverter<DateTime>