1. Einführung
Lange Zeit war im .NET-Ökosystem das großartige Drittanbieter-Paket Newtonsoft.Json (auch bekannt als Json.NET) das Hauptwerkzeug für die Arbeit mit JSON. Es ist mächtig, flexibel und wird immer noch breit eingesetzt. Aber mit den neuen Versionen .NET 9 und C# 14 hat Microsoft entschieden, dass es Zeit für einen eigenen, eingebauten, hochperformanten JSON-Serializer ist. So entstand System.Text.Json.
Warum dieser neue Ansatz? System.Text.Json wurde für moderne Anforderungen entwickelt und löst Probleme, die sich über Jahre der Nutzung von Drittanbieter-Bibliotheken angesammelt haben. Er ist auf maximale Geschwindigkeit und Sicherheit optimiert, eignet sich ideal für asynchrone Szenarien und Web-APIs und — das Beste — er muss nicht über NuGet installiert werden: alles ist bereits in der Plattform.
Natürlich ist Newtonsoft.Json nicht verschwunden, und wir werden ihn später anschauen. Aber für die meisten neuen Projekten ist System.Text.Json die Standardwahl. Macht euch bereit, wir bringen unseren Objekten bei, JSON zu sprechen!
2. Grundlagen der Arbeit mit System.Text.Json
Einfache Objektserialisierung
Also dann, fangen wir mit der Basis-Magie an. Wir serialisieren unser Objekt in einen JSON-String.
using System;
using System.Text.Json; // Nicht vergessen!
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
public bool IsAlive { get; set; }
}
// Irgendwo in Ihrem Programm:
Player player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };
// Serialisierung:
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // Gibt aus: {"Name":"Aragorn","Health":100,"IsAlive":true}
Kommentar: Wenn du gerade erst mit Serialisierung anfängst, zeigt dieses Beispiel, wie einfach alles ist: JsonSerializer.Serialize — und fertig.
Objektdeserialisierung
Lassen wir ein Objekt aus einem JSON-String wiederauferstehen:
string incomingJson = "{\"Name\":\"Legolas\",\"Health\":88,\"IsAlive\":true}";
Player player2 = JsonSerializer.Deserialize<Player>(incomingJson);
Console.WriteLine(player2.Name); // Legolas
Console.WriteLine(player2.Health); // 88
Console.WriteLine(player2.IsAlive); // true
Kommentar: Wenn die JSON-Struktur mit deiner Klasse übereinstimmt — läuft alles wie geschmiert. Wenn nicht — können Ausnahmen auftreten oder Standardwerte gesetzt werden.
3. Arbeitsprinzipien und Aufbau der Serialisierung
Wie das Mapping passiert
System.Text.Json verwendet standardmäßig dieselben Eigenschaftsnamen wie in der Klasse. Die Groß-/Kleinschreibung wird beachtet! Wenn im JSON health statt Health steht, klappt die Deserialisierung nicht — die Eigenschaft bleibt beim Standardwert (0, false oder null).
Zum Beispiel:
// JSON mit kleingeschriebenen Keys:
string badJson = "{\"name\":\"Gimli\",\"health\":120,\"isAlive\":true}";
Player player3 = JsonSerializer.Deserialize<Player>(badJson);
Console.WriteLine(player3.Name); // leer
Console.WriteLine(player3.Health); // 0
Console.WriteLine(player3.IsAlive); // false
Interessante Tatsache: Viele APIs schreiben Keys in camelCase (health), während in C# PascalCase (Health) üblich ist. Das lässt sich mit Einstellungen lösen (siehe unten).
4. Steuerung der Serialisierung — Optionen und Einstellungen
JSON formatieren: "menschenlesbare" Ausgabe
Manchmal willst du keinen kompakten, sondern schön formatierten JSON — für Konfigurationen oder Logs.
var options = new JsonSerializerOptions
{
WriteIndented = true // Einrückungen hinzufügen
};
string prettyJson = JsonSerializer.Serialize(player, options);
Console.WriteLine(prettyJson);
/*
{
"Name": "Frodo",
"Health": 50,
"IsAlive": true,
"Inventory": [
"Ring",
"Bread",
"Torch"
],
"Position": {
"X": 5,
"Y": 15
}
}
*/
Die Eigenschaft WriteIndented sorgt dafür, dass der Serializer Einrückungen und Zeilenumbrüche einfügt.
Steuerung des Namensstils (CamelCase vs PascalCase)
Wenn du mit einer Web-API arbeitest, die alle Keys in camelCase hat, aktiviere die Namenspolitik:
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
string camelCaseJson = JsonSerializer.Serialize(player, options);
// {"name":"Frodo","health":50,"isAlive":true,"inventory":["Ring","Bread","Torch"],"position":{"x":5,"y":15}}
Dann werden solche Keys auch bei der Deserialisierung korrekt gemappt:
string apiJson = "{\"name\":\"Bilbo\",\"health\":40,\"isAlive\":true,\"inventory\":[\"Mug\"],\"position\":{\"x\":10,\"y\":5}}";
Player bilbo = JsonSerializer.Deserialize<Player>(apiJson, options);
Console.WriteLine(bilbo.Name); // Bilbo
5. Nützliche Feinheiten
Verwendung des Attributs [JsonIgnore]
Manchmal sollen nicht alle Eigenschaften serialisiert werden — z.B. private Daten oder temporäre berechnete Werte.
using System.Text.Json.Serialization;
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
[JsonIgnore] // Diese Eigenschaft kommt nicht ins JSON
public bool IsSecretCharacter { get; set; }
}
Jetzt beim Serialisieren:
var player = new Player { Name = "Boromir", Health = 80, IsSecretCharacter = true };
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // {"Name":"Boromir","Health":80}
Bei der Deserialisierung bekommt IsSecretCharacter den Standardwert (false).
Verwendung von [JsonPropertyName("...")]
Angenommen, in deinem Code heißt die Eigenschaft IsAlive, aber im JSON soll sie "status" heißen:
using System.Text.Json.Serialization;
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
[JsonPropertyName("status")]
public bool IsAlive { get; set; }
}
Die Serialisierung sieht dann so aus:
var player = new Player { Name = "Pippin", Health = 60, IsAlive = false };
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // {"Name":"Pippin","Health":60,"status":false}
Und bei der Deserialisierung füllt der Key "status" korrekt die Eigenschaft IsAlive.
Eingebaute Einschränkungen und Sicherheitsbesonderheiten
- Standardmäßig werden nur öffentliche Eigenschaften mit Gettern/Settern serialisiert; private Felder/Eigenschaften werden ignoriert.
- Bei zyklischen Referenzen wird eine Ausnahme geworfen: "A possible object cycle was detected".
- Im Gegensatz zu Newtonsoft.Json verlässt sich der Standardserializer weniger auf "magische" Tricks mit Typen — dafür ist er sicherer und schneller für typische Szenarien.
6. Häufige Fehler und Fallstricke
Du änderst versehentlich den Namen einer Eigenschaft im JSON und vergisst, den Code anzupassen — folglich bekommt die Eigenschaft den Standardwert (null, 0, false).
Im JSON fehlt ein benötigtes Feld — die entsprechende Eigenschaft des Objekts bleibt der Defaultwert (siehe Dokumentation).
Die Eigenschaft hat keinen öffentlichen Setter — beim Deserialisieren wird sie nicht gesetzt.
Die Struktur verschachtelter Klassen oder Collections wurde geändert — Deserialisierung kann fehlschlagen oder unerwartete Ergebnisse liefern.
Gleiche Namen auf unterschiedlichen Verschachtelungsebenen (in Eltern- und Kindobjekten) verwirren und erschweren das Debugging.
Manchmal liegt die Ursache an der Plattformversion: ältere Versionen von System.Text.Json hatten Probleme mit einigen Typen (z.B. Dictionary, DateTime, enum), aber in .NET 7/8/9 ist vieles behoben.
GO TO FULL VERSION