Neu in .NET 10.0 [39]: JSON Patch

System.Text.Json beherrscht nun den JSON Patch-Standard nach RFC 6902.

vorlesen Druckansicht
Verkehrsschild mit Aufschrift .NET

(Bild: Pincasso / Shutterstock.com)

Lesezeit: 3 Min.
Von
  • Dr. Holger Schwichtenberg
close notice

This article is also available in English. It was translated with technical assistance and editorially reviewed before publication.

Die JSON-Bibliothek System.Text.Json beherrscht nun den JSON Patch-Standard nach RFC 6902. JSON Patch ist ein standardisiertes Format, um Änderungen an JSON-Dokumenten in Form einer Sequenz von Operationen auszudrücken. Jede Operation spezifiziert dabei den Typ der Änderung (add, remove, replace, move, copy oder test) sowie den Pfad innerhalb des JSON-Dokuments, auf den sich die Modifikation bezieht.

Der Dotnet-Doktor – Holger Schwichtenberg
Der Dotnet-Doktor – Holger Schwichtenberg

Dr. Holger Schwichtenberg ist technischer Leiter des Expertennetzwerks www.IT-Visions.de, das mit 53 renommierten Experten zahlreiche mittlere und große Unternehmen durch Beratungen und Schulungen sowie bei der Softwareentwicklung unterstützt. Durch seine Auftritte auf zahlreichen nationalen und internationalen Fachkonferenzen sowie mehr als 90 Fachbücher und mehr als 1500 Fachartikel gehört Holger Schwichtenberg zu den bekanntesten Experten für .NET und Webtechniken in Deutschland.

Durch diese strukturierte Beschreibung lassen sich inkrementelle Änderungen effizient übertragen, ohne das gesamte Dokument erneut austauschen zu müssen. Insbesondere in RESTful Web-APIs ist JSON Patch ein etabliertes Mittel, um partielle Updates über HTTP-PATCH zu implementieren.

Heise-Konferenz: betterCode() .NET 11.0
betterCode() .NET 11.0

(Bild: King / stock.adobe.com)

Das ist neu in .NET 11.0: Dr. Holger Schwichtenberg und weitere Experten präsentieren am 17. November 2026 auf der Online-Konferenz betterCode() .NET 11.0 die Änderungen für Entwicklerinnen und Entwickler. Tickets zum Frühbucherpreis sind im Online-Shop verfügbar.

Ein wesentliches Merkmal von JSON Patch ist die strikte Definition der Pfadangaben über JSON Pointer (RFC 6901), siehe Beispiel in folgender Tabelle. Dadurch werden Änderungen eindeutig und positionsgenau adressiert. JSON Patch eignet sich auch für komplexe Datenmodelle mit tiefen Verschachtelungen oder Arrays.

Operation Beschreibung Felder Beispiel
add FĂĽgt einen Wert an einem bestimmten Pfad hinzu. path, value { "op": "add", "path": "/a/b/c", "value": "foo" }
remove Entfernt den Wert an einem bestimmten Pfad. path { "op": "remove", "path": "/a/b/c" }
replace Ersetzt den Wert an einem Pfad durch einen neuen. path, value { "op": "replace", "path": "/a/b/c", "value": 42 }
move Verschiebt einen Wert von einem Pfad zu einem anderen. from, path { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }
copy Kopiert einen Wert von einem Pfad zu einem anderen. from, path { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" }
test PrĂĽft, ob ein Wert an einem Pfad einem bestimmten Wert entspricht. path, value { "op": "test", "path": "/a/b/c", "value": "foo" }

Der Vorteil von JSON Patch zeigt sich insbesondere bei großen Objekten und eingeschränkter Bandbreite, da nur inkrementelle Änderungen übertragen werden. Bei sehr kleinen Datenstrukturen kann der Mehraufwand für das Erzeugen und Verarbeiten eines Patch-Dokuments größer sein als der Gewinn gegenüber einem vollständigen Update.

Bisher musste man für JSON Patch die ältere Community-Bibliothek Newtonsoft.Json einsetzen. Nun geht es auch mit der neueren Microsoft-Bibliothek System.Text.Json.

FĂĽr JSON Patch mit System.Text.Json gibt es eine Erweiterung fĂĽr System.Text.Json in Form des neuen NuGet-Pakets Microsoft.AspNetCore.JsonPatch.SystemTextJson, das erstmalig zusammen mit .NET 10.0 Preview 4 erschienen ist.

Trotz des „AspNetCore“ im Namen funktioniert dieses Zusatzpaket auch außerhalb von ASP.NET Core.

Microsoft verspricht in der neuen Implementierung „eine verbesserte Leistung und weniger Speichernutzung im Vergleich zu der existierenden Implementierung in Newtonsoft.Json“ in Verbindung mit einem ähnlichen Design wie bei Newtonsoft.Json, einschließlich der Aktualisierung eingebetteter Objekte und Arrays. Bisher nicht verfügbar ist JSON Patch für dynamische Objekte, weil Newtonsoft.Json dafür Reflection einsetzt. System.Text.Json soll aber auch mit dem NativeAOT-Compiler funktionieren.

Videos by heise

Leider funktioniert das neue JSON-Patch-Paket nicht in Verbindung mit dem NativeAOT-Compiler des modernen .NET, sondern nur mit dem Just-in-Time-Compiler.

Im NuGet-Paket Microsoft.AspNetCore.JsonPatch.SystemTextJson gibt es eine Klasse Microsoft.AspNetCore.JsonPatch.SystemTextJson.JsonPatchDocument<T> mit einer Methode ApplyTo(obj), die eine JSON-Patch-Operation auf das übergebene Objekt anwendet. Vor dem Anwenden von ApplyTo() lädt man das JSON-Patch-Dokument via JsonSerializer.Deserialize<JsonPatchDocument<Person>>() in eine Instanz von JsonPatchDocument<T>.

ApplyTo() erwartet im zweiten Parameter vom Typ Action<JsonPatchError> eine Methode, die als Parameter Objekte vom Typ JsonPatchError entgegennimmt. Als JsonPatchError signalisiert ApplyTo() fehlerhafte Operationsnamen, fehlerhafte Pfade und fehlgeschlagene Test-Operationen. Wenn bei einer oder mehreren Operationen ein Fehler auftritt, werden die ĂĽbrigen fehlerfreien Operationen dennoch verarbeitet.

Folgender Code zeigt ein Beispiel fĂĽr eine Objekthierarchie:

public class Person
 {
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string PrivateWebsite { get; set; }
  public string CompanyWebsite { get; set; }
  public string PrivateEmail { get; set; }
  public string OfficeEmail { get; set; }
  public List<PhoneNumber> PhoneNumbers { get; set; } = new();
  public Address Address { get; set; }
 }
 
 public class Address
 {
  public string Street { get; set; }
  public string City { get; set; }
  public string State { get; set; }
  public string ZipCode { get; set; }
 }
 
 public enum PhoneNumberType
 {
  Mobile,
  Home,
  Work,
  Other
 }
 
 public class PhoneNumber
 {
  public string? Number { get; set; }
  public PhoneNumberType Type { get; set; }
 }

Der folgende Codeausschnitt verändert die Objekthierarchie mit JSON Patch:

public void JSONPatchDemo()
 {
  CUI.Demo();

  // Originalobjekt
  var person = new Person
  {
   FirstName = "Holger",
   LastName = "Schwichtenberg",
   PrivateEmail = "buero@IT-Visions.de",
   PrivateWebsite = "www.dotnet-doktor.de",
   CompanyWebsite = "www.IT-Visions.de",
   PhoneNumbers = [new PhoneNumber() { Number = "0201 649590-0", Type = PhoneNumberType.Work }],
   Address = new Address
   {
    Street = "Fahrenberg 40b",
    City = "Essen",
    State = "NRW"
   }
  };

  CUI.H2("Ausgabe des Objekts in seinem alten Zustand");
  CUI.Print(ITVisions.ObjectDumper.Dump(person));

  #region ------------------- Beispiel 1: Anwenden eines JSON-Patch-Dokuments auf ein Objekt
  // JSON-Patch-Dokument: Erlaubte Operationen nach RFC 6902 sind: "add", "remove", "replace", "move", "copy" und "test"
  string jsonPatch = """
[
    { "op": "test", "path": "/FirstName", "value": "Holger" },
    { "op": "test", "path": "/LastName", "value": "Schwichtenberg" },
    { "op": "replace", "path": "/FirstName", "value": "Dr. Holger" },
    { "op": "move", "from": "/PrivateEmail", "path": "/OfficeEmail" },
    { "op": "remove", "path": "/PrivateWebsite" },
    { "op": "add", "path": "/Address/ZipCode", "value": "45257" },
    { "op": "add", "path": "/PhoneNumbers/-", "value": { "Number": "0201 649590-40" } }
]
""";

  // JSON-Patch-Dokument laden
  JsonPatchDocument<Person> patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);

  // JSON-Patch-Dokument anwenden auf das Person-Objekt mit ApplyTo()
  patchDoc!.ApplyTo(person, patchError => CUI.PrintError(patchError.ErrorMessage));

  CUI.H2("Ausgabe des Objekts in seinem neuen Zustand");
  CUI.Print(ITVisions.ObjectDumper.Dump(person));
#endregion
}

Ausgabe des Beispielcodes (Abb. 1).

Alternativ kann man das Objekt auch als JSON ausgeben. Das habe ich im obigen Listing bewusst ausgelassen, damit nicht der Eindruck entsteht, man mĂĽsse das Objekt als JSON weiterverwenden. Folgender Code zeigt die Ausgabe als JSON:

// Ausgabe des Resultats
var serializerOptions = new JsonSerializerOptions()
  {
   IndentSize = 1, WriteIndented=true, IncludeFields = true,
  };
 
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));

Ausgabe des geänderten Objekts im JSON-Format (Abb. 2).

(rme)