1. Вступ
Newtonsoft.Json — це своєрідний динозавр у доброму сенсі. Він був де-факто стандартом для роботи з JSON в екосистемі .NET багато років, ще до того, як System.Text.Json узагалі з’явився. Мільйони проєктів, тисячі бібліотек і фреймворків (навіть чимало частин ASP.NET Core ще донедавна) використовували саме Json.NET.
Його переваги такі:
- Дуже функціональний і гнучкий: Json.NET пропонує безліч налаштувань, атрибутів і можливостей для тонкого керування процесом серіалізації та десеріалізації. Він уміє робити те, що System.Text.Json або виконує з великими труднощами, або взагалі не підтримує.
- Поблажливий до «неідеального» JSON: дані із зовнішніх систем не завжди ідеальні — Json.NET часто дає змогу десеріалізувати їх без зайвого болю.
- Зворотно сумісний: якщо проєкт або бібліотека зав’язані на Json.NET, уміння працювати з ним усе ще необхідне.
Звісно, System.Text.Json швидший і створений з огляду на сучасні вимоги. Проте Newtonsoft.Json і надалі лишається потужним інструментом, особливо коли потрібна специфічна логіка або максимальна гнучкість.
Як встановити Newtonsoft.Json?
Оскільки це стороння бібліотека, додайте пакет через NuGet:
- Відкрийте проєкт.
- Клацніть правою кнопкою миші на проєкті в Solution Explorer.
- Виберіть «Manage NuGet Packages…».
- У полі пошуку введіть Newtonsoft.Json.
- Виберіть пакет і натисніть «Install».
Після встановлення з’явиться нова залежність — відтепер можна використовувати Json.NET!
Порівняння можливостей Newtonsoft.Json і System.Text.Json
| Можливість | System.Text.Json | Newtonsoft.Json |
|---|---|---|
| Проста серіалізація/десеріалізація | Так | Так |
| Підтримка атрибутів для властивостей | Частково | Повна |
| Кастомні конвертери | Так | Так |
| Робота з приватними полями | Ні | Так |
| Робота з динамічними структурами | Обмежено | Так |
| LINQ to JSON (JObject/JArray) | Ні | Так |
| Reference Loop Handling | Так | Так |
| Підтримка DataTable, DataSet і складних типів | Ні | Так |
| Продуктивність | Краще | Хороша |
2. Приклад серіалізації та десеріалізації простого об’єкта
Візьмімо базову ігрову модель, яка поступово розвивається від лекції до лекції:
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
public bool IsAlive { get; set; }
public List<string> Inventory { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int X { get; set; }
public int Y { get; set; }
}
Тепер збережемо об’єкт Player у JSON і відновимо його:
using Newtonsoft.Json;
Player player1 = new Player
{
Name = "Aragorn",
Health = 100,
IsAlive = true,
Inventory = new List<string> { "sword", "bow", "healing potion" },
Position = new Position { X = 10, Y = 25 }
};
// Серіалізація в JSON-рядок
string json = JsonConvert.SerializeObject(player1, Formatting.Indented);
Console.WriteLine(json);
// Десеріалізація назад в об’єкт Player
Player player2 = JsonConvert.DeserializeObject<Player>(json);
Console.WriteLine($"Імʼя: {player2.Name}, здоровʼя: {player2.Health}");
Усе? Майже. Та це лише верхівка айсберга. Розберімося, чим Newtonsoft.Json цікавіший і гнучкіший за інші бібліотеки.
3. Форматування, налаштування та розширені параметри
Коли серіалізуєте об’єкт, можете отримати як компактний рядок, так і гарно відформатований JSON. Цю поведінку контролюють параметри та налаштування.
Приклад: різні варіанти форматування
// Зручний для читання JSON
string prettyJson = JsonConvert.SerializeObject(player1, Formatting.Indented);
// Компактний «мініфікований» JSON
string compactJson = JsonConvert.SerializeObject(player1, Formatting.None);
Налаштування серіалізації
За потреби передавайте JsonSerializerSettings для тонкого налаштування:
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore, // Пропускати поля зі значенням null
DefaultValueHandling = DefaultValueHandling.Ignore, // Пропускати поля зі значенням за замовчуванням
Formatting = Formatting.Indented
};
string customJson = JsonConvert.SerializeObject(player1, settings);
Ви отримуєте максимальний контроль над форматом виводу: потрібно пропустити порожні поля — без проблем. Хочете серіалізувати навіть приватні властивості — налаштуйте контракт.
4. Керування полями й властивостями за допомогою атрибутів
Newtonsoft.Json підтримує потужну систему атрибутів, що дає змогу керувати серіалізацією безпосередньо в класі.
JsonProperty — перейменування властивостей
Якщо JSON‑протокол вимагає іншу назву поля:
public class Player
{
[JsonProperty("player_name")]
public string Name { get; set; }
// ...
}
Отриманий JSON:
{ "player_name": "Aragorn", ... }
JsonIgnore — ігнорування властивості
public class Player
{
[JsonIgnore]
public int Health { get; set; }
}
Тепер властивість Health не буде серіалізовано.
JsonConverter — власні перетворення
Дозволяє зазначити, яким конвертером обробляти конкретне поле.
public class Player
{
[JsonConverter(typeof(InventoryToStringConverter))]
public List<string> Inventory { get; set; }
}
(Детальніше про конвертери — нижче.)
5. Робота з вкладеними об’єктами та колекціями
Json.NET чудово справляється з вкладеними об’єктами, масивами, колекціями, словниками.
Приклад: словники
public class GameStats
{
public Dictionary<string, int> Scores { get; set; }
}
GameStats stats = new GameStats
{
Scores = new Dictionary<string, int>
{
["Alice"] = 1023,
["Bob"] = 999
}
};
string statsJson = JsonConvert.SerializeObject(stats, Formatting.Indented);
Console.WriteLine(statsJson);
JSON матиме такий вигляд:
{
"Scores": {
"Alice": 1023,
"Bob": 999
}
}
6. Складні структури: циклічні посилання, самопосилальні об’єкти
Іноді об’єкти містять посилання один на одного. Newtonsoft.Json підтримує серіалізацію таких структур через спеціальні налаштування.
Приклад: розв’язання циклічних посилань
public class Person
{
public string Name { get; set; }
public Person Parent { get; set; }
public List<Person> Children { get; set; }
}
// Налаштовуємо серіалізацію для циклів
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore // або .Serialize
};
Person p1 = new Person { Name = "Тато" };
Person p2 = new Person { Name = "Син", Parent = p1 };
p1.Children = new List<Person> { p2 };
string json = JsonConvert.SerializeObject(p1, settings);
Console.WriteLine(json);
За замовчуванням, якщо залишити ReferenceLoopHandling = Error, ви отримаєте виняток. Це захищає від випадкової нескінченної серіалізації.
7. Робота з динамічними структурами: JObject, JArray
Коли структура JSON наперед невідома або змінюється на льоту, використовуйте динамічні об’єкти без суворої типізації в C#.
Основні типи:
- JObject — подання JSON‑об’єкта.
- JArray — подання масиву.
using Newtonsoft.Json.Linq;
// Перетворення рядка на JObject
string json = @"{ 'name': 'Aragorn', 'health': 100 }";
JObject obj = JObject.Parse(json);
Console.WriteLine((string)obj["name"]); // Aragorn
Console.WriteLine((int)obj["health"]); // 100
// Динамічне додавання властивостей
obj["class"] = "Ranger";
Console.WriteLine(obj.ToString());
Перебирання масиву
string jsonArr = @"['apple', 'banana', 'cherry']";
JArray array = JArray.Parse(jsonArr);
foreach (JToken item in array)
{
Console.WriteLine(item);
}
8. Підтримка версійності та обов’язкових полів
Формат JSON може змінюватися; деяких полів може не бути. Скористайтеся атрибутами та налаштуваннями:
- [JsonProperty(Required = Required.Always)] — вимагати наявність поля (інакше виняток).
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] — підставити значення за замовчуванням, якщо поле відсутнє.
public class Player
{
[JsonProperty(Required = Required.Always)]
public string Name { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[DefaultValue(50)]
public int Health { get; set; }
}
9. Перетворення дат і форматів часу
Для дат часто потрібен явний формат.
var dateSettings = new JsonSerializerSettings
{
DateFormatString = "yyyy-MM-dd"
};
string json = JsonConvert.SerializeObject(DateTime.Now, dateSettings);
Console.WriteLine(json); // "2024-06-15"
І назад під час десеріалізації:
string dateJson = "\"2024-06-15\""; // Зверніть увагу: це рядок у лапках!
DateTime dt = JsonConvert.DeserializeObject<DateTime>(dateJson);
Console.WriteLine(dt);
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ