Migrieren von Newtonsoft.Json zu System.Text.Json

In diesem Artikel wird erläutert, wie Sie von Newtonsoft.Json zu System.Text.Json migrieren.

Der System.Text.Json-Namespace bietet Funktionalitäten zum Serialisieren in und Deserialisieren aus JSON (JavaScript Object Notation). Die Bibliothek System.Text.Json ist ab .NET Core 3.1 in der Runtime enthalten. Installieren Sie für andere Zielframeworks das NuGet-Paket System.Text.Json. Das Paket unterstützt:

  • .NET Standard 2.0 und höhere Versionen
  • .NET Framework 4.7.2 und höhere Versionen
  • .NET Core 2.0, 2.1 und 2.2

System.Text.Json konzentriert sich hauptsächlich auf Leistung, Sicherheit und Einhaltung von Standards. Es weist einige wesentliche Unterschiede beim Standardverhalten auf und zielt nicht auf Featureparität mit Newtonsoft.Json ab. In einigen Szenarios verfügt System.Text.Json aktuell über keine integrierte Funktionalität, doch gibt es dafür empfohlene Problemumgehungen. In anderen Szenarien sind Problemumgehungen nicht praktikabel.

Wir investieren in das Hinzufügen der Features, die am häufigsten angefordert werden. Wenn Ihre Anwendung von einem fehlenden Feature abhängig ist, sollten Sie erwägen, im GitHub-Repository „dotnet/runtime“ ein Issue zu registrieren, um herauszufinden, ob eine Unterstützung für Ihr Szenario hinzugefügt werden kann.

Der Großteil dieses Artikels behandelt die Verwendungsweise der JsonSerializer-API, aber er enthält auch Anleitungen zur Verwendung der Typen JsonDocument (der das Dokumentobjektmodell (DOM) darstellt), Utf8JsonReader und Utf8JsonWriter.

In Visual Basic können Sie Utf8JsonReader nicht verwenden, was auch bedeutet, dass Sie keine benutzerdefinierten Konverter schreiben können. Für die meisten der hier vorgestellten Problemumgehungen müssen Sie benutzerdefinierte Konverter schreiben. Sie können einen benutzerdefinierten Konverter in C# schreiben und in einem Visual Basic-Projekt registrieren. Weitere Informationen finden Sie unter Visual Basic-Support.

Übersicht über die Unterschiede

In der folgenden Tabelle sind die Newtonsoft.Json-Funktionen mit ihren Entsprechungen in System.Text.Json aufgeführt. Die Entsprechungen lassen sich in die folgenden Kategorien einteilen:

  • ✔️ Unterstützt durch integrierte Funktionalität Um ein ähnliches Verhalten von System.Text.Json zu erzielen, ist möglicherweise die Verwendung eines Attributs oder einer globalen Option erforderlich.
  • ⚠ Nicht unterstützt, Problemumgehung ist möglich Die Problemumgehungen sind benutzerdefinierte Konverter, die möglicherweise keine vollständige Parität mit der Newtonsoft.Json-Funktionalität bereitstellen. Für einige davon wird Beispielcode als Beispiele bereitgestellt. Wenn Sie von diesen Newtonsoft.Json-Funktionen abhängig sind, erfordert die Migration Änderungen an Ihren .NET-Objektmodellen oder andere Codeänderungen.
  • ❌ Nicht unterstützt, Problemumgehung ist nicht praktikabel oder nicht möglich Wenn Sie von diesen Newtonsoft.Json-Funktionen abhängig sind, ist die Migration nicht ohne wesentliche Änderungen möglich.
Feature in Newtonsoft.Json Äquivalent in System.Text.Json
Standardmäßige Deserialisierung ohne Berücksichtigung von Groß-/Kleinschreibung ✔️ Globale Einstellung „PropertyNameCaseInsensitive“
Eigenschaftsnamen mit Camel-Case-Schreibweise ✔️ Globale Einstellung „PropertyNamingPolicy“
Eigenschaftsnamen mit Snake-Case-Schreibweise ✔️ Snake-Case-Benennungsrichtlinie
Minimales Escapen von Zeichen ✔️ Strenges Escapen von Zeichen, konfigurierbar
Globale Einstellung NullValueHandling.Ignore ✔️ DefaultIgnoreCondition (globale Option)
Kommentare zulassen ✔️ Globale Einstellung „ReadCommentHandling“
Nachfolgende Kommas zulassen ✔️ Globale Einstellung „AllowTrailingCommas“
Registrierung benutzerdefinierter Konverter ✔️ Rangordnung weicht ab
Keine standardmäßige maximale Tiefe ✔️ Standardmäßige maximale Tiefe von  64, konfigurierbar
Globale Einstellung PreserveReferencesHandling ✔️ ReferenceHandling (globale Einstellung)
Serialisieren oder Deserialisieren von Zahlen in Anführungszeichen ✔️ NumberHandling (globale Einstellung), [JsonNumberHandling] (Attribut)
Deserialisieren in unveränderliche Klassen und Strukturen ✔️ JsonConstructor, C# 9-Datensätze
Unterstützung für Felder ✔️ IncludeFields (globale Einstellung), [JsonInclude] (Attribut)
Globale Einstellung DefaultValueHandling ✔️ DefaultIgnoreCondition (globale Einstellung)
Einstellung NullValueHandling für [JsonProperty] ✔️ JsonIgnore (Attribut)
Einstellung DefaultValueHandling für [JsonProperty] ✔️ JsonIgnore (Attribut)
Deserialisieren von Dictionary mit Nicht-Zeichenfolgen-Schlüssel ✔️ Unterstützt
Unterstützung für nicht öffentliche Setter und Getter von Eigenschaften ✔️ JsonInclude (Attribut)
[JsonConstructor]-Attribut ✔️ [JsonConstructor] (Attribut)
Globale Einstellung ReferenceLoopHandling ✔️ ReferenceHandling (globale Einstellung)
Rückrufe ✔️ Rückrufe
„NaN“, „Infinity“, „-Infinity“ ✔️ Unterstützt
Required-Einstellung für [JsonProperty]-Attribut ✔️ [JsonRequired]-Attribut und erforderlicher C#-Modifizierer
DefaultContractResolver zum Ignorieren von Eigenschaften ✔️ „DefaultJsonTypeInfoResolver“-Klasse
Polymorphe Serialisierung ✔️ [JsonDerivedType]-Attribut
Polymorphe Deserialisierung ✔️ Typdiskriminator für das [JsonDerivedType]-Attribut
Deserialisieren des Enumerationswerts für Zeichenfolgen ✔️ Deserialisieren von Enumerationswerten für Zeichenfolgen
Globale Einstellung MissingMemberHandling ✔️ Behandeln fehlender Member
Auffüllen von Eigenschaften ohne Setter ✔️ Auffüllen von Eigenschaften ohne Setter
Globale Einstellung ObjectCreationHandling ✔️ Wiederverwenden statt Ersetzen von Eigenschaften
Unterstützung für ein breites Spektrum von Typen Einige Typen benötigen benutzerdefinierte Konverter
Deserialisieren eines abgeleiteten Typs in object-Eigenschaften Nicht unterstützt, Problemumgehung, Beispiel
Deserialisieren des JSON-Literals null in Non-Nullable-Werttypen Nicht unterstützt, Problemumgehung, Beispiel
Einstellungen DateTimeZoneHandling und DateFormatString Nicht unterstützt, Problemumgehung, Beispiel
JsonConvert.PopulateObject-Methode Nicht unterstützt, Problemumgehung
Unterstützung für System.Runtime.Serialization-Attribute Nicht unterstützt, Problemumgehung, Beispiel
JsonObjectAttribute Nicht unterstützt, Problemumgehung
Eigenschaftsnamen ohne Anführungszeichen zulassen Vom Design nicht unterstützt
Einschließen von Zeichenfolgenwerten in einfache Anführungszeichen zulassen Vom Design nicht unterstützt
JSON-Werte, die keine Zeichenfolgen sind, für Zeichenfolgeneigenschaften zulassen Vom Design nicht unterstützt
Globale Einstellung TypeNameHandling.All Vom Design nicht unterstützt
Unterstützung für JsonPath-Abfragen Nicht unterstützt
Konfigurierbare Grenzwerte Nicht unterstützt
Feature in Newtonsoft.Json Äquivalent in System.Text.Json
Standardmäßige Deserialisierung ohne Berücksichtigung von Groß-/Kleinschreibung ✔️ Globale Einstellung „PropertyNameCaseInsensitive“
Eigenschaftsnamen mit Camel-Case-Schreibweise ✔️ Globale Einstellung „PropertyNamingPolicy“
Minimales Escapen von Zeichen ✔️ Strenges Escapen von Zeichen, konfigurierbar
Globale Einstellung NullValueHandling.Ignore ✔️ DefaultIgnoreCondition (globale Option)
Kommentare zulassen ✔️ Globale Einstellung „ReadCommentHandling“
Nachfolgende Kommas zulassen ✔️ Globale Einstellung „AllowTrailingCommas“
Registrierung benutzerdefinierter Konverter ✔️ Rangordnung weicht ab
Keine standardmäßige maximale Tiefe ✔️ Standardmäßige maximale Tiefe von  64, konfigurierbar
Globale Einstellung PreserveReferencesHandling ✔️ ReferenceHandling (globale Einstellung)
Serialisieren oder Deserialisieren von Zahlen in Anführungszeichen ✔️ NumberHandling (globale Einstellung), [JsonNumberHandling] (Attribut)
Deserialisieren in unveränderliche Klassen und Strukturen ✔️ JsonConstructor, C# 9-Datensätze
Unterstützung für Felder ✔️ IncludeFields (globale Einstellung), [JsonInclude] (Attribut)
Globale Einstellung DefaultValueHandling ✔️ DefaultIgnoreCondition (globale Einstellung)
Einstellung NullValueHandling für [JsonProperty] ✔️ JsonIgnore (Attribut)
Einstellung DefaultValueHandling für [JsonProperty] ✔️ JsonIgnore (Attribut)
Deserialisieren von Dictionary mit Nicht-Zeichenfolgen-Schlüssel ✔️ Unterstützt
Unterstützung für nicht öffentliche Setter und Getter von Eigenschaften ✔️ JsonInclude (Attribut)
[JsonConstructor]-Attribut ✔️ [JsonConstructor] (Attribut)
Globale Einstellung ReferenceLoopHandling ✔️ ReferenceHandling (globale Einstellung)
Rückrufe ✔️ Rückrufe
„NaN“, „Infinity“, „-Infinity“ ✔️ Unterstützt
Required-Einstellung für [JsonProperty]-Attribut ✔️ [JsonRequired]-Attribut und erforderlicher C#-Modifizierer
DefaultContractResolver zum Ignorieren von Eigenschaften ✔️ „DefaultJsonTypeInfoResolver“-Klasse
Polymorphe Serialisierung ✔️ [JsonDerivedType]-Attribut
Polymorphe Deserialisierung ✔️ Typdiskriminator für das [JsonDerivedType]-Attribut
Deserialisieren des Enumerationswerts für Zeichenfolgen ✔️ Deserialisieren von Enumerationswerten für Zeichenfolgen
Unterstützung für ein breites Spektrum von Typen Einige Typen benötigen benutzerdefinierte Konverter
Deserialisieren eines abgeleiteten Typs in object-Eigenschaften Nicht unterstützt, Problemumgehung, Beispiel
Deserialisieren des JSON-Literals null in Non-Nullable-Werttypen Nicht unterstützt, Problemumgehung, Beispiel
Einstellungen DateTimeZoneHandling und DateFormatString Nicht unterstützt, Problemumgehung, Beispiel
JsonConvert.PopulateObject-Methode Nicht unterstützt, Problemumgehung
Globale Einstellung ObjectCreationHandling Nicht unterstützt, Problemumgehung
Hinzufügen zu Sammlungen ohne Setter Nicht unterstützt, Problemumgehung
Eigenschaftsnamen mit Snake-Case-Schreibweise Nicht unterstützt, Problemumgehung
Unterstützung für System.Runtime.Serialization-Attribute Nicht unterstützt, Problemumgehung, Beispiel
Globale Einstellung MissingMemberHandling Nicht unterstützt, Problemumgehung, Beispiel
JsonObjectAttribute Nicht unterstützt, Problemumgehung
Eigenschaftsnamen ohne Anführungszeichen zulassen Vom Design nicht unterstützt
Einschließen von Zeichenfolgenwerten in einfache Anführungszeichen zulassen Vom Design nicht unterstützt
JSON-Werte, die keine Zeichenfolgen sind, für Zeichenfolgeneigenschaften zulassen Vom Design nicht unterstützt
Globale Einstellung TypeNameHandling.All Vom Design nicht unterstützt
Unterstützung für JsonPath-Abfragen Nicht unterstützt
Konfigurierbare Grenzwerte Nicht unterstützt
Feature in Newtonsoft.Json Äquivalent in System.Text.Json
Standardmäßige Deserialisierung ohne Berücksichtigung von Groß-/Kleinschreibung ✔️ Globale Einstellung „PropertyNameCaseInsensitive“
Eigenschaftsnamen mit Camel-Case-Schreibweise ✔️ Globale Einstellung „PropertyNamingPolicy“
Minimales Escapen von Zeichen ✔️ Strenges Escapen von Zeichen, konfigurierbar
Globale Einstellung NullValueHandling.Ignore ✔️ DefaultIgnoreCondition (globale Option)
Kommentare zulassen ✔️ Globale Einstellung „ReadCommentHandling“
Nachfolgende Kommas zulassen ✔️ Globale Einstellung „AllowTrailingCommas“
Registrierung benutzerdefinierter Konverter ✔️ Rangordnung weicht ab
Keine standardmäßige maximale Tiefe ✔️ Standardmäßige maximale Tiefe von  64, konfigurierbar
Globale Einstellung PreserveReferencesHandling ✔️ ReferenceHandling (globale Einstellung)
Serialisieren oder Deserialisieren von Zahlen in Anführungszeichen ✔️ NumberHandling (globale Einstellung), [JsonNumberHandling] (Attribut)
Deserialisieren in unveränderliche Klassen und Strukturen ✔️ JsonConstructor, C# 9-Datensätze
Unterstützung für Felder ✔️ IncludeFields (globale Einstellung), [JsonInclude] (Attribut)
Globale Einstellung DefaultValueHandling ✔️ DefaultIgnoreCondition (globale Einstellung)
Einstellung NullValueHandling für [JsonProperty] ✔️ JsonIgnore (Attribut)
Einstellung DefaultValueHandling für [JsonProperty] ✔️ JsonIgnore (Attribut)
Deserialisieren von Dictionary mit Nicht-Zeichenfolgen-Schlüssel ✔️ Unterstützt
Unterstützung für nicht öffentliche Setter und Getter von Eigenschaften ✔️ JsonInclude (Attribut)
[JsonConstructor]-Attribut ✔️ [JsonConstructor] (Attribut)
Globale Einstellung ReferenceLoopHandling ✔️ ReferenceHandling (globale Einstellung)
Rückrufe ✔️ Rückrufe
„NaN“, „Infinity“, „-Infinity“ ✔️ Unterstützt
Deserialisieren des Enumerationswerts für Zeichenfolgen ✔️ Deserialisieren von Enumerationswerten für Zeichenfolgen
Unterstützung für ein breites Spektrum von Typen Einige Typen benötigen benutzerdefinierte Konverter
Polymorphe Serialisierung Nicht unterstützt, Problemumgehung, Beispiel
Polymorphe Deserialisierung Nicht unterstützt, Problemumgehung, Beispiel
Deserialisieren eines abgeleiteten Typs in object-Eigenschaften Nicht unterstützt, Problemumgehung, Beispiel
Deserialisieren des JSON-Literals null in Non-Nullable-Werttypen Nicht unterstützt, Problemumgehung, Beispiel
Required-Einstellung für [JsonProperty]-Attribut Nicht unterstützt, Problemumgehung, Beispiel
DefaultContractResolver zum Ignorieren von Eigenschaften Nicht unterstützt, Problemumgehung, Beispiel
Einstellungen DateTimeZoneHandling und DateFormatString Nicht unterstützt, Problemumgehung, Beispiel
JsonConvert.PopulateObject-Methode Nicht unterstützt, Problemumgehung
Globale Einstellung ObjectCreationHandling Nicht unterstützt, Problemumgehung
Hinzufügen zu Sammlungen ohne Setter Nicht unterstützt, Problemumgehung
Eigenschaftsnamen mit Snake-Case-Schreibweise Nicht unterstützt, Problemumgehung
JsonObjectAttribute Nicht unterstützt, Problemumgehung
Unterstützung für System.Runtime.Serialization-Attribute Nicht unterstützt
Globale Einstellung MissingMemberHandling Nicht unterstützt
Eigenschaftsnamen ohne Anführungszeichen zulassen Vom Design nicht unterstützt
Einschließen von Zeichenfolgenwerten in einfache Anführungszeichen zulassen Vom Design nicht unterstützt
JSON-Werte, die keine Zeichenfolgen sind, für Zeichenfolgeneigenschaften zulassen Vom Design nicht unterstützt
Globale Einstellung TypeNameHandling.All Vom Design nicht unterstützt
Unterstützung für JsonPath-Abfragen Nicht unterstützt
Konfigurierbare Grenzwerte Nicht unterstützt

Dies ist keine vollständige Liste der Newtonsoft.Json-Funktionen. Die Liste enthält viele der Szenarien, die in GitHub-Issues oder StackOverflow-Beiträgen angefordert wurden. Wenn Sie eine Problemumgehung für eins der hier aufgelisteten Szenarien implementieren, für das derzeit kein Beispielcode vorhanden ist, und wenn Sie Ihre Lösung teilen möchten, wählen Sie Diese Seite im Abschnitt Feedback unten auf dieser Seite aus. Dadurch wird ein Issue im GitHub-Repository dieser Dokumentation erstellt und dieses ebenfalls im Abschnitt Feedback auf dieser Seite aufgeführt.

Unterschiede beim Standardverhalten

System.Text.Json ist standardmäßig strikt und vermeidet jegliche Vermutung oder Interpretation im Namen des Aufrufers, womit deterministisches Verhalten betont wird. Die Bibliothek ist absichtlich auf diese Weise für Leistung und Sicherheit konzipiert. Newtonsoft.Json ist standardmäßig flexibel. Dieser grundlegende Unterschied beim Design steht hinter vielen der folgenden spezifischen Unterschiede im Standardverhalten.

Deserialisierung ohne Berücksichtigung von Groß-/Kleinschreibung

Während der Deserialisierung führt Newtonsoft.Json standardmäßig einen Abgleich der Eigenschaftsnamen ohne Berücksichtigung von Groß-/Kleinschreibung durch. Das Standardverhalten von System.Text.Json berücksichtigt die Groß-/Kleinschreibung, was eine bessere Leistung liefert, weil ein exakter Abgleich erfolgt. Informationen, wie Sie einen Abgleich ohne Berücksichtigung von Groß-/Kleinschreibung durchführen, finden Sie unter Eigenschaftenabgleich ohne Berücksichtigung von Groß-/Kleinschreibung.

Wenn Sie System.Text.Json indirekt durch Verwendung von ASP.NET Core verwenden, müssen Sie nichts tun, um ein Verhalten wie Newtonsoft.Json zu erhalten. ASP.NET Core gibt die Einstellungen für die Verwendung von Eigenschaftsnamen mit Camel-Case-Schreibweise an sowie für den Abgleich ohne Berücksichtigung von Groß-/Kleinschreibung, wenn System.Text.Json verwendet wird.

ASP.NET Core ermöglicht auch standardmäßig das Deserialisieren von Zahlen in Anführungszeichen.

Minimales Escapen von Zeichen

Während der Serialisierung ist Newtonsoft.Json relativ nachsichtig, wenn es um das Durchlassen von Zeichen geht, ohne sie zu escapen. Dies bedeutet, dass sie nicht durch \uxxxx ersetzt werden, wobei xxxx der Codepunkt des Zeichens ist. Wenn sie escapet werden, geschieht dies durch Ausgabe eines umgekehrten Schrägstrichs (\) vor dem Zeichen (z. B. wird " zu \"). System.Text.Json escapet standardmäßig mehr Zeichen, um eine tief greifenden Abwehr als Schutz vor Cross-Site Scripting (XSS)- oder Information-Disclosure-Angriffen (Veröffentlichung von Informationen) zu bieten, wobei dies durch Verwendung der Abfolge von sechs Zeichen erzielt wird. System.Text.Json escapet standardmäßig alle Nicht-ASCII-Zeichen, sodass Sie nichts machen müssen, wenn Sie StringEscapeHandling.EscapeNonAscii in Newtonsoft.Json verwenden. System.Text.Json escapet außerdem standardmäßig HTML-abhängige Zeichen. Informationen, wie Sie das Standardverhalten von System.Text.Json außer Kraft setzen können, finden Sie unter Anpassen der Zeichencodierung.

Kommentare

Während der Deserialisierung ignoriert Newtonsoft.Json standardmäßig Kommentare in der JSON-Datei. Das Standardverhalten von System.Text.Json besteht im Auslösen von Ausnahmen für Kommentare, weil die Spezifikation RFC 8259 diese nicht umfasst. Informationen, wie Sie Kommentare zulassen können, finden Sie unter Zulassen von Kommentaren und nachfolgenden Kommas.

Nachfolgende Kommas

Während der Deserialisierung ignoriert Newtonsoft.Json standardmäßig nachfolgende Kommas. Es ignoriert ferner mehrere nachfolgende Kommas (z. B. [{"Color":"Red"},{"Color":"Green"},,]). Das Standardverhalten von System.Text.Json besteht im Auslösen von Ausnahmen für nachfolgende Kommas, weil die Spezifikation RFC 8259 diese nicht zulässt. Informationen, wie Sie System.Text.Json dazu bringen, diese zu akzeptieren, finden Sie unter Zulassen von Kommentaren und nachfolgenden Kommas. Es gibt keine Möglichkeit, mehrere nachfolgende Kommas zuzulassen.

Rangfolge für die Registrierung von Konvertern

Die Rangfolge der Registrierung von benutzerdefinierten Konvertern in Newtonsoft.Json ist wie folgt:

  • Attribut auf Eigenschaftsebene
  • Attribut auf Typebene
  • Converters-Sammlung

Diese Reihenfolge bedeutet, dass ein benutzerdefinierter Konverter in der Converters-Sammlung von einem Konverter außer Kraft gesetzt wird, der durch Anwenden eines Attributs auf Typebene registriert wird. Beide dieser Registrierungen werden von einem Attribut auf Eigenschaftsebene außer Kraft gesetzt.

Die Rangfolge der Registrierung von benutzerdefinierten Konvertern in System.Text.Json weicht hiervon ab:

  • Attribut auf Eigenschaftsebene
  • Converters-Sammlung
  • Attribut auf Typebene

Der Unterschied besteht hierbei darin, dass ein benutzerdefinierter Konverter in der Converters-Sammlung ein Attribut auf Typebene außer Kraft setzt. Die Absicht hinter dieser Rangfolge ist es, dass Änderungen zur Laufzeit eine zur Entwurfszeit getroffene Auswahl außer Kraft setzen sollen. Diese Rangfolge lässt sich nicht ändern.

Weitere Informationen zur Registrierung benutzerdefinierter Konverter finden Sie unter Registrieren eines benutzerdefinierten Konverters.

Maximale Tiefe

Die neueste Version von Newtonsoft.Json weist standardmäßig einen Grenzwert von 64 für die maximale Tiefe auf. System.Text.Json verfügt ebenfalls über einen Standardgrenzwert von 64, der sich durch Festlegen von JsonSerializerOptions.MaxDepth konfigurieren lässt.

Wenn Sie System.Text.Json indirekt durch ASP.NET Core verwenden, beträgt die Obergrenze für die maximale Tiefe standardmäßig 32. Der Standardwert ist der gleiche wie bei Modellbindung und wird in der JsonOptions-Klasse festgelegt.

JSON-Zeichenfolgen (Eigenschaftsnamen und Zeichenfolgenwerte)

Während der Deserialisierung akzeptiert Newtonsoft.Json Eigenschaftsnamen, die in doppelten oder einfachen Anführungszeichen stehen oder gar keine Anführungszeichen aufweisen. Es akzeptiert Zeichenfolgenwerte, die in doppelten oder einfachen Anführungszeichen stehen. Newtonsoft.Json akzeptiert beispielsweise folgendes JSON:

{
  "name1": "value",
  'name2': "value",
  name3: 'value'
}

System.Text.Json akzeptiert nur Eigenschaftsnamen und Zeichenfolgenwerte, die in doppelten Anführungszeichen stehen, weil dieses Format von der Spezifikation RFC 8259 gefordert wird und das einzige Format ist, das als gültiges JSON-Format gilt.

Ein in einfache Anführungszeichen eingeschlossener Wert führt zu einer JsonException mit folgender Meldung:

''' is an invalid start of a value.

Werte, die keine Zeichenfolgen sind, für Zeichenfolgeneigenschaften

Newtonsoft.Json akzeptiert Werte, die keine Zeichenfolgen sind, z. B. eine Zahl oder die Literale true und false, für die Deserialisierung von Eigenschaften vom Typ „String“ (Zeichenfolge). Im Folgenden finden Sie ein JSON-Beispiel, das Newtonsoft.Json erfolgreich in die folgende Klasse deserialisiert:

{
  "String1": 1,
  "String2": true,
  "String3": false
}
public class ExampleClass
{
    public string String1 { get; set; }
    public string String2 { get; set; }
    public string String3 { get; set; }
}

System.Text.Json deserialisiert Werte, die keine Zeichenfolgen sind, nicht in Zeichenfolgeneigenschaften. Ein für ein Zeichenfolgenfeld empfangener Wert, der keine Zeichenfolge ist, führt zu einer JsonException mit folgender Meldung:

The JSON value could not be converted to System.String.

Szenarien mit JsonSerializer

Einige der folgenden Szenarien werden von der integrierten Funktionalität nicht unterstützt, aber Problemumgehungen sind möglich. Die Problemumgehungen sind benutzerdefinierte Konverter, die möglicherweise keine vollständige Parität mit der Newtonsoft.Json-Funktionalität bereitstellen. Für einige davon wird Beispielcode als Beispiele bereitgestellt. Wenn Sie von diesen Newtonsoft.Json-Funktionen abhängig sind, erfordert die Migration Änderungen an Ihren .NET-Objektmodellen oder andere Codeänderungen.

Für einige der folgenden Szenarien sind Problemumgehungen nicht praktikabel oder möglich. Wenn Sie von diesen Newtonsoft.Json-Funktionen abhängig sind, ist die Migration nicht ohne wesentliche Änderungen möglich.

Zulassen oder Schreiben von Zahlen in Anführungszeichen

Newtonsoft.Json kann Zahlen serialisieren oder deserialisieren, die von JSON-Zeichenfolgen (in Anführungszeichen) dargestellt werden. Beispielsweise kann es {"DegreesCelsius":"23"} akzeptieren, anstelle von {"DegreesCelsius":23}. Um dieses Verhalten in System.Text.Json zu aktivieren, legen Sie JsonSerializerOptions.NumberHandling auf WriteAsString oder AllowReadingFromString fest, oder verwenden Sie das Attribut [JsonNumberHandling].

Wenn Sie System.Text.Json indirekt durch Verwendung von ASP.NET Core verwenden, müssen Sie nichts tun, um ein Verhalten wie Newtonsoft.Json zu erhalten. ASP.NET Core gibt Webstandardwerte an, wenn es System.Text.Json verwendet, und Webstandardwerte erlauben Zahlen in Anführungszeichen.

Weitere Informationen finden Sie unter Zulassen oder Schreiben von Zahlen in Anführungszeichen.

Angeben des bei der Deserialisierung zu verwendenden Konstruktors

Mit dem Newtonsoft.Json[JsonConstructor]-Attribut von können Sie angeben, welcher Konstruktor beim Deserialisieren in ein POCO aufgerufen werden soll.

System.Text.Json verfügt auch über das Attribut [JsonConstructor]. Weitere Informationen finden Sie unter Unveränderliche Typen und Datensätze.

Bedingtes Ignorieren einer Eigenschaft

Newtonsoft.Json bietet mehrere Möglichkeiten, um eine Eigenschaft bei der Serialisierung oder Deserialisierung bedingt zu ignorieren:

  • DefaultContractResolver ermöglicht das Auswählen von Eigenschaften, die basierend auf beliebigen Kriterien eingeschlossen oder ignoriert werden sollen.
  • Mit den Einstellungen NullValueHandling und DefaultValueHandling von JsonSerializerSettings können Sie angeben, dass alle Eigenschaften mit Nullwert oder Standardwert ignoriert werden sollen.
  • Mit den Einstellungen NullValueHandling und DefaultValueHandling des [JsonProperty]-Attributs können Sie einzelne Eigenschaften angeben, die ignoriert werden sollen, wenn Sie auf Null oder den Standardwert festgelegt sind.

System.Text.Json bietet die folgenden Möglichkeiten, Eigenschaften oder Felder bei der Serialisierung zu ignorieren:

Darüber hinaus können Sie in .NET 7 und höheren Versionen den JSON-Vertrag anpassen, um Eigenschaften basierend auf beliebigen Kriterien zu ignorieren. Weitere Informationen finden Sie unter Benutzerdefinierte Verträge.

Mit dieser Option können Sie ausgewählte Eigenschaften auf Grundlage beliebiger Kriterien, die zur Laufzeit ausgewertet wurden, nicht ignorieren.

Öffentliche und nicht öffentliche Felder

Newtonsoft.Json kann Felder ebenso wie Eigenschaften serialisieren und deserialisieren.

Verwenden Sie in System.Text.Json die globale Einstellung JsonSerializerOptions.IncludeFields oder das Attribut [JsonInclude], um öffentliche Felder bei der Serialisierung oder Deserialisierung einzuschließen. Ein Beispiel finden Sie unter Einschließen von Feldern.

Erhalten von Objektverweise und Behandeln von Schleifen

Standardmäßig serialisiert Newtonsoft.Json als Wert. Wenn ein Objekt beispielsweise zwei Eigenschaften enthält, die einen Verweis auf dasselbe Person-Objekt enthalten, werden die Werte der Eigenschaften dieses Person-Objekts in JSON dupliziert.

Newtonsoft.Json verfügt über eine PreserveReferencesHandling-Einstellung für JsonSerializerSettings, mit der Sie als Verweis serialisieren können:

  • Dem für das erste Person-Objekt erstellten JSON-Code werden Bezeichnermetadaten hinzugefügt.
  • Der für das zweite Person-Objekt erstellte JSON-Code enthält einen Verweis auf diesen Bezeichner anstelle der Eigenschaftswerte.

Newtonsoft.Json verfügt außerdem über eine ReferenceLoopHandling-Einstellung, mit der Sie Zirkelbezüge ignorieren können, anstatt eine Ausnahme auszulösen.

Um Verweise beizubehalten und Zirkelbezüge zu behandeln, legen Sie JsonSerializerOptions.ReferenceHandler in System.Text.Json auf Preserve fest. Die Einstellung ReferenceHandler.Preserve entspricht PreserveReferencesHandling = PreserveReferencesHandling.All in Newtonsoft.Json.

Die Option ReferenceHandler.IgnoreCycles weist ein Verhalten ähnlich Newtonsoft.JsonReferenceLoopHandling.Ignore auf. Ein Unterschied besteht darin, dass die System.Text.Json-Implementierung Verweisschleifen durch das JSON-Token null ersetzt, anstatt den Objektverweis zu ignorieren. Weitere Informationen finden Sie unter Ignorieren von Zirkelbezügen.

Wie bei Newtonsoft.JsonReferenceResolver definiert die Klasse System.Text.Json.Serialization.ReferenceResolver das Verhalten bei Beibehaltung von Verweisen für Serialisierung und Deserialisierung. Erstellen Sie eine abgeleitete Klasse, um benutzerdefiniertes Verhalten anzugeben. Ein Beispiel finden Sie unter GuidReferenceResolver.

Einige verwandte Newtonsoft.Json-Features werden nicht unterstützt:

Weitere Informationen finden Sie unter Beibehalten von Verweisen und Behandeln von Zirkelbezügen.

Wörterbuch mit Schlüssel, der keine Zeichenfolgen ist

Newtonsoft.Json und System.Text.Json unterstützen Sammlungen des Typs Dictionary<TKey, TValue>. In System.Text.Json muss TKey jedoch ein primitiver Typ und kein benutzerdefinierter Typ sein. Weitere Informationen finden Sie unter Unterstützte Schlüsseltypen.

Achtung

Das Deserialisieren zu Dictionary<TKey, TValue>, wo TKey als etwas Anderes als string beschrieben wird, könnte zu einem Sicherheitsrisiko in der nutzenden Anwendung führen. Weitere Informationen finden Sie unter dotnet/runtime#4761.

Typen ohne integrierte Unterstützung

System.Text.Json bietet für die folgenden Typen keine integrierte Unterstützung:

Benutzerdefinierte Konverter können für Typen implementiert werden, für die keine integrierte Unterstützung vorhanden ist.

Polymorphe Serialisierung

Newtonsoft.Json führt automatisch eine polymorphe Serialisierung durch. Ab .NET 7 unterstützt System.Text.Json die polymorphe Serialisierung mit dem JsonDerivedTypeAttribute-Attribut. Weitere Informationen finden Sie unter Serialisieren von Eigenschaften abgeleiteter Klassen.

Polymorphe Deserialisierung

Newtonsoft.Json verfügt über eine TypeNameHandling-Einstellung, mit der dem JSON-Code beim Serialisieren Typnamenmetadaten hinzugefügt werden. Sie verwendet die Metadaten während der Deserialisierung, um die polymorphe Deserialisierung durchzuführen. Ab .NET 7 basiert System.Text.Json auf Typdiskriminatorinformationen, um eine polymorphe Deserialisierung durchzuführen. Diese Metadaten werden im JSON-Code ausgegeben und dann während dem Deserialisieren verwendet, um zu bestimmen, ob als Basistyp oder als abgeleiteter Typ deserialisiert werden soll. Weitere Informationen finden Sie unter Serialisieren von Eigenschaften abgeleiteter Klassen.

Um die polymorphe Deserialisierung mit älteren .NET-Versionen zu unterstützen, erstellen Sie einen Konverter wie im Beispiel Schreiben von benutzerdefinierten Konvertern.

Deserialisieren von Enumerationswerten für Zeichenfolgen

Standardmäßig unterstützt System.Text.Json das Deserialisieren von Zeichenfolgenumwerten nicht, während dies für Newtonsoft.Json der Fall ist. Der folgende Code löst z. B. eine JsonException aus:

string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";
var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.

class MyObj
{
    public string Text { get; set; } = "";
    public MyEnum Enum { get; set; }
}

enum MyEnum
{
    One,
    Two,
    Three
}

Sie können jedoch die Deserialisierung von Zeichenfolgenumwerten mithilfe des JsonStringEnumConverter-Konverters aktivieren. Weitere Informationen finden Sie unter Enumerationen als Zeichenfolgen.

Deserialisierung von Objekteigenschaften

Wenn Newtonsoft.Json in Object deserialisieren, wird Folgendes ausgeführt:

  • Es leitet den Typ primitiver Werte in der JSON-Nutzlast (außer null) ab und gibt die gespeicherten Typen string, long, double, boolean oder DateTime als geschachteltes Objekt zurück. Primitive Werte sind einzelne JSON-Werte, wie eine JSON-Zahl, -Zeichenfolge, true, false oder null.
  • Gibt ein JObject oder JArray für komplexe Werte in der JSON-Nutzlast zurück. Komplexe Werte sind Sammlungen von JSON-Schlüssel-Wert-Paaren in geschweiften Klammern ({}) oder Listen mit Werten in eckigen Klammern ([]). Die Eigenschaften und Werte in geschweiften oder eckigen Klammern können zusätzliche Eigenschaften oder Werte besitzen.
  • Gibt einen Nullverweis zurück, wenn die Nutzlast das JSON-Literal null enthält.

System.Text.Json speichert bei der Deserialisierung in Object ein geschachteltes JsonElement sowohl für primitive als auch für komplexe Werte, z. B.:

  • Eine object-Eigenschaft.
  • Ein object-Wörterbuchwert.
  • Ein object-Arraywert.
  • Ein Stamm-object.

System.Text.Json behandelt null jedoch genau wie Newtonsoft.Json und gibt einen Nullverweis zurück, wenn die Nutzlast das JSON-Literal null enthält.

Um Typrückschlüsse für object-Eigenschaften zu implementieren, erstellen Sie einen Konverter wie in Schreiben von benutzerdefinierten Konvertern.

Deserialisieren eines Null-Typs in einen Non-Nullable-Typ

Newtonsoft.Json löst im folgenden Szenario keine Ausnahme aus:

  • NullValueHandling ist auf Ignore festgelegt, und
  • Während der Deserialisierung enthält JSON einen Nullwert für einen Non-Nullable-Werttyp.

Im selben Szenario löst System.Text.Json eine Ausnahme aus. (Die entsprechende Einstellung in System.Text.Json für die Behandlung von NULL ist JsonSerializerOptions.IgnoreNullValues = true.)

Wenn Sie den Zieltyp besitzen, besteht die beste Problemumgehung darin, die betreffende Eigenschaft „nullable“ zu machen (z. B. int in int? ändern).

Eine weitere Problemumgehung besteht darin, einen Konverter für den Typ zu erstellen, wie im folgenden Beispiel, das Nullwerte für DateTimeOffset-Typen behandelt:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
    {
        public override DateTimeOffset Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options) =>
            reader.TokenType == JsonTokenType.Null
                ? default
                : reader.GetDateTimeOffset();

        public override void Write(
            Utf8JsonWriter writer,
            DateTimeOffset dateTimeValue,
            JsonSerializerOptions options) =>
            writer.WriteStringValue(dateTimeValue);
    }
}

Registrieren Sie diesen benutzerdefinierten Konverter mithilfe eines Attributs für die Eigenschaft oder durch Hinzufügen des Konverters zur Converters-Sammlung.

Hinweis: Der vorherige Konverter behandelt Nullwerte anders, als dies Newtonsoft.Json für POCOS tut, die Standardwerte angeben. Angenommen, der folgende Code stellt Ihr Zielobjekt dar:

public class WeatherForecastWithDefault
{
    public WeatherForecastWithDefault()
    {
        Date = DateTimeOffset.Parse("2001-01-01");
        Summary = "No summary";
    }
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}

Und nehmen wir weiterhin an, der folgende JSON-Code wird mithilfe des vorherigen Konverters deserialisiert:

{
  "Date": null,
  "TemperatureCelsius": 25,
  "Summary": null
}

Nach der Deserialisierung hat die Date-Eigenschaft den Wert „1/1/0001“ (default(DateTimeOffset)), d. h., der im Konstruktor festgelegte Wert wird überschrieben. Beim selben POCO und JSON-Code würde die Deserialisierung mit Newtonsoft.Json den Wert „1/1/2001“ in der Date-Eigenschaft belassen.

Deserialisieren in unveränderliche Klassen und Strukturen

Newtonsoft.Json kann in unveränderliche Klassen und Strukturen deserialisieren, weil es Konstruktoren verwenden kann, die Parameter besitzen.

Geben Sie in System.Text.Json das Attribut [JsonConstructor] an, um die Verwendung eines parametrisierten Konstruktors festzulegen. Datensätze in C# 9 sind ebenfalls unveränderlich und werden als Deserialisierungsziele unterstützt. Weitere Informationen finden Sie unter Unveränderliche Typen und Datensätze.

Erforderliche Eigenschaften

In Newtonsoft.Json geben Sie an, dass eine Eigenschaft erforderlich ist, indem Sie Required für das [JsonProperty]-Attribut festlegen. Newtonsoft.Json löst eine Ausnahme aus, wenn im JSON für eine als erforderlich markierte Eigenschaft kein Wert empfangen wird.

Ab .NET 7 können Sie den C#-required-Modifizierer oder das JsonRequiredAttribute-Attribut für eine erforderliche Eigenschaft verwenden. System.Text.Json löst eine Ausnahme aus, wenn die JSON-Nutzdaten keinen Wert für die markierte Eigenschaft enthalten. Weitere Informationen finden Sie unter Erforderliche Eigenschaften.

System.Text.Json löst keine Ausnahme aus, wenn kein Wert für eine der Eigenschaften des Zieltyps empfangen wird. Wenn Sie z. B. eine WeatherForecast-Klasse haben:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}

Der folgende JSON-Code wird ohne Fehler deserialisiert:

{
    "TemperatureCelsius": 25,
    "Summary": "Hot"
}

Wählen Sie eine der folgenden Optionen aus, damit die Deserialisierung fehlschlägt, wenn keine Date-Eigenschaft im JSON-Code enthalten ist:

Der folgende Beispielcode für einen Konverter löst nach Abschluss der Deserialisierung eine Ausnahme aus, wenn die Date-Eigenschaft nicht festgelegt ist:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class WeatherForecastRequiredPropertyConverter : JsonConverter<WeatherForecast>
    {
        public override WeatherForecast Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // Don't pass in options when recursively calling Deserialize.
            WeatherForecast forecast = JsonSerializer.Deserialize<WeatherForecast>(ref reader)!;

            // Check for required fields set by values in JSON
            return forecast!.Date == default
                ? throw new JsonException("Required property not received in the JSON")
                : forecast;
        }

        public override void Write(
            Utf8JsonWriter writer,
            WeatherForecast forecast, JsonSerializerOptions options)
        {
            // Don't pass in options when recursively calling Serialize.
            JsonSerializer.Serialize(writer, forecast);
        }
    }
}

Registrieren Sie diesen benutzerdefinierten Konverter durch Hinzufügen des Konverters zur JsonSerializerOptions.Converters-Sammlung.

Für dieses Muster rekursiver Aufrufe des Konverters ist erforderlich, dass Sie den Konverter mit JsonSerializerOptions registrieren, anstatt mithilfe eines Attributs. Wenn Sie den Konverter mithilfe eines Attributs registrieren, ruft der benutzerdefinierte Konverter sich rekursiv selbst auf. Dies resultiert in einer Endlosschleife, die mit einer Stapelüberlaufausnahme endet.

Wenn Sie den Konverter mithilfe des Optionsobjekts registrieren, vermeiden Sie eine Endlosschleife, indem Sie nicht das Options-Objekt übergeben, wenn Serialize oder Deserialize rekursiv aufgerufen wird. Das options-Objekt enthält die Converters-Sammlung. Wenn Sie es an Serialize oder Deserialize übergeben, ruft sich der benutzerdefinierte Konverter selbst auf, wodurch eine Endlosschleife entsteht, die zu einer Stapelüberlaufausnahme führt. Wenn die Standardoptionen nicht praktikabel sind, erstellen Sie eine neue Instanz der Optionen mit den Einstellungen, die Sie benötigen. Diese Vorgehensweise ist langsam, da jede neue Instanz unabhängig zwischengespeichert wird.

Es gibt ein alternatives Muster, dass die JsonConverterAttribute-Registrierung für die zu konvertierende Klasse verwenden kann. Bei diesem Ansatz ruft der Code des Konverters Serialize oder Deserialize für eine Klasse auf, die von der zu konvertierenden Klasse abgeleitet wird. JsonConverterAttribute wird nicht auf die abgeleitete Klasse angewendet. Im folgenden Beispiel dieser Alternative:

  • ist WeatherForecastWithRequiredPropertyConverterAttribute die zu deserialisierende Klasse, und JsonConverterAttribute wurde auf diese angewendet.
  • ist WeatherForecastWithoutRequiredPropertyConverterAttribute die abgeleitete Klasse, die das Konverterattribut nicht aufweist.
  • Der Code im Konverter ruft Serialize und Deserialize auf WeatherForecastWithoutRequiredPropertyConverterAttribute auf, um eine Endlosschleife zu vermeiden. Bei diesem Serialisierungsansatz kommt es zu Leistungseinbußen, da eine zusätzliche Objektinstanziierung und Kopien von Eigenschaftswerten vorgenommen werden.

Im Folgenden werden die WeatherForecast*-Typen veranschaulicht:

[JsonConverter(typeof(WeatherForecastRequiredPropertyConverterForAttributeRegistration))]
public class WeatherForecastWithRequiredPropertyConverterAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}

public class WeatherForecastWithoutRequiredPropertyConverterAttribute :
    WeatherForecastWithRequiredPropertyConverterAttribute
{
}

So sieht der Konverter aus:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class WeatherForecastRequiredPropertyConverterForAttributeRegistration :
        JsonConverter<WeatherForecastWithRequiredPropertyConverterAttribute>
    {
        public override WeatherForecastWithRequiredPropertyConverterAttribute Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // OK to pass in options when recursively calling Deserialize.
            WeatherForecastWithRequiredPropertyConverterAttribute forecast =
                JsonSerializer.Deserialize<WeatherForecastWithoutRequiredPropertyConverterAttribute>(
                    ref reader,
                    options)!;

            // Check for required fields set by values in JSON.
            return forecast!.Date == default
                ? throw new JsonException("Required property not received in the JSON")
                : forecast;
        }

        public override void Write(
            Utf8JsonWriter writer,
            WeatherForecastWithRequiredPropertyConverterAttribute forecast,
            JsonSerializerOptions options)
        {
            var weatherForecastWithoutConverterAttributeOnClass =
                new WeatherForecastWithoutRequiredPropertyConverterAttribute
                {
                    Date = forecast.Date,
                    TemperatureCelsius = forecast.TemperatureCelsius,
                    Summary = forecast.Summary
                };

            // OK to pass in options when recursively calling Serialize.
            JsonSerializer.Serialize(
                writer,
                weatherForecastWithoutConverterAttributeOnClass,
                options);
        }
    }
}

Der erforderliche Eigenschaftenkonverter würde zusätzliche Logik erfordern, wenn Sie Attribute wie [JsonIgnore] oder verschiedene Optionen wie benutzerdefinierte Encoder verarbeiten müssten. Der Beispielcode verarbeitet außerdem keine Eigenschaften, für die im Konstruktor ein Standardwert festgelegt ist. Und dieser Ansatz unterscheidet nicht zwischen den folgenden Szenarien:

  • Im JSON fehlt eine Eigenschaft.
  • Im JSON ist eine Eigenschaft für einen Non-Nullable-Typ vorhanden, aber der Wert ist der Standardwert für den Typ, z. B. Null für einen int.
  • Im JSON ist eine Eigenschaft für einen Nullable-Werttyp vorhanden, aber der Wert ist Null.

Hinweis

Wenn Sie System.Text.Json von einem ASP.NET Core-Controller aus verwenden, können Sie eventuell ein [Required]-Attribut für Eigenschaften der Modellklasse verwenden, anstatt einen System.Text.Json-Konverter zu implementieren.

Angeben des Datumsformats

Newtonsoft.Json bietet verschiedene Möglichkeiten, um zu kontrollieren, wie Eigenschaften des Typs DateTime und DateTimeOffset serialisiert und deserialisiert werden:

  • Die DateTimeZoneHandling-Einstellung kann verwendet werden, um alle DateTime-Werte als UTC-Datumsangaben zu serialisieren.
  • Die DateFormatString-Einstellung und DateTime-Konverter können verwendet werden, um das Format von Datumszeichenfolgen anzupassen.

System.Text.Json unterstützt ISO 8601-1:2019 einschließlich des Profils RFC 3339. Dieses Format ist gängig, eindeutig und ermöglicht präzise Roundtrips. Um ein beliebiges anderes Format zu verwenden, erstellen Sie einen benutzerdefinierten Konverter. Die folgenden Konverter serialisieren und deserialisieren beispielsweise JSON-Code, der das Unix-Epochenformat mit oder ohne Zeitzonenversatz verwendet (Werte wie /Date(1590863400000-0700)/ oder /Date(1590863400000)/):

sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
    static readonly DateTimeOffset s_epoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);

    public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
                || !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
                || !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
        {
            throw new JsonException();
        }

        int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
        TimeSpan utcOffset = new(hours * sign, minutes * sign, 0);

        return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
    }

    public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
        TimeSpan utcOffset = value.Offset;

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");

        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
    static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
        {
            throw new JsonException();
        }

        return s_epoch.AddMilliseconds(unixTime);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime})/");
        writer.WriteStringValue(formatted);
    }
}

Weitere Informationen finden Sie unter Unterstützung von „DateTime“ und „DateTimeOffset“ in System.Text.Json.

Rückrufe

Newtonsoft.Json ermöglicht das Ausführen von benutzerdefiniertem Code an mehreren Stellen im Serialisierungs- oder Deserialisierungsprozess:

  • OnDeserializing (bei Beginn der Deserialisierung eines Objekts)
  • OnDeserialized (nach Abschluss der Deserialisierung eines Objekts)
  • OnSerializing (bei Beginn der Serialisierung eines Objekts)
  • OnSerialized (nach Abschluss der Serialisierung eines Objekts)

System.Text.Json macht dieselben Benachrichtigungen während der Serialisierung und Deserialisierung verfügbar. Um sie zu verwenden, implementieren Sie eine oder mehrere der folgenden Schnittstellen aus dem Namespace System.Text.Json.Serialization:

Hier sehen Sie ein Beispiel, das nach einer NULL-Eigenschaft sucht und Meldungen zu Beginn und Ende der Serialisierung und Deserialisierung schreibt:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Callbacks
{
    public class WeatherForecast : 
        IJsonOnDeserializing, IJsonOnDeserialized, 
        IJsonOnSerializing, IJsonOnSerialized
    {
        public DateTime Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }

        void IJsonOnDeserializing.OnDeserializing() => Console.WriteLine("\nBegin deserializing");
        void IJsonOnDeserialized.OnDeserialized()
        {
            Validate();
            Console.WriteLine("Finished deserializing");
        }
        void IJsonOnSerializing.OnSerializing()
        {
            Console.WriteLine("Begin serializing");
            Validate();
        }
        void IJsonOnSerialized.OnSerialized() => Console.WriteLine("Finished serializing");

        private void Validate()
        {
            if (Summary is null)
            {
                Console.WriteLine("The 'Summary' property is 'null'.");
            }
        }
    }

    public class Program
    {
        public static void Main()
        {
            var weatherForecast = new WeatherForecast
            {
                Date = DateTime.Parse("2019-08-01"),
                TemperatureCelsius = 25,
            };

            string jsonString = JsonSerializer.Serialize(weatherForecast);
            Console.WriteLine(jsonString);

            weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
            Console.WriteLine($"Date={weatherForecast?.Date}");
            Console.WriteLine($"TemperatureCelsius={weatherForecast?.TemperatureCelsius}");
            Console.WriteLine($"Summary={weatherForecast?.Summary}");
        }
    }
}
// output:
//Begin serializing
//The 'Summary' property is 'null'.
//Finished serializing
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":null}

//Begin deserializing
//The 'Summary' property is 'null'.
//Finished deserializing
//Date=8/1/2019 12:00:00 AM
//TemperatureCelsius = 25
//Summary=

Der OnDeserializing-Code hat keinen Zugriff auf die neue POCO-Instanz. Um die neue POCO-Instanz zu Beginn der Deserialisierung zu bearbeiten, fügen Sie diesen Code in den POCO-Konstruktor ein.

Nicht öffentliche Setter und Getter von Eigenschaften

Newtonsoft.Json kann private und interne Eigenschaften-Setter und -Getter über das JsonProperty-Attribut verwenden.

System.Text.Json unterstützt private und interne Setter und Getter von Eigenschaften über das Attribut [JsonInclude]. Beispielcode finden Sie unter Nicht öffentliche Eigenschaftenaccessoren.

Auffüllen vorhandener Objekte

Die JsonConvert.PopulateObject-Methode in Newtonsoft.Json deserialisiert ein JSON-Dokument in eine vorhandene Instanz einer Klasse, anstatt eine neue Instanz zu erstellen. System.Text.Json erstellt immer eine neue Instanz des Zieltyps unter Verwendung des standardmäßigen öffentlichen parameterlosen Konstruktors. Benutzerdefinierte Konverter können in eine vorhandene Instanz deserialisieren.

Wiederverwenden statt Ersetzen von Eigenschaften

Ab .NET 8 System.Text.Json unterstützt die erneute Wiederverwendung initialisierter Eigenschaften, anstatt sie zu ersetzen. Es gibt einige Unterschiede im Verhalten, die Sie im API-Vorschlag nachlesen können.

Weitere Informationen finden Sie unter Auffüllen initialisierter Eigenschaften.

Mit der ObjectCreationHandling Einstellung Newtonsoft.Json können Sie angeben, dass Objekte in Eigenschaften bei der Deserialisierung nicht ersetzt, sondern wiederverwendet werden sollen. System.Text.Json ersetzt Objekte in Eigenschaften immer. Benutzerdefinierte Konverter können diese Funktionalität bereitstellen, oder Sie können ein Upgrade auf .NET 8 durchführen, das die Auffüllfunktionalität bereitstellt.

Auffüllen von Eigenschaften ohne Setter

Ab .NET 8 System.Text.Json wird das Auffüllen von Eigenschaften unterstützt, auch von solchen, die keinen Setter haben. Weitere Informationen finden Sie unter Auffüllen initialisierter Eigenschaften.

Während der Deserialisierung fügt Newtonsoft.Json einer Sammlung Objekte hinzu, auch wenn die Eigenschaft über keinen Setter verfügt. System.Text.Json ignoriert Eigenschaften, die keinen Setter besitzen. Benutzerdefinierte Konverter können diese Funktionalität bereitstellen, oder Sie können auf .NET 8 aktualisieren, wodurch schreibgeschützte Eigenschaften aufgefüllt werden können.

Snake-Case-Benennungsrichtlinie

System.Text.Json enthält eine integrierte Benennungsrichtlinie für Snake Case. Bei einigen Eingaben gibt es jedoch einige Verhaltensunterschiede Newtonsoft.Json . Die folgende Tabelle zeigt einige dieser Unterschiede beim Konvertieren von Eingaben mithilfe der JsonNamingPolicy.SnakeCaseLower-Richtlinie.

Eingabe Newtonsoft.Json-Ergebnis System.Text.Json-Ergebnis
„AB1“ „a_b1“ „ab1“
„SHA512Managed“ „sh_a512_managed“ „sha512_managed“
„abc123DEF456“ „abc123_de_f456“ „abc123_def456“
„KEBAB-CASE“ „keba_b-_case“ „kebab-case“

Die einzige integrierte Benennungsrichtlinie für Eigenschaften in System.Text.Json gilt für Camel Cases. Newtonsoft.Json kann Eigenschaftennamen in die Snake-Case-Schreibweise konvertieren. Eine benutzerdefinierte Benennungsrichtlinie kann diese Funktionalität bereitstellen oder auf .NET 8 oder höher aktualisieren, die integrierte Benennungsrichtlinien für Schlangenfälle enthält.

System.Runtime.Serialization-Attribute

Mit System.Runtime.Serialization- Attributen wie DataContractAttribute, DataMemberAttributeund IgnoreDataMemberAttribute können Sie einen Datenvertrag definieren. Ein neuer Datenvertrag ist eine formale Vereinbarung zwischen einem Dienst und einem Client, in dem die auszutauschenden Daten abstrakt beschrieben werden. Der Datenvertrag definiert genau, welche Eigenschaften für den Austausch serialisiert werden.

System.Text.Json verfügt über keine integrierte Unterstützung für diese Attribute. Ab .NET 7 können Sie jedoch einen benutzerdefinierten Typlöser verwenden, um Unterstützung hinzuzufügen. Ein Beispiel finden Sie unter ZCS. DataContractResolver.

Oktalzahlen

Newtonsoft.Json behandelt Zahlen mit einer führenden Null als Oktalzahlen. System.Text.Json lässt keine führenden Nullen zu, da sie von der Spezifikation RFC 8259 nicht zugelassen werden.

Behandeln fehlender Member

Newtonsoft.Json kann so konfiguriert werden, dass Ausnahmen ausgelöst werden, wenn der deserialisierte JSON-Code Eigenschaften enthält, die im Zieltyp fehlen. Standardmäßig ignoriert System.Text.Json zusätzliche Eigenschaften im JSON-Code, außer wenn Sie das Attribut [JsonExtensionData] verwenden.

In .NET 8 und späteren Versionen können Sie festlegen, ob nicht zugeordnete JSON-Eigenschaften übersprungen oder nicht zugelassen werden sollen, indem Sie eine der folgenden Möglichkeiten nutzen:

JsonObjectAttribute

Newtonsoft.Jsonverfügt über ein Attribut namens JsonObjectAttribute, das auf Typebene angewendet werden kann, um zu steuern, welche Member serialisiert werden, wie null Werte behandelt werden und ob alle Member erforderlich sind. System.Text.Json verfügt über kein entsprechendes Attribut, das auf einen Typ angewendet werden kann. Für einige Verhaltensweisen, z. B. die Behandlung von null-Werten, können Sie entweder dasselbe Verhalten für die globale Ebene JsonSerializerOptions oder einzeln für jede Eigenschaft konfigurieren.

Betrachten Sie das folgende Beispiel, in dem Newtonsoft.Json.JsonObjectAttribute verwendet wird, um anzugeben, dass alle null-Eigenschaften ignoriert werden sollen:

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Person { ... }

In System.Text.Json können Sie das Verhalten für alle Typen und Eigenschaften festlegen:

JsonSerializerOptions options = new()
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

string json = JsonSerializer.Serialize<Person>(person, options);

Alternativ können Sie das Verhalten für jede Eigenschaft separat festlegen:

public class Person
{
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Name { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public int? Age { get; set; }
}

Betrachten Sie als Nächstes das folgende Beispiel, in dem Newtonsoft.Json.JsonObjectAttribute verwendet wird, um anzugeben, dass alle Membereigenschaften im JSON-Code vorhanden sein müssen:

[JsonObject(ItemRequired = Required.Always)]
public class Person { ... }

Sie können das gleiche Verhalten in System.Text.Json erzielen, indem Sie den C#-Modifizierer required oder JsonRequiredAttributezu jeder Eigenschaft hinzufügen. Weitere Informationen finden Sie unter Erforderliche Eigenschaften.

public class Person
{
    [JsonRequired]
    public string? Name { get; set; }

    public required int? Age { get; set; }
}

TraceWriter

Mit Newtonsoft.Json können Sie Debuggen, indem Sie einen TraceWriter verwenden, um Protokolle anzuzeigen, die von der Serialisierung oder Deserialisierung generiert werden. System.Text.Json führt keine Protokollierung aus.

„JsonDocument“ und „JsonElement“ im Vergleich zu „JToken“ (wie „JObject“, „JArray“)

System.Text.Json.JsonDocument bietet die Möglichkeit, ein schreibgeschütztes Dokumentobjektmodell (DOM) aus vorhandenen JSON-Nutzlasten zu analysieren und zu erstellen. Das DOM bietet zufälligen Zugriff auf Daten in einer JSON-Nutzlast. Auf die JSON-Elemente, aus denen sich die Nutzlast zusammensetzt, kann über den Typ JsonElement zugegriffen werden. Der JsonElement-Typ stellt APIs zum Konvertieren von JSON-Text in allgemeine .NET-Typen bereit. JsonDocument macht eine RootElement-Eigenschaft verfügbar.

Ab .NET 6 können Sie mithilfe des JsonNode-Typs und anderer Typen im System.Text.Json.Nodes-Namespace ein veränderliches DOM aus vorhandenen JSON-Nutzdaten parsen und erstellen. Weitere Informationen finden Sie unter Verwenden von JsonNode.

„JsonDocument“ ist „IDisposable“

JsonDocument erstellt eine In-Memory-Ansicht der Daten in einem gepoolten Puffer. Daher implementiert der JsonDocument-Typ IDisposable, im Gegensatz zu JObject oder JArray von Newtonsoft.Json, und muss innerhalb eines using-Blocks verwendet werden. Weitere Informationen finden Sie unter „JsonDocument“ ist „IDisposable“.

Das „JsonDocument“ ist schreibgeschützt.

Das System.Text.Json-DOM kann keine JSON-Elemente hinzufügen, entfernen oder ändern. Es ist auf diese Weise konzipiert, um Leistung zu bieten und Zuordnungen für das Parsen gängiger JSON-Nutzdatengrößen (d. h. < 1 MB) zu verringern.

„JsonElement“ ist eine union-Struktur

JsonDocument macht das RootElement als Eigenschaft des Typs JsonElement verfügbar, wobei es sich um einen Union-Strukturtyp handelt, der jedes JSON-Element umfasst. Newtonsoft.Json verwendet dedizierte hierarchische Typen wie JObject, JArray, JToken usw. JsonElement ist das, was Sie durchsuchen und worüber Sie aufzählen können, und Sie können JsonElement verwenden, um JSON-Elemente in .NET-Typen zu materialisieren.

Ab .NET 6 können Sie Typ JsonNode und Typen im System.Text.Json.Nodes-Namespace verwenden, die JObject, JArray und JToken entsprechen. Weitere Informationen finden Sie unter Verwenden von JsonNode.

Durchsuchen eines „JSonDocument“ und „JsonElement“ nach Unterelementen

Suchen nach JSON-Token mithilfe von JObject oder JArray von Newtonsoft.Json sind in der Regel relativ schnell, da es sich dabei um Nachschlagevorgänge in einem Wörterbuch handelt. Im Vergleich dazu erfordern Suchen im JsonElement eine sequenzielle Suche der Eigenschaften, sodass sie relativ langsam sind (z. B. beim Verwenden von TryGetProperty). System.Text.Json ist darauf ausgelegt, die anfängliche Analysezeit zu minimieren anstatt die Nachschlagezeit. Weitere Informationen finden Sie unter Durchsuchen von „JSonDocument“ und „JsonElement“ nach Unterelementen.

Utf8JsonReader und JsonTextReader

Bei System.Text.Json.Utf8JsonReader handelt es sich um einen leistungsstarken Vorwärtsreader mit geringer Zuordnung für UTF-8-kodierten JSON-Text, der aus einer ReadOnlySpan<byte> oder ReadOnlySequence<byte> gelesen wird. Der Utf8JsonReader ist ein Low-Level-Typ, der zum Erstellen von benutzerdefinierten Parsern und Deserialisierungsprogrammen genutzt werden kann.

„Utf8JsonReader“ ist eine ref-Struktur.

Beim JsonTextReader in Newtonsoft.Json handelt es sich um eine Klasse. Der Utf8JsonReader-Typ unterscheidet sich darin, dass es sich um eine Referenzstruktur handelt. Weitere Informationen finden Sie unter Einschränkungen bei „ref struct“ für Utf8JsonReader.

Lesen von Nullwerten in Nullable-Werttypen

Newtonsoft.Json bietet APIs, die Nullable<T> zurückgeben, z. B. ReadAsBoolean, das einen Null-TokenType für Sie verarbeitet, indem es einen bool? zurückgibt. Die integrierten System.Text.Json-APIs geben nur Non-Nullable-Werttypen zurück. Weitere Informationen finden Sie unter Lesen von NULL-Werten in Nullwerte zulassende Werttypen.

Mehrere Ziele zum Lesen von JSON-Code

Wenn Sie weiterhin Newtonsoft.Json für bestimmte Zielframeworks verwenden müssen, können Sie mehrere Zielversionen verwenden und zwei Implementierungen verwenden. Dies ist jedoch nicht trivial und erfordert einige #ifdefs sowie Quellduplizierung. Eine Möglichkeit, so viel Code wie möglich gemeinsam zu verwenden, besteht darin, Utf8JsonReader und Newtonsoft.Json.JsonTextReader in einen ref struct-Wrapper einzuschließen. Mit diesem Wrapper wird der öffentliche Oberflächenbereich vereinheitlicht, während die Verhaltensunterschiede isoliert werden. Auf diese Weise können Sie die Änderungen hauptsächlich auf die Konstruktion des Typs beschränken und den neuen Typ gleichzeitig als Verweis übergeben. Dies ist das Muster, an dem sich die Microsoft.Extensions.DependencyModel-Bibliothek orientiert:

Utf8JsonWriter und JsonTextWriter

Mit System.Text.Json.Utf8JsonWriter lassen sich in UTF-8 codierte JSON-Texte aus gängigen .NET-Typen wie String, Int32 und DateTime schnell mit hohem Durchsatz schreiben. Der Writer ist ein Low-Level-Typ, der zum Erstellen von benutzerdefinierten Serialisierungsmodulen genutzt werden kann.

Schreiben von Rohwerten

Newtonsoft.Json enthält eine WriteRawValue-Methode, die JSON-Rohcode schreibt, wenn ein Wert erwartet wird. System.Text.Json hat eine direkte Entsprechung: Utf8JsonWriter.WriteRawValue. Weitere Informationen finden Sie unter Schreiben von JSON-Rohdaten.

Anpassen des JSON-Formats

JsonTextWriter umfasst die folgenden Einstellungen, für die es bei Utf8JsonWriter keine Entsprechung gibt:

  • QuoteChar: Gibt das Zeichen an, das zum Umschließen von Zeichenfolgenwerten verwendet werden soll. Utf8JsonWriter verwendet immer doppelte Anführungszeichen.
  • QuoteName: Gibt an, ob Eigenschaftsnamen in Anführungszeichen gesetzt werden sollen oder nicht. Utf8JsonWriter umschließt sie immer mit Anführungszeichen.

Ab .NET 9 können Sie das Einzugszeichen und die Größe für Utf8JsonWriter mithilfe von Optionen anpassen, die von der JsonWriterOptions-Struktur verfügbar gemacht werden:

  • JsonWriterOptions.IndentCharacter
  • JsonWriterOptions.IndentSize

JsonTextWriter umfasst die folgenden Einstellungen, für die es bei Utf8JsonWriter keine Entsprechung gibt:

  • Indentation: Gibt an, um wie viele Zeichen ein Einzug erfolgen soll. Utf8JsonWriter zieht immer um 2 Zeichen ein.
  • IndentChar: Gibt das für Einzüge zu verwendende Zeichen an. Utf8JsonWriter verwendet immer Leerzeichen.
  • QuoteChar: Gibt das Zeichen an, das zum Umschließen von Zeichenfolgenwerten verwendet werden soll. Utf8JsonWriter verwendet immer doppelte Anführungszeichen.
  • QuoteName: Gibt an, ob Eigenschaftsnamen in Anführungszeichen gesetzt werden sollen oder nicht. Utf8JsonWriter umschließt sie immer mit Anführungszeichen.

Es gibt keine Problemumgehungen, mit denen Sie den von Utf8JsonWriter auf diese Weisen erzeugten JSON-Code anpassen können.

Schreiben von TimeSpan-, Uri- oder char-Werten

JsonTextWriter stellt WriteValue-Methoden für TimeSpan-, URI- und char-Werte bereit. Utf8JsonWriter verfügt über keine entsprechenden Methoden. Formatieren Sie diese Werte stattdessen als Zeichenfolgen (z. B. durch Aufrufen von ToString()), und rufen Sie WriteStringValue auf.

Mehrere Ziele zum Schreiben von JSON-Code

Wenn Sie weiterhin Newtonsoft.Json für bestimmte Zielframeworks verwenden müssen, können Sie mehrere Zielversionen verwenden und zwei Implementierungen verwenden. Dies ist jedoch nicht trivial und erfordert einige #ifdefs sowie Quellduplizierung. Eine Möglichkeit, so viel Code wie möglich gemeinsam zu verwenden, besteht darin, Utf8JsonWriter und Newtonsoft.Json.JsonTextWriter in einen Wrapper einzuschließen. Mit diesem Wrapper wird der öffentliche Oberflächenbereich vereinheitlicht, während die Verhaltensunterschiede isoliert werden. Auf diese Weise können Sie die Änderungen hauptsächlich auf die Konstruktion des Typs beschränken. Die Microsoft.Extensions.DependencyModel-Bibliothek orientiert sich an:

„TypeNameHandling.All“ nicht unterstützt

Die Entscheidung, die TypeNameHandling.All-äquivalente Funktionalität aus System.Text.Json auszuschließen, wurde absichtlich getroffen. Das Angeben der eigenen Typinformationen durch eine JSON-Nutzdaten ist eine häufige Ursache von Sicherheitsrisiken in Webanwendungen. Insbesondere ermöglicht das Konfigurieren von Newtonsoft.Json mit TypeNameHandling.All dem Remoteclient das Einbetten einer gesamten ausführbaren Anwendung in den JSON-Nutzdaten, sodass die Webanwendung während der Deserialisierung den eingebetteten Code extrahiert und ausführt. Weitere Informationen finden Sie auf der PowerPoint-Präsentation zu „Friday the 13th: JSON Attacks“ und in den Details zu „Friday the 13th: JSON Attacks“.

JSON-Pfadabfragen werden nicht unterstützt

Das JsonDocument-DOM unterstützt keine Abfragen mithilfe des JSON-Pfads.

In einem JsonNode-DOM verfügt jede JsonNode-Instanz über eine GetPath-Methode, die einen Pfad zu diesem Knoten zurückgibt. Allerdings ist keine integrierte API zum Verarbeiten von Abfragen basierend auf JSON-Pfadabfragezeichenfolgen vorhanden.

Weitere Informationen finden Sie unter dem GitHub-Issue „dotnet/runtime #31068“.

Einige Grenzwerte sind nicht konfigurierbar

System.Text.Json legt Grenzwerte fest, die für einige Werte nicht geändert werden können, z. B. die maximale Tokengröße in Zeichen (166 MB) und in Base 64 (125 MB). Weitere Informationen finden Sie JsonConstants im Quellcode und im GitHub-Issue dotnet/runtime #39953.

„NaN“, „Infinity“, „-Infinity“

Newtonsoft parst die JSON-Zeichenfolgentoken NaN, Infinity und -Infinity. Bei System.Text.Json verwenden Sie JsonNumberHandling.AllowNamedFloatingPointLiterals. Informationen zum Verwenden dieser Einstellung finden Sie unter Zulassen oder Schreiben von Zahlen in Anführungszeichen.

Zusätzliche Ressourcen