1. Wprowadzenie
Newtonsoft.Json to taki "dinozaur" w dobrym znaczeniu. Był on de facto standardem do pracy z JSON w ekosystemie .NET przez wiele lat, jeszcze zanim pojawił się System.Text.Json. Miliony projektów, tysiące bibliotek i frameworków (nawet wiele części ASP.NET Core do niedawna) korzystały właśnie z Json.NET.
Jego zalety to:
- Bogactwo funkcji i elastyczność: Json.NET oferuje ogrom możliwości konfiguracji, atrybutów i sposobów dostosowania procesu serializacji i deserializacji. Potrafi robić rzeczy, które System.Text.Json albo robi z trudnością, albo wcale nie potrafi.
- Tolerancyjność wobec "nieidealnego" JSON: dane z zewnętrznych systemów nie zawsze są idealne — Json.NET częściej sobie z tym radzi i pozwala deserializować bez zbędnego bólu.
- Wsteczna kompatybilność: jeśli projekt lub biblioteka są zależne od Json.NET, umiejętność pracy z nim wciąż jest przydatna.
Oczywiście, System.Text.Json jest szybszy i zaprojektowany z myślą o nowoczesnych wymaganiach. Ale Newtonsoft.Json wciąż pozostaje potężnym narzędziem, zwłaszcza gdy potrzebujesz specyficznej logiki lub maksymalnej elastyczności.
Jak zainstalować Newtonsoft.Json?
Ponieważ to biblioteka zewnętrzna, dodaj pakiet przez NuGet:
- Otwórz projekt.
- Kliknij prawym przyciskiem na projekt w Solution Explorer.
- Wybierz "Manage NuGet Packages...".
- W polu wyszukiwania wpisz Newtonsoft.Json.
- Wybierz pakiet i kliknij "Install".
Po instalacji pojawi się nowe zależność — możesz używać Json.NET!
Porównanie możliwości Newtonsoft.Json i System.Text.Json
| Możliwość | System.Text.Json | Newtonsoft.Json |
|---|---|---|
| Prosta serializacja/deserializacja | Tak | Tak |
| Wsparcie atrybutów dla właściwości | Częściowo | Pełna |
| Kustomne konwertery | Tak | Tak |
| Praca z prywatnymi polami | Nie | Tak |
| Praca z dynamicznymi strukturami | Ograniczona | Tak |
| LINQ to JSON (JObject/JArray) | Nie | Tak |
| Reference Loop Handling | Tak | Tak |
| Wsparcie DataTable, DataSet i złożonych typów | Nie | Tak |
| Wydajność | Lepsza | Dobra |
2. Przykład serializacji i deserializacji prostego obiektu
Weźmy ogólną strukturę gry, która przewija się przez kolejne wykłady:
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; }
}
Teraz zapiszemy obiekt Player do JSON i przywrócimy go:
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 }
};
// Serializacja do ciągu JSON
string json = JsonConvert.SerializeObject(player1, Formatting.Indented);
Console.WriteLine(json);
// Deserializacja z powrotem do obiektu Player
Player player2 = JsonConvert.DeserializeObject<Player>(json);
Console.WriteLine($"Imię: {player2.Name}, zdrowie: {player2.Health}");
To wszystko? Prawie, ale to tylko wierzchołek góry lodowej. Zobaczmy, co czyni Newtonsoft.Json ciekawszym i bardziej elastycznym od innych bibliotek.
3. Formatowanie, ustawienia i zaawansowane opcje
Kiedy serializujesz obiekt, możesz uzyskać zarówno kompaktowy ciąg, jak i ładnie sformatowany JSON. Zachowanie kontrolujesz parametrami i ustawieniami.
Przykład: Różne warianty formatowania
// Ładnie czytelny JSON
string prettyJson = JsonConvert.SerializeObject(player1, Formatting.Indented);
// Kompaktowy "minifikowany" JSON
string compactJson = JsonConvert.SerializeObject(player1, Formatting.None);
Ustawienia serializacji
W razie potrzeby przekaż JsonSerializerSettings dla precyzyjnej konfiguracji:
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore, // Pomijaj pola o wartości null
DefaultValueHandling = DefaultValueHandling.Ignore, // Pomijaj pola z wartością domyślną
Formatting = Formatting.Indented
};
string customJson = JsonConvert.SerializeObject(player1, settings);
Masz pełną kontrolę nad formatem wyjścia: chcesz pominąć puste pola — proszę bardzo. Potrzebujesz serializować nawet prywatne właściwości — skonfiguruj kontrakt.
4. Zarządzanie polami i właściwościami za pomocą atrybutów
Newtonsoft.Json wspiera potężny system atrybutów, które konfigurują serializację bezpośrednio w klasie.
JsonProperty — Zmiana nazwy właściwości
Jeśli protokół JSON wymaga innej nazwy pola:
public class Player
{
[JsonProperty("player_name")]
public string Name { get; set; }
// ...
}
Wynikowy JSON:
{ "player_name": "Aragorn", ... }
JsonIgnore — Ignorowanie właściwości
public class Player
{
[JsonIgnore]
public int Health { get; set; }
}
Teraz pole Health zniknie podczas serializacji.
JsonConverter — Własne konwersje
Pozwala wskazać, którym konwerterem ma być obsługiwane konkretne pole.
public class Player
{
[JsonConverter(typeof(InventoryToStringConverter))]
public List<string> Inventory { get; set; }
}
(Więcej o konwerterach — poniżej.)
5. Praca z obiektami zagnieżdżonymi i kolekcjami
Json.NET świetnie radzi sobie z obiektami zagnieżdżonymi, tablicami, kolekcjami i słownikami.
Przykład: Słowniki
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 będzie wyglądać tak:
{
"Scores": {
"Alice": 1023,
"Bob": 999
}
}
6. Złożone struktury: cykliczne referencje, Self-Referencing Objects
Czasem obiekty zawierają referencje do siebie nawzajem. Newtonsoft.Json wspiera serializację takich struktur przez specjalne ustawienia.
Przykład: Rozwiązywanie cyklicznych referencji
public class Person
{
public string Name { get; set; }
public Person Parent { get; set; }
public List<Person> Children { get; set; }
}
// Konfigurujemy serializację dla cykli
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore // lub .Serialize
};
Person p1 = new Person { Name = "Tata" };
Person p2 = new Person { Name = "Syn", Parent = p1 };
p1.Children = new List<Person> { p2 };
string json = JsonConvert.SerializeObject(p1, settings);
Console.WriteLine(json);
Domyślnie, jeśli zostawisz ReferenceLoopHandling = Error, dostaniesz wyjątek. Chroni to przed przypadkową nieskończoną serializacją.
7. Praca ze strukturami dynamicznymi: JObject, JArray
Gdy struktura JSON nie jest znana z góry lub zmienia się w locie, użyj dynamicznych obiektów bez ścisłej typizacji po stronie C#.
Podstawowe typy:
- JObject — reprezentacja obiektu JSON.
- JArray — reprezentacja tablicy.
using Newtonsoft.Json.Linq;
// Konwersja stringa do JObject
string json = @"{ 'name': 'Aragorn', 'health': 100 }";
JObject obj = JObject.Parse(json);
Console.WriteLine((string)obj["name"]); // Aragorn
Console.WriteLine((int)obj["health"]); // 100
// Dynamiczne dodawanie właściwości
obj["class"] = "Ranger";
Console.WriteLine(obj.ToString());
Iteracja po tablicy
string jsonArr = @"['apple', 'banana', 'cherry']";
JArray array = JArray.Parse(jsonArr);
foreach (JToken item in array)
{
Console.WriteLine(item);
}
8. Wsparcie wersjonowania i pól obowiązkowych
Format JSON może się zmieniać, pola mogą nie występować. Użyj atrybutów i ustawień:
- [JsonProperty(Required = Required.Always)] — wymaga obecności pola (w przeciwnym razie wyjątek).
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] — podstawia wartość domyślną, jeśli pole nie występuje.
public class Player
{
[JsonProperty(Required = Required.Always)]
public string Name { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[DefaultValue(50)]
public int Health { get; set; }
}
9. Konwersja dat i formaty czasu
Praca z datami często wymaga jawnego formatu.
var dateSettings = new JsonSerializerSettings
{
DateFormatString = "yyyy-MM-dd"
};
string json = JsonConvert.SerializeObject(DateTime.Now, dateSettings);
Console.WriteLine(json); // "2024-06-15"
A także przy deserializacji:
string dateJson = "\"2024-06-15\""; // Uwaga: to jest string w cudzysłowie!
DateTime dt = JsonConvert.DeserializeObject<DateTime>(dateJson);
Console.WriteLine(dt);
GO TO FULL VERSION