1. Введение
Если вы только начали знакомиться с сериализацией в .NET, вполне естественно задаться вопросом: зачем нужна ещё одна библиотека, если уже есть встроенный System.Text.Json? Ответ прост: Newtonsoft.Json появилась раньше и за годы развилась во всё ещё самый гибкий инструмент для JSON в .NET.
Она стала де-факто стандартом, потому что поддерживает сложные сценарии: кастомные контракты, мощные конвертеры, сериализацию приватных полей, LINQ к JSON, динамические объекты (JObject), гибкую обработку циклических ссылок и множество форматов даты/времени. Много библиотек и API до сих пор используют Json.NET “под капотом”.
Важно: в ряде сценариев встроенный System.Text.Json пока уступает возможностям Newtonsoft.Json, поэтому изучение Json.NET остаётся актуальным.
Как подключить Newtonsoft.Json (Json.NET)
Установите пакет через NuGet:
dotnet add package Newtonsoft.Json
Подключите пространство имён:
using Newtonsoft.Json;
2. Сериализация через Newtonsoft.Json
Возьмём традиционный класс Person:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Сериализация объекта в строку JSON
Person person = new Person { Name = "Иван", Age = 30 };
// Сериализация в JSON
string json = JsonConvert.SerializeObject(person);
Console.WriteLine(json);
// Вывод: {"Name":"Иван","Age":30}
Десериализация JSON обратно в объект
string json = "{\"Name\":\"Иван\",\"Age\":30}";
Person person = JsonConvert.DeserializeObject<Person>(json);
Console.WriteLine($"{person.Name}, {person.Age}");
// Вывод: Иван, 30
Что происходит "под капотом"?
Newtonsoft.Json проходит по всем открытым свойствам (public), сериализует их в JSON и записывает в строку. При десериализации он сопоставляет ключи из JSON с именами свойств и заполняет объект.
3. Сериализация коллекций объектов
Сериализация коллекций и массивов
С коллекциями всё работает “из коробки”.
List<Person> people = new List<Person>
{
new Person { Name = "Иван", Age = 30 },
new Person { Name = "Мария", Age = 25 }
};
string json = JsonConvert.SerializeObject(people);
// Вывод: [{"Name":"Иван","Age":30},{"Name":"Мария","Age":25}]
List<Person> deserialized = JsonConvert.DeserializeObject<List<Person>>(json);
// Теперь у вас снова список Person!
Особенности сериализации и десериализации словарей
var dict = new Dictionary<string, int>
{
["apple"] = 2,
["banana"] = 5
};
string json = JsonConvert.SerializeObject(dict);
// Вывод: {"apple":2,"banana":5}
var deserializedDict = JsonConvert.DeserializeObject<Dictionary<string,int>>(json);
// Всё работает!
Если ключи не строковые (например, Dictionary<int,string>), Json.NET преобразует ключи в строки при сериализации, а при десериализации попытается сконвертировать назад. Для сложных ключей (например, Guid) надёжнее использовать Dictionary<string, TValue>.
4. Работа с вложенными объектами и иерархиями
public class Order
{
public int Id { get; set; }
public Person Customer { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public string Title { get; set; }
public double Price { get; set; }
}
Order order = new Order
{
Id = 123,
Customer = new Person { Name = "Иван", Age = 30 },
Products = new List<Product>
{
new Product { Title = "Ноутбук", Price = 50000.0 },
new Product { Title = "Мышка", Price = 1500.0 }
}
};
string json = JsonConvert.SerializeObject(order);
Console.WriteLine(json);
Результат: вложенные объекты будут корректно представлены внутри JSON-структуры.
5. Настройка сериализации с помощью атрибутов
Игнорируем свойство
public class Person
{
public string Name { get; set; }
[JsonIgnore]
public int Age { get; set; }
}
Теперь Age не попадёт в JSON.
Переименовываем свойство
public class Person
{
[JsonProperty("full_name")]
public string Name { get; set; }
}
В JSON имя будет как "full_name".
6. Гибкая настройка: JsonSerializerSettings
Форматирование (красивый многострочный JSON):
string json = JsonConvert.SerializeObject(
people,
Formatting.Indented
);
Результат:
[
{
"Name": "Иван",
"Age": 30
},
{
"Name": "Мария",
"Age": 25
}
]
Часто используемые настройки:
| Свойство | Описание |
|---|---|
|
Как обрабатывать null-свойства (пропускать их или писать явно null) |
|
Пропускать ли значения по умолчанию |
|
Что делать с круговыми ссылками |
|
Формат строк для дат и времени |
Пример с пропуском null:
string json = JsonConvert.SerializeObject(
person,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }
);
7. Работа с динамическими JSON-структурами: JObject, JArray и др.
Когда структура JSON заранее неизвестна, используйте типы из Newtonsoft.Json.Linq:
using Newtonsoft.Json.Linq;
string json = @"{
'Name': 'Иван',
'Age': 30,
'Skills': ['C#', 'SQL', 'JSON']
}";
JObject obj = JObject.Parse(json);
Console.WriteLine(obj["Name"]); // Иван
Console.WriteLine(obj["Skills"][0]); // C#
Создание JSON “на лету”:
var jObj = new JObject
{
["Status"] = "Success",
["Result"] = new JArray("item1", "item2", "item3")
};
Console.WriteLine(jObj.ToString(Formatting.Indented));
JObject и JArray — представления JSON-объекта и массива, по сути — удобные коллекции.
8. Полезные нюансы
Циклические ссылки
Newtonsoft.Json умеет гибко их обрабатывать:
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
string json = JsonConvert.SerializeObject(obj, settings);
Так циклы будут пропущены. Для сохранения ссылок можно использовать ReferenceLoopHandling.Serialize совместно с [JsonObject(IsReference = true)].
Сериализация анонимных и динамических объектов
var anon = new { Foo = 42, Bar = "Hello" };
string json = JsonConvert.SerializeObject(anon);
// {"Foo":42,"Bar":"Hello"}
Валидация и обработка ошибок
try
{
Person p = JsonConvert.DeserializeObject<Person>(brokenJson);
}
catch (JsonSerializationException ex)
{
Console.WriteLine("Ошибка при десериализации: " + ex.Message);
}
Сравнение Newtonsoft.Json vs System.Text.Json
| Newtonsoft.Json (Json.NET) | System.Text.Json (.NET) | |
|---|---|---|
| Поддержка .NET | .NET Framework/Standard/6+ | .NET Core 3.0+ / .NET 5/6/9 |
| LINQ к JSON (JObject/JArray) | Да | Нет |
| Гибкая настройка | Очень большая | Ограниченная |
| Атрибуты ([JsonProperty], ...) | Да | Да (частично, меньше возможностей) |
| Поддержка приватных свойств | Да | Нет |
| Скорость | Медленнее в ряде сценариев | Быстрее |
| Сложные конвертеры | Да | Да (менее гибко, пока что) |
| Поддержка DataTable, DataSet | Да | Нет |
| Документация и примеры | Море | Растёт |
9. Ошибки новичков и типичные подводные камни
Ошибка №1: свойства становятся null после десериализации.
Часто у свойства нет сеттера или отсутствует конструктор без параметров — сериализатору нечем заполнить объект.
Ошибка №2: несовпадение имён свойств между JSON и классом.
Если в JSON поле "fullName", а в классе — FullName, используйте [JsonProperty] или настройте ContractResolver для сопоставления имён.
Ошибка №3: сериализация работает только с публичными свойствами по умолчанию.
Приватные поля/свойства не сериализуются без доп. настроек. Нужны конвертеры или специальные резолверы/контракты.
Ошибка №4: циклические ссылки приводят к StackOverflowException.
Взаимные ссылки объектов зацикливают сериализацию без настроек. Настраивайте обработку ссылок (например, ReferenceLoopHandling) или меняйте модель данных.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ