CodeGym /Kursy /C# SELF /Zanurzamy się w Newtonsoft...

Zanurzamy się w Newtonsoft.Json

C# SELF
Poziom 47 , Lekcja 2
Dostępny

1. Wprowadzenie

Jeśli dopiero zaczynasz zapoznawać się z serializacją w .NET, całkiem naturalne pytanie brzmi: po co kolejna biblioteka, skoro jest już wbudowany System.Text.Json? Odpowiedź prosta: Newtonsoft.Json pojawił się wcześniej i przez lata rozwinął się w nadal najbardziej elastyczne narzędzie do JSON w .NET.

Stał się de-facto standardem, bo obsługuje skomplikowane scenariusze: customowe kontrakty, potężne konwertery, serializację prywatnych pól, LINQ do JSON, dynamiczne obiekty (JObject), elastyczną obsługę cyklicznych odniesień i mnóstwo formatów dat/czasu. Wiele bibliotek i API wciąż używa Json.NET "pod maską".

Ważne: w pewnych scenariuszach wbudowany System.Text.Json wciąż ustępuje możliwościom Newtonsoft.Json, więc nauka Json.NET jest nadal przydatna.

Jak dodać Newtonsoft.Json (Json.NET)

Zainstaluj pakiet przez NuGet:

dotnet add package Newtonsoft.Json

Dodaj przestrzeń nazw:

using Newtonsoft.Json;

2. Serializacja z użyciem Newtonsoft.Json

Weźmy tradycyjną klasę Person:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Serializacja obiektu do stringa JSON

Person person = new Person { Name = "Iwan", Age = 30 };

// Serializacja do JSON
string json = JsonConvert.SerializeObject(person);

Console.WriteLine(json);
// Wynik: {"Name":"Iwan","Age":30}

Deserializacja JSON z powrotem do obiektu

string json = "{\"Name\":\"Iwan\",\"Age\":30}";

Person person = JsonConvert.DeserializeObject<Person>(json);

Console.WriteLine($"{person.Name}, {person.Age}");
// Wynik: Iwan, 30

Co się dzieje "pod maską"?

Newtonsoft.Json iteruje po wszystkich publicznych właściwościach (public), serializuje je do JSON i zapisuje w stringu. Przy deserializacji dopasowuje klucze z JSON do nazw właściwości i wypełnia obiekt.

3. Serializacja kolekcji obiektów

Serializacja kolekcji i tablic

Z kolekcjami działa to "od ręki".

List<Person> people = new List<Person>
{
    new Person { Name = "Iwan", Age = 30 },
    new Person { Name = "Maria", Age = 25 }
};

string json = JsonConvert.SerializeObject(people);
// Wynik: [{"Name":"Iwan","Age":30},{"Name":"Maria","Age":25}]

List<Person> deserialized = JsonConvert.DeserializeObject<List<Person>>(json);
// Masz z powrotem listę Person!

Szczegóły przy serializacji i deserializacji słowników

var dict = new Dictionary<string, int>
{
    ["apple"] = 2,
    ["banana"] = 5
};

string json = JsonConvert.SerializeObject(dict);
// Wynik: {"apple":2,"banana":5}

var deserializedDict = JsonConvert.DeserializeObject<Dictionary<string,int>>(json);
// Wszystko działa!

Jeśli klucze nie są stringami (np. Dictionary<int,string>), Json.NET zamieni klucze na stringi przy serializacji, a przy deserializacji spróbuje skonwertować z powrotem. Dla skomplikowanych kluczy (np. Guid) pewniej użyć Dictionary<string, TValue>.

4. Praca z obiektami zagnieżdżonymi i hierarchiami

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 = "Iwan", Age = 30 },
    Products = new List<Product>
    {
        new Product { Title = "Laptop", Price = 50000.0 },
        new Product { Title = "Mysz", Price = 1500.0 }
    }
};

string json = JsonConvert.SerializeObject(order);

Console.WriteLine(json);

Rezultat: zagnieżdżone obiekty będą poprawnie przedstawione wewnątrz struktury JSON.

5. Konfigurowanie serializacji za pomocą atrybutów

Ignorowanie właściwości

public class Person
{
    public string Name { get; set; }

    [JsonIgnore]
    public int Age { get; set; }
}

Teraz Age nie trafi do JSON.

Zmiana nazwy właściwości

public class Person
{
    [JsonProperty("full_name")]
    public string Name { get; set; }
}

W JSON nazwa będzie "full_name".

6. Elastyczna konfiguracja: JsonSerializerSettings

Formatowanie (ładny, wieloliniowy JSON):

string json = JsonConvert.SerializeObject(
    people,
    Formatting.Indented
);

Rezultat:

[
  {
    "Name": "Iwan",
    "Age": 30
  },
  {
    "Name": "Maria",
    "Age": 25
  }
]

Często używane ustawienia:

Właściwość Opis
NullValueHandling
Jak obsługiwać właściwości null (pominąć je czy zapisać explicite null)
DefaultValueHandling
Czy pomijać wartości domyślne
ReferenceLoopHandling
Co robić z cyklicznymi referencjami
DateFormatString
Format string dla dat i czasu

Przykład pomijania null:

string json = JsonConvert.SerializeObject(
    person,
    new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }
);

7. Praca z dynamicznymi strukturami JSON: JObject, JArray i inne

Gdy struktura JSON nie jest znana z góry, użyj typów z Newtonsoft.Json.Linq:

using Newtonsoft.Json.Linq;

string json = @"{
    'Name': 'Iwan',
    'Age': 30,
    'Skills': ['C#', 'SQL', 'JSON']
}";

JObject obj = JObject.Parse(json);

Console.WriteLine(obj["Name"]);     // Iwan
Console.WriteLine(obj["Skills"][0]); // C#

Tworzenie JSON "w locie":

var jObj = new JObject
{
    ["Status"] = "Success",
    ["Result"] = new JArray("item1", "item2", "item3")
};

Console.WriteLine(jObj.ToString(Formatting.Indented));

JObject i JArray — reprezentacje obiektu JSON i tablicy, w praktyce wygodne kolekcje.

8. Przydatne niuanse

Cykliczne referencje

Newtonsoft.Json potrafi je elastycznie obsługiwać:

var settings = new JsonSerializerSettings
{
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};

string json = JsonConvert.SerializeObject(obj, settings);

W ten sposób cykle zostaną pominięte. Aby zachować referencje można użyć ReferenceLoopHandling.Serialize razem z [JsonObject(IsReference = true)].

Serializacja anonimowych i dynamicznych obiektów

var anon = new { Foo = 42, Bar = "Hello" };
string json = JsonConvert.SerializeObject(anon);
// {"Foo":42,"Bar":"Hello"}

Walidacja i obsługa błędów

try
{
    Person p = JsonConvert.DeserializeObject<Person>(brokenJson);
}
catch (JsonSerializationException ex)
{
    Console.WriteLine("Błąd podczas deserializacji: " + ex.Message);
}

Porównanie Newtonsoft.Json vs System.Text.Json

Newtonsoft.Json (Json.NET) System.Text.Json (.NET)
Wsparcie .NET .NET Framework/Standard/6+ .NET Core 3.0+ / .NET 5/6/9
LINQ do JSON (JObject/JArray) Tak Nie
Elastyczna konfiguracja Bardzo szeroka Ograniczona
Atrybuty ([JsonProperty], ...) Tak Tak (częściowo, mniej możliwości)
Wsparcie prywatnych właściwości Tak Nie
Szybkość Wolniejsze w niektórych scenariuszach Szybsze
Zaawansowane konwertery Tak Tak (mniej elastyczne, póki co)
Wsparcie DataTable, DataSet Tak Nie
Dokumentacja i przykłady Mnóstwo Rośnie

9. Błędy początkujących i typowe pułapki

Błąd #1: właściwości stają się null po deserializacji.
Często właściwość nie ma settera albo brakuje konstruktora bezparametrowego — serializator nie ma jak wypełnić obiektu.

Błąd #2: niezgodność nazw właściwości między JSON a klasą.
Jeśli w JSON pole ma nazwę "fullName", a w klasie — FullName, użyj [JsonProperty] lub skonfiguruj ContractResolver do mapowania nazw.

Błąd #3: serializacja działa domyślnie tylko z publicznymi właściwościami.
Prywatne pola/właściwości nie są serializowane bez dodatkowych ustawień. Potrzebne są konwertery lub specjalne resolvery/kontrakty.

Błąd #4: cykliczne referencje prowadzą do StackOverflowException.
Wzajemne referencje obiektów mogą zapętlić serializację bez właściwej konfiguracji. Konfiguruj obsługę referencji (np. ReferenceLoopHandling) lub zmień model danych.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION