JavaRush /Курси /C# SELF /Занурюємося у Newtonsoft.J...

Занурюємося у Newtonsoft.Json

C# SELF
Рівень 47 , Лекція 2
Відкрита

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
  }
]

Часто вживані налаштування:

Властивість Опис
NullValueHandling
Як обробляти властивості зі значенням null (пропускати їх чи записувати явне null)
DefaultValueHandling
Чи пропускати значення за замовчуванням
ReferenceLoopHandling
Що робити з циклічними посиланнями
DateFormatString
Формат рядкового представлення дат і часу

Приклад із пропуском 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) або змініть модель даних.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ