Migrer de Newtonsoft.Json vers System.Text.Json

Cet article explique comment migrer de Newtonsoft.Json vers System.Text.Json.

L’espace de noms System.Text.Json fournit des fonctionnalités de sérialisation vers et de désérialisation à partir de JavaScript Object Notation (JSON). La bibliothèque System.Text.Json est incluse dans le runtime pour .NET Core 3.1 (et les versions ultérieures). Pour d’autres frameworks cibles, installez le package NuGet System.Text.Json. Le package prend en charge :

  • .NET Standard 2.0 et versions ultérieures
  • .NET Framework 4.7.2 et versions ultérieures
  • .NET Core 2.0, 2.1 et 2.2

System.Text.Json se concentre principalement sur les performances, la sécurité et la conformité aux normes. Il présente des différences clés dans le comportement par défaut et ne vise pas à avoir la parité des fonctionnalités avec Newtonsoft.Json. Pour certains scénarios, System.Text.Json n’offre actuellement aucune fonctionnalité intégrée, mais il existe des solutions de contournement recommandées. Pour les autres scénarios, les solutions de contournement sont peu pratiques.

Nous investissons dans l’ajout des fonctionnalités qui sont le plus souvent demandées. Si votre application dépend d’une fonctionnalité manquante, envisagez de signaler un problème dans le référentiel GitHub dotnet/runtime pour savoir si la prise en charge de votre scénario peut être ajoutée.

La majeure partie de cet article traite de l’utilisation de l’API JsonSerializer, mais il inclut également des conseils sur l’utilisation des types JsonDocument (qui représente le Document Object Model, ou DOM), Utf8JsonReader et Utf8JsonWriter.

En Visual Basic, vous ne pouvez pas utiliser Utf8JsonReader, ce qui signifie également que vous ne pouvez pas écrire de convertisseurs personnalisés. La plupart des solutions de contournement présentées ici nécessitent l’écriture de convertisseurs personnalisés. Vous pouvez écrire un convertisseur personnalisé en C# et l’inscrire dans un projet Visual Basic. Pour plus d’informations, consultez le Support Visual Basic.

Tableau des différences

Le tableau suivant répertorie les fonctionnalités de Newtonsoft.Json et les équivalents System.Text.Json. Les équivalents sont répartis dans les catégories suivantes :

  • ✔️ Pris en charge par la fonctionnalité intégrée. L’obtention d’un comportement similaire à partir de System.Text.Json peut nécessiter l’utilisation d’un attribut ou d’une option globale.
  • ⚠️ Non pris en charge, mais une solution de contournement existe. Les solutions de contournement sont des convertisseurs personnalisés, qui peuvent ne pas fournir une parité complète avec les fonctionnalités de Newtonsoft.Json. Dans certains de ces cas, l’exemple de code est fourni en tant qu’exempls. Si vous vous appuyez sur ces fonctionnalités de Newtonsoft.Json, la migration nécessite des modifications à vos modèles d’objet .NET ou d’autres modifications de code.
  • ❌ Non pris en charge, et la solution de contournement n’est pas pratique ou possible. Si vous vous appuyez sur ces fonctionnalités de Newtonsoft.Json, la migration ne sera pas possible sans modifications significatives.
Fonctionnalité Newtonsoft.Json Équivalent System.Text.Json
Désérialisation non sensible à la casse par défaut ✔️ Paramètre global PropertyNameCaseInsensitive
Noms des propriétés en camel-case ✔️ Paramètre global PropertyNamingPolicy
Noms de propriétés cas avec tirets ✔️ Stratégie d’affectation de noms avec des tirets
Échappement de caractères minimal ✔️ Échappement de caractères strict, configurable
Paramètre global NullValueHandling.Ignore ✔️ Option globale DefaultIgnoreCondition
Autoriser les commentaires ✔️ Paramètre global ReadCommentHandling
Autoriser les virgules de fin ✔️ Paramètre global AllowTrailingCommas
Inscription de convertisseur personnalisé ✔️ L’ordre de priorité diffère
Profondeur maximale par défaut de 64, configurable ✔️ Profondeur maximale par défaut 64, configurable
Paramètre global PreserveReferencesHandling ✔️ Paramètre global ReferenceHandling
Sérialiser ou désérialiser des nombres entre guillemets ✔️ Paramètre global NumberHandling, attribut [JsonNumberHandling]
Désérialiser en classes et structs immuables ✔️ JsonConstructor, C# 9 Enregistrements
Prise en charge des champs ✔️ Paramètre global IncludeFields, attribut [JsonInclude]
Paramètre global DefaultValueHandling ✔️ Paramètre global DefaultIgnoreCondition
Paramètre NullValueHandling sur [JsonProperty] ✔️ Attribut JsonIgnore
Paramètre DefaultValueHandling sur [JsonProperty] ✔️ Attribut JsonIgnore
Désérialiser Dictionary avec une clé non chaîne ✔️ Pris en charge
Prise en charge des setters et getters de propriétés non publiques ✔️ Attribut JsonInclude
Attribut [JsonConstructor] ✔️ Attribut [JsonConstructor]
Paramètre global ReferenceLoopHandling ✔️ Paramètre global ReferenceHandling
Rappels ✔️ Rappels
NaN, Infinity, -Infinity ✔️ Pris en charge
Paramètre Required sur l’attribut [JsonProperty] ✔️ Attribut [JsonRequired] et modificateur C# requis
DefaultContractResolver pour ignorer les propriétés ✔️ Classe DefaultJsonTypeInfoResolver
Sérialisation polymorphe ✔️ Attribut [JsonDerivedType]
Désérialisation polymorphe ✔️ Discriminateur de type sur l’attribut [JsonDerivedType]
Désérialiser la valeur d’une énumération de chaîne ✔️ Désérialiser les valeurs d’une énumération de chaîne
Paramètre global MissingMemberHandling ✔️ Gérer des membres manquants
Remplir les propriétés sans aucun setter ✔️ Remplir les propriétés sans aucun setter
Paramètre global ObjectCreationHandling ✔️ Réutiliser plutôt que remplacer les propriétés
Prise en charge d’un large éventail de types ⚠️ Certains types nécessitent des convertisseurs personnalisés
Désérialiser le type déduit en propriétés object ⚠️ Non pris en charge, solution de contournement, exemple
Désérialiser le littéral JSON null en types valeur non nullables ⚠️ Non pris en charge, solution de contournement, exemple
Paramètres DateTimeZoneHandling, DateFormatString ⚠️ Non pris en charge, solution de contournement, exemple
Méthode JsonConvert.PopulateObject ⚠️ Non pris en charge, solution de contournement
Prise en charge des attributs System.Runtime.Serialization ⚠️ Non pris en charge, solution de contournement, exemple
JsonObjectAttribute ⚠️ Non pris en charge, solution de contournement
Autorisation des noms de propriétés sans guillemets Non pris en charge par conception
Autorisation des guillemets uniques autour des valeurs de chaîne Non pris en charge par conception
Autorisation des valeurs JSON autres que les chaînes pour les propriétés de chaîne Non pris en charge par conception
Paramètre global TypeNameHandling.All Non pris en charge par conception
Prise en charge des requêtes JsonPath Non pris en charge
Limites configurables Non pris en charge
Fonctionnalité Newtonsoft.Json Équivalent System.Text.Json
Désérialisation non sensible à la casse par défaut ✔️ Paramètre global PropertyNameCaseInsensitive
Noms des propriétés en camel-case ✔️ Paramètre global PropertyNamingPolicy
Échappement de caractères minimal ✔️ Échappement de caractères strict, configurable
Paramètre global NullValueHandling.Ignore ✔️ Option globale DefaultIgnoreCondition
Autoriser les commentaires ✔️ Paramètre global ReadCommentHandling
Autoriser les virgules de fin ✔️ Paramètre global AllowTrailingCommas
Inscription de convertisseur personnalisé ✔️ L’ordre de priorité diffère
Profondeur maximale par défaut de 64, configurable ✔️ Profondeur maximale par défaut 64, configurable
Paramètre global PreserveReferencesHandling ✔️ Paramètre global ReferenceHandling
Sérialiser ou désérialiser des nombres entre guillemets ✔️ Paramètre global NumberHandling, attribut [JsonNumberHandling]
Désérialiser en classes et structs immuables ✔️ JsonConstructor, C# 9 Enregistrements
Prise en charge des champs ✔️ Paramètre global IncludeFields, attribut [JsonInclude]
Paramètre global DefaultValueHandling ✔️ Paramètre global DefaultIgnoreCondition
Paramètre NullValueHandling sur [JsonProperty] ✔️ Attribut JsonIgnore
Paramètre DefaultValueHandling sur [JsonProperty] ✔️ Attribut JsonIgnore
Désérialiser Dictionary avec une clé non chaîne ✔️ Pris en charge
Prise en charge des setters et getters de propriétés non publiques ✔️ Attribut JsonInclude
Attribut [JsonConstructor] ✔️ Attribut [JsonConstructor]
Paramètre global ReferenceLoopHandling ✔️ Paramètre global ReferenceHandling
Rappels ✔️ Rappels
NaN, Infinity, -Infinity ✔️ Pris en charge
Paramètre Required sur l’attribut [JsonProperty] ✔️ Attribut [JsonRequired] et modificateur C# requis
DefaultContractResolver pour ignorer les propriétés ✔️ Classe DefaultJsonTypeInfoResolver
Sérialisation polymorphe ✔️ Attribut [JsonDerivedType]
Désérialisation polymorphe ✔️ Discriminateur de type sur l’attribut [JsonDerivedType]
Désérialiser la valeur d’une énumération de chaîne ✔️ Désérialiser les valeurs d’une énumération de chaîne
Prise en charge d’un large éventail de types ⚠️ Certains types nécessitent des convertisseurs personnalisés
Désérialiser le type déduit en propriétés object ⚠️ Non pris en charge, solution de contournement, exemple
Désérialiser le littéral JSON null en types valeur non nullables ⚠️ Non pris en charge, solution de contournement, exemple
Paramètres DateTimeZoneHandling, DateFormatString ⚠️ Non pris en charge, solution de contournement, exemple
Méthode JsonConvert.PopulateObject ⚠️ Non pris en charge, solution de contournement
Paramètre global ObjectCreationHandling ⚠️ Non pris en charge, solution de contournement
Ajouter aux collections sans setters ⚠️ Non pris en charge, solution de contournement
Noms de propriétés cas avec tirets ⚠️ Non pris en charge, solution de contournement
Prise en charge des attributs System.Runtime.Serialization ⚠️ Non pris en charge, solution de contournement, exemple
Paramètre global MissingMemberHandling ⚠️ Non pris en charge, solution de contournement, exemple
JsonObjectAttribute ⚠️ Non pris en charge, solution de contournement
Autorisation des noms de propriétés sans guillemets Non pris en charge par conception
Autorisation des guillemets uniques autour des valeurs de chaîne Non pris en charge par conception
Autorisation des valeurs JSON autres que les chaînes pour les propriétés de chaîne Non pris en charge par conception
Paramètre global TypeNameHandling.All Non pris en charge par conception
Prise en charge des requêtes JsonPath Non pris en charge
Limites configurables Non pris en charge
Fonctionnalité Newtonsoft.Json Équivalent System.Text.Json
Désérialisation non sensible à la casse par défaut ✔️ Paramètre global PropertyNameCaseInsensitive
Noms des propriétés en camel-case ✔️ Paramètre global PropertyNamingPolicy
Échappement de caractères minimal ✔️ Échappement de caractères strict, configurable
Paramètre global NullValueHandling.Ignore ✔️ Option globale DefaultIgnoreCondition
Autoriser les commentaires ✔️ Paramètre global ReadCommentHandling
Autoriser les virgules de fin ✔️ Paramètre global AllowTrailingCommas
Inscription de convertisseur personnalisé ✔️ L’ordre de priorité diffère
Profondeur maximale par défaut de 64, configurable ✔️ Profondeur maximale par défaut 64, configurable
Paramètre global PreserveReferencesHandling ✔️ Paramètre global ReferenceHandling
Sérialiser ou désérialiser des nombres entre guillemets ✔️ Paramètre global NumberHandling, attribut [JsonNumberHandling]
Désérialiser en classes et structs immuables ✔️ JsonConstructor, C# 9 Enregistrements
Prise en charge des champs ✔️ Paramètre global IncludeFields, attribut [JsonInclude]
Paramètre global DefaultValueHandling ✔️ Paramètre global DefaultIgnoreCondition
Paramètre NullValueHandling sur [JsonProperty] ✔️ Attribut JsonIgnore
Paramètre DefaultValueHandling sur [JsonProperty] ✔️ Attribut JsonIgnore
Désérialiser Dictionary avec une clé non chaîne ✔️ Pris en charge
Prise en charge des setters et getters de propriétés non publiques ✔️ Attribut JsonInclude
Attribut [JsonConstructor] ✔️ Attribut [JsonConstructor]
Paramètre global ReferenceLoopHandling ✔️ Paramètre global ReferenceHandling
Rappels ✔️ Rappels
NaN, Infinity, -Infinity ✔️ Pris en charge
Désérialiser la valeur d’une énumération de chaîne ✔️ Désérialiser les valeurs d’une énumération de chaîne
Prise en charge d’un large éventail de types ⚠️ Certains types nécessitent des convertisseurs personnalisés
Sérialisation polymorphe ⚠️ Non pris en charge, solution de contournement, exemple
Désérialisation polymorphe ⚠️ Non pris en charge, solution de contournement, exemple
Désérialiser le type déduit en propriétés object ⚠️ Non pris en charge, solution de contournement, exemple
Désérialiser le littéral JSON null en types valeur non nullables ⚠️ Non pris en charge, solution de contournement, exemple
Paramètre Required sur l’attribut [JsonProperty] ⚠️ Non pris en charge, solution de contournement, exemple
DefaultContractResolver pour ignorer les propriétés ⚠️ Non pris en charge, solution de contournement, exemple
Paramètres DateTimeZoneHandling, DateFormatString ⚠️ Non pris en charge, solution de contournement, exemple
Méthode JsonConvert.PopulateObject ⚠️ Non pris en charge, solution de contournement
Paramètre global ObjectCreationHandling ⚠️ Non pris en charge, solution de contournement
Ajouter aux collections sans setters ⚠️ Non pris en charge, solution de contournement
Noms de propriétés cas avec tirets ⚠️ Non pris en charge, solution de contournement
JsonObjectAttribute ⚠️ Non pris en charge, solution de contournement
Prise en charge des attributs System.Runtime.Serialization Non pris en charge
Paramètre global MissingMemberHandling Non pris en charge
Autorisation des noms de propriétés sans guillemets Non pris en charge par conception
Autorisation des guillemets uniques autour des valeurs de chaîne Non pris en charge par conception
Autorisation des valeurs JSON autres que les chaînes pour les propriétés de chaîne Non pris en charge par conception
Paramètre global TypeNameHandling.All Non pris en charge par conception
Prise en charge des requêtes JsonPath Non pris en charge
Limites configurables Non pris en charge

Il ne s’agit pas d’une liste exhaustive des fonctionnalités de Newtonsoft.Json. La liste inclut de nombreux scénarios qui ont été demandés dans des problèmes GitHub ou des messages sur StackOverflow. Si vous implémentez une solution de contournement pour l’un des scénarios répertoriés ici et que vous ne disposez pas actuellement d’un exemple de code, et si vous souhaitez partager votre solution, sélectionnez Cette page dans la section Commentaires en bas de cette page. Cela crée un problème dans le référentiel GitHub de cette documentation et le répertorie également dans la section Commentaires de cette page.

Différences dans le comportement par défaut

System.Text.Json est strict par défaut et évite toute estimation ou interprétation au nom de l’appelant, en mettant l’accent sur le comportement déterministe. La bibliothèque est intentionnellement conçue de cette façon à des fins de performances et de sécurité. Newtonsoft.Json est flexible par défaut. Cette différence fondamentale dans la conception est à l’origine de la plupart des différences spécifiques suivantes dans le comportement par défaut.

Désérialisation non sensible à la casse

Pendant la désérialisation, Newtonsoft.Json ne respecte pas la casse par défaut lors de la mise en correspondance. System.Text.Json est par défaut sensible à la casse, ce qui offre de meilleures performances, car elle effectue une correspondance exacte. Pour plus d’informations sur la façon d’effectuer une correspondance sans respect de la casse, consultez Correspondance de propriétés sans respect de la casse.

Si vous utilisez System.Text.Json indirectement en utilisant ASP.NET Core, vous n’avez rien à faire pour obtenir un comportement comme Newtonsoft.Json. ASP.NET Core spécifie les paramètres pour les noms de propriétés à casse mixte et la correspondance ne respectant pas la casse lorsqu’il utilise System.Text.Json.

ASP.NET Core permet également la désérialisation des nombres entre guillemets par défaut.

Échappement de caractères minimal

Pendant la sérialisation, Newtonsoft.Json est relativement permissif pour ce qui est de laisser passer des caractères sans les échapper. Autrement dit, il ne les remplace pas par \uxxxx, où xxxx est le point de code du caractère. Lorsqu’il les échappe, il le fait en émettant un \ avant le caractère (par exemple, " devient \"). System.Text.Json échappe davantage de caractères par défaut pour fournir des protections de défense en profondeur contre les attaques de scripting inter-site (XSS) ou de divulgation d’informations, et le fait à l’aide de la séquence de six caractères. System.Text.Json échappe tous les caractères non ASCII par défaut. Vous n’avez donc rien à faire si vous utilisez StringEscapeHandling.EscapeNonAscii dans Newtonsoft.Json. System.Text.Json permet également d’échapper les caractères HTML, par défaut. Pour plus d’informations sur la façon de remplacer le comportement par défaut de System.Text.Json, consultez Personnaliser l’encodage de caractères.

Commentaires

Pendant la désérialisation, Newtonsoft.Json ignore les commentaires dans le JSON par défaut. Par défaut, System.Text.Json lève des exceptions pour les commentaires, car la spécification RFC 8259 ne les inclut pas. Pour plus d’informations sur l’autorisation des commentaires, consultez Autoriser les commentaires et les virgules de fin.

Virgules de fin

Pendant la désérialisation, Newtonsoft.Json ignore les virgules de fin par défaut. Il ignore également les virgules de fin multiples (par exemple, [{"Color":"Red"},{"Color":"Green"},,]). Par défaut, System.Text.Json lève des exceptions pour les virgules de fin, car la spécification RFC 8259 ne les autorise pas. Pour plus d’informations sur la façon d’obliger System.Text.Json à les accepter, consultez Autoriser les commentaires et les virgules de fin. Il n’existe aucun moyen d’autoriser les virgules de fin multiples.

Priorité d’inscription du convertisseur

La priorité d’inscription Newtonsoft.Json pour les convertisseurs personnalisés est la suivante :

  • Attribut sur la propriété
  • Attribut sur le type
  • Collection Converters

Cet ordre signifie qu’un convertisseur personnalisé dans la collection Converters est remplacé par un convertisseur inscrit en appliquant un attribut au niveau du type. Ces deux inscriptions sont remplacées par un attribut au niveau de la propriété.

La priorité d’inscription System.Text.Json pour les convertisseurs personnalisés est différente :

  • Attribut sur la propriété
  • Converterscollection
  • Attribut sur le type

La différence est qu’un convertisseur personnalisé dans la collection Converters remplace un attribut au niveau du type. L’objectif de cet ordre de priorité est de faire en sorte que les modifications au moment de l’exécution remplacent les choix au moment de la conception. Il n’existe aucun moyen de modifier la priorité.

Pour plus d’informations sur l’inscription d’un convertisseur personnalisé, consultez Inscrire un convertisseur personnalisé.

Profondeur maximale

La dernière version de Newtonsoft.Json a une limite de profondeur maximale de 64 par défaut. System.Text.Json a également une limite par défaut de 64, et elle est configurable en définissant JsonSerializerOptions.MaxDepth.

Si vous utilisez System.Text.Json indirectement en utilisant ASP.NET Core, la limite de profondeur maximale par défaut est de 32. La valeur par défaut est la même que pour la liaison de modèle et est définie dans la classe JsonOptions.

Chaînes JSON (noms de propriétés et valeurs de chaîne)

Pendant la désérialisation, Newtonsoft.Json accepte les noms de propriétés entourés de guillemets doubles, de guillemets simples ou sans guillemets. Il accepte les valeurs de chaîne entourées de guillemets doubles ou de guillemets simples. Par exemple, Newtonsoft.Json accepte le JSON suivant :

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

System.Text.Json Accepte uniquement les noms de propriétés et les valeurs de chaîne entre guillemets doubles, car ce format est requis par la spécification RFC 8259 et est le seul format considéré comme du JSON valide.

Une valeur placée entre guillemets simples entraîne une exception JsonException avec le message suivant :

''' is an invalid start of a value.

Valeurs autres que des chaînes pour les propriétés de chaîne

Newtonsoft.Json accepte des valeurs autres que des chaînes, comme un nombre ou les littéraux true et false, pour la désérialisation vers les propriétés de type chaîne. Voici un exemple de JSON que Newtonsoft.Json désérialise correctement en la classe suivante :

{
  "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 ne désérialise pas les valeurs autres que les chaînes en propriétés de chaîne. Une valeur autre que la chaîne reçue pour un champ de chaîne entraîne une exception JsonException avec le message suivant :

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

Scénarios utilisant JsonSerializer

Certains des scénarios suivants ne sont pas pris en charge par les fonctionnalités intégrées, mais des solutions de contournement sont possibles. Les solutions de contournement sont des convertisseurs personnalisés, qui peuvent ne pas fournir une parité complète avec les fonctionnalités de Newtonsoft.Json. Dans certains de ces cas, l’exemple de code est fourni en tant qu’exempls. Si vous vous appuyez sur ces fonctionnalités de Newtonsoft.Json, la migration nécessite des modifications à vos modèles d’objet .NET ou d’autres modifications de code.

Pour certains des scénarios suivants, les solutions de contournement ne sont pas pratiques ou possibles. Si vous vous appuyez sur ces fonctionnalités de Newtonsoft.Json, la migration ne sera pas possible sans modifications significatives.

Autoriser ou écrire des nombres entre guillemets

Newtonsoft.Json peut sérialiser ou désérialiser des nombres représentés par des chaînes JSON (entourées de guillemets). Par exemple, il peut accepter {"DegreesCelsius":"23"} au lieu de {"DegreesCelsius":23}. Pour activer ce comportement dans System.Text.Json, définissez JsonSerializerOptions.NumberHandling sur WriteAsString ou AllowReadingFromString, ou utilisez l’attribut [JsonNumberHandling].

Si vous utilisez System.Text.Json indirectement en utilisant ASP.NET Core, vous n’avez rien à faire pour obtenir un comportement comme Newtonsoft.Json. ASP.NET Core spécifie les valeurs web par défaut lorsqu’il utilise System.Text.Json, et les valeurs par défaut web autorisent les nombres entre guillemets.

Pour plus d’informations, consultez Autoriser ou écrire des nombres entre guillemets.

Spécifier le constructeur à utiliser lors de la désérialisation

L’attribut Newtonsoft.Json[JsonConstructor] vous permet de spécifier le constructeur à appeler lors de la désérialisation d’un OCT.

System.Text.Json a également un attribut [JsonConstructor]. Pour plus d’informations, consultez Types et enregistrements immuables.

Ignorer une propriété de manière conditionnelle

Newtonsoft.Json propose plusieurs façons d’ignorer de manière conditionnelle une propriété lors de la sérialisation ou de la désérialisation :

  • DefaultContractResolver vous permet de sélectionner des propriétés à inclure ou à ignorer, en fonction de critères arbitraires.
  • Les paramètres NullValueHandling et DefaultValueHandling sur JsonSerializerSettings vous permettent de spécifier que toutes les propriétés à valeur null ou valeur par défaut doivent être ignorées.
  • Les paramètres NullValueHandling et DefaultValueHandling sur l’attribut [JsonProperty] vous permettent de spécifier des propriétés individuelles qui doivent être ignorées lorsqu’elles sont définies sur null ou sur la valeur par défaut.

System.Text.Json fournit les méthodes suivantes pour ignorer les propriétés ou les champs lors de la sérialisation :

En outre, dans .NET 7 et versions ultérieures, vous pouvez personnaliser le contrat JSON pour ignorer les propriétés en fonction de critères arbitraires. Pour plus d’informations, consultez Contrats personnalisés.

Ces options ne vous permettent pas d’ignorer les propriétés sélectionnées en fonction de critères arbitraires évalués au moment de l’exécution.

Champs publics et non publics

Newtonsoft.Json peut sérialiser et désérialiser des champs ainsi que des propriétés.

Dans System.Text.Json, utilisez le paramètre global JsonSerializerOptions.IncludeFields ou l’attribut [JsonInclude] pour inclure des champs publics lors de la sérialisation ou de la désérialisation. Pour obtenir un exemple, consultez Inclure les champs.

Conserver les références d’objet et gérer les boucles

Par défaut, Newtonsoft.Json sérialise par valeur. Par exemple, si un objet contient deux propriétés qui contiennent une référence au même objet Person, les valeurs des propriétés de cet objet Person sont dupliquées dans le JSON.

Newtonsoft.Json a un paramètre PreserveReferencesHandling sur JsonSerializerSettings qui vous permet de sérialiser par référence :

  • Une métadonnée d’identificateur est ajoutée au JSON créé pour le premier objet Person.
  • Le JSON créé pour le deuxième objet Person contient une référence à cet identificateur au lieu des valeurs de propriété.

Newtonsoft.Json a également un paramètre ReferenceLoopHandling qui vous permet d’ignorer les références circulaires plutôt que de lever une exception.

Pour conserver les références et gérer les références circulaires dans System.Text.Json, définissez JsonSerializerOptions.ReferenceHandler sur Preserve. Le paramètre ReferenceHandler.Preserve équivaut à PreserveReferencesHandling = PreserveReferencesHandling.All dans Newtonsoft.Json.

L’option ReferenceHandler.IgnoreCycles a un comportement similaire à Newtonsoft.JsonReferenceLoopHandling.Ignore. Une différence est que l’implémentation de System.Text.Json remplace les boucles de référence par le jeton JSON null au lieu d’ignorer la référence d’objet. Pour plus d’informations, consultez Ignorer les références circulaires.

À l’instar de Newtonsoft.JsonReferenceResolver, la classe System.Text.Json.Serialization.ReferenceResolver définit le comportement de conservation des références lors de la sérialisation et de la désérialisation. Créez une classe dérivée pour spécifier un comportement personnalisé. Pour obtenir un exemple, consultez GuidReferenceResolver.

Certaines fonctionnalités associées à Newtonsoft.Json ne sont pas prises en charge :

Pour plus d’informations, consultez Conserver les références et gérer les références circulaires.

Dictionnaire avec une clé non-chaîne

Newtonsoft.Json et System.Text.Json prennent tous deux en charge les collections de type Dictionary<TKey, TValue>. Toutefois, dans System.Text.Json, TKey doit être un type primitif, et non un type personnalisé. Pour plus d’informations, consultez Types de clés pris en charge.

Attention

La désérialisation en Dictionary<TKey, TValue>, où TKey a une autre valeur que string pourrait introduire une vulnérabilité de sécurité dans l’application consommatrice. Pour plus d’informations, consultez dotnet/runtime#4761.

Types sans prise en charge intégrée

System.Text.Json ne fournit pas de prise en charge intégrée pour les types suivants :

Des convertisseurs personnalisés peuvent être implémentés pour les types qui n’ont pas de prise en charge intégrée.

Sérialisation polymorphe

Newtonsoft.Json effectue automatiquement une sérialisation polymorphe. À partir de .NET 7, System.Text.Json prend en charge la sérialisation polymorphe via l’attribut JsonDerivedTypeAttribute. Pour plus d’informations, consultez Sérialiser les propriétés des classes dérivées.

Désérialisation polymorphe

Newtonsoft.Json a un paramètre TypeNameHandling qui ajoute des métadonnées de nom de type au JSON lors de la sérialisation. Il utilise les métadonnées lors de la désérialisation pour effectuer une désérialisation polymorphe. À partir de .NET 7, System.Text.Json s’appuie sur les informations de discriminateur de type pour effectuer une désérialisation polymorphe. Ces métadonnées sont émises dans le JSON, puis utilisées lors de la désérialisation pour déterminer s’il faut désérialiser vers le type de base ou un type dérivé. Pour plus d’informations, consultez Sérialiser les propriétés des classes dérivées.

Pour prendre en charge la désérialisation polymorphe dans les versions antérieures de .NET, créez un convertisseur comme dans l’exemple de Guide pratique pour écrire des convertisseurs personnalisés.

Désérialiser les valeurs d’une énumération de chaîne

Par défaut, System.Text.Json ne prend pas en charge la désérialisation des valeurs d’une énumération de chaîne, tandis que Newtonsoft.Json le fait. Par exemple, le code suivant lève une JsonException :

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
}

Toutefois, vous pouvez activer la désérialisation des valeurs d’une énumération de chaîne en utilisant le convertisseur JsonStringEnumConverter. Pour obtenir plus d’informations, consultez Énumérations en tant que chaînes.

Désérialisation des propriétés d’objet

Quand Newtonsoft.Json désérialise en Object, il :

  • Déduit le type des valeurs primitives dans la charge utile JSON (autre que null) et retourne le string, long, double, boolean ou DateTime stocké en tant qu’objet boxed. Les valeurs primitives sont des valeurs JSON uniques comme un nombre JSON, une chaîne, true, false ou null.
  • Retourne un JObject ou JArray pour les valeurs complexes dans la charge utile JSON. Les valeurs complexes sont des collections de paires clé-valeur JSON dans des accolades ({}) ou des listes de valeurs entre crochets ([]). Les propriétés et valeurs dans les accolades ou crochets peuvent avoir des propriétés ou des valeurs supplémentaires.
  • Retourne une référence null lorsque la charge utile a le littéral JSON null.

System.Text.Json stocke un JsonElement boxed pour les valeurs primitives et complexes lors de chaque désérialisation en Object, par exemple :

  • Propriété object.
  • Valeur de dictionnaire object.
  • Valeur de tableau object.
  • object racine.

Toutefois, System.Text.Json traite null de manière identique à Newtonsoft.Json et retourne une référence null lorsque la charge utile contient le littéral JSON null.

Pour implémenter l’inférence de type pour les propriétés object, créez un convertisseur comme dans l’exemple dans Guide pratique pour écrire des convertisseurs personnalisés.

Désérialiser null en type non nullable

Newtonsoft.Json ne lève pas d’exception dans le scénario suivant :

  • NullValueHandling est défini sur Ignore, et
  • Pendant la désérialisation, le JSON contient une valeur null pour un type de valeur non nullable.

Dans le même scénario, System.Text.Json lève une exception. (Le paramètre de gestion de la valeur null correspondant dans System.Text.Json est JsonSerializerOptions.IgnoreNullValues = true.)

Si vous êtes propriétaire du type cible, la meilleure solution de contournement consiste à rendre la propriété en question nullable (par exemple en modifiant int en int?).

Une autre solution de contournement consiste à créer un convertisseur pour le type, comme dans l’exemple suivant qui gère les valeurs null pour les types DateTimeOffset :

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);
    }
}

Inscrivez ce convertisseur personnalisé à l’aide d’un attribut sur la propriété ou en ajoutant le convertisseur à la collection Converters.

Remarque : Le convertisseur précédent gère les valeurs null différemment de Newtonsoft.Json pour les OCT qui spécifient des valeurs par défaut. Par exemple, supposons que le code suivant représente votre objet cible :

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; }
}

Supposons que le code JSON suivant soit désérialisé à l’aide du convertisseur précédent :

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

Après la désérialisation, la propriété Date a la valeur 1/1/0001 (default(DateTimeOffset)), c’est-à-dire que la valeur définie dans le constructeur est remplacée. Pour les mêmes OCT et JSON, la désérialisation de Newtonsoft.Json laisserait 1/1/2001 dans la propriété Date.

Désérialiser en classes et structs immuables

Newtonsoft.Json peut désérialiser en classes et structs immuables, car il peut utiliser des constructeurs qui ont des paramètres.

Dans System.Text.Json, utilisez l’attribut [JsonConstructor] pour spécifier l’utilisation d’un constructeur paramétrable. Les enregistrements dans C# 9 sont également immuables et pris en charge en tant que cibles de désérialisation. Pour plus d’informations, consultez Types et enregistrements immuables.

Propriétés requises

Dans Newtonsoft.Json, vous spécifiez qu’une propriété est requise en définissant Required sur l’attribut [JsonProperty]. Newtonsoft.Json lève une exception si aucune valeur n’est reçue dans le JSON pour une propriété marquée comme obligatoire.

À partir de .NET 7, vous pouvez utiliser le modificateur C# required ou l’attribut JsonRequiredAttribute sur une propriété requise. System.Text.Json lève une exception si la charge utile JSON ne contient pas de valeur pour la propriété marquée. Pour plus d’informations, consultez Propriétés requises.

System.Text.Json ne lève pas d’exception si aucune valeur n’est reçue pour l’une des propriétés du type cible. Par exemple, si vous avez une classe WeatherForecast :

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

Le code JSON suivant est désérialisé sans erreur :

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

Pour faire échouer la désérialisation si aucune propriété Date ne se trouve dans le JSON, choisissez l’une des options suivantes :

L’exemple de code de convertisseur suivant lève une exception si la propriété Date n’est pas définie une fois la désérialisation terminée :

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);
        }
    }
}

Inscrivez ce convertisseur personnalisé en ajoutant le convertisseur à la collection JsonSerializerOptions.Converters.

Ce modèle d’appel récursif du convertisseur nécessite que vous inscriviez le convertisseur à l’aide de JsonSerializerOptions, et non d’un attribut. Si vous inscrivez le convertisseur à l’aide d’un attribut, le convertisseur personnalisé s’appelle de manière récursive. Le résultat est une boucle infinie qui se termine par une exception de dépassement de capacité de la pile.

Lorsque vous inscrivez le convertisseur à l’aide de l’objet options, évitez une boucle infinie en ne transmettant pas l’objet options lors de l’appel récursif à Serialize ou Deserialize. L’objet options contient la collection Converters. Si vous le transmettez à Serialize ou Deserialize, le convertisseur personnalisé s’appelle lui-même, en faisant une boucle infinie qui entraîne une exception de dépassement de capacité de la pile. Si les options par défaut ne sont pas réalisables, créez une instance des options avec les paramètres dont vous avez besoin. Cette approche sera lente, car chaque nouvelle instance est mise en cache indépendamment.

Il existe un autre modèle qui peut utiliser l’inscription de JsonConverterAttribute sur la classe à convertir. Dans cette approche, le code du convertisseur appelle Serialize ou Deserialize sur une classe qui dérive de la classe à convertir. La classe dérivée n’a pas de JsonConverterAttribute appliqué. Dans l’exemple suivant de cette alternative :

  • WeatherForecastWithRequiredPropertyConverterAttribute est la classe à désérialiser et à laquelle le JsonConverterAttribute est appliqué.
  • WeatherForecastWithoutRequiredPropertyConverterAttribute est la classe dérivée qui n’a pas l’attribut de convertisseur.
  • Le code du convertisseur appelle Serialize et Deserialize sur WeatherForecastWithoutRequiredPropertyConverterAttribute pour éviter une boucle infinie. Cette approche de la sérialisation entraîne un coût de performances en raison de l’ajout d’une instanciation d’objet et d’une copie des valeurs de propriété.

Voici les types WeatherForecast* :

[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
{
}

Et voici le convertisseur :

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);
        }
    }
}

Le convertisseur de propriétés requis nécessite une logique supplémentaire si vous devez gérer des attributs comme [JsonIgnore] ou différentes options, comme des encodeurs personnalisés. En outre, l’exemple de code ne gère pas les propriétés pour lesquelles une valeur par défaut est définie dans le constructeur. Et cette approche ne fait pas la différence entre les scénarios suivants :

  • Une propriété est manquante dans le JSON.
  • Une propriété pour un type non nullable est présente dans le JSON, mais la valeur est la valeur par défaut pour le type, par exemple zéro pour un int.
  • Une propriété pour un type valeur nullable est présente dans le JSON, mais la valeur est null.

Notes

Si vous utilisez System.Text.Json à partir d’un contrôleur ASP.NET Core, vous pourrez peut-être utiliser un attribut [Required] sur les propriétés de la classe de modèle au lieu d’implémenter un convertisseur System.Text.Json.

Spécifier le format de date

Newtonsoft.Json offre plusieurs façons de contrôler la façon dont les propriétés des types DateTime et DateTimeOffset sont sérialisées et désérialisées :

  • Le paramètre DateTimeZoneHandling peut être utilisé pour sérialiser toutes les valeurs de DateTime sous forme de dates UTC.
  • Le paramètre DateFormatString et les convertisseurs DateTime peuvent être utilisés pour personnaliser le format des chaînes de date.

System.Text.Json prend en charge ISO 8601-1:2019, y compris le profil RFC 3339. Ce format est largement adopté, sans ambiguïté, et fait des allers-retours précis. Pour utiliser un autre format, créez un convertisseur personnalisé. Par exemple, les convertisseurs suivants sérialisent et désérialisent du JSON qui utilise le format d’époque Unix avec ou sans décalage de fuseau horaire (des valeurs comme /Date(1590863400000-0700)/ ou /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);
    }
}

Pour plus d’informations, consultez Prise en charge de DateTime et DateTimeOffset dans System.Text.Json.

Rappels

Newtonsoft.Json vous permet d’exécuter du code personnalisé à plusieurs points du processus de sérialisation ou de désérialisation :

  • OnDeserializing (lorsque vous commencez à désérialiser un objet)
  • OnDeserialized (lorsque vous avez terminé la désérialisation d’un objet)
  • OnSerializing (lorsque vous commencez à sérialiser un objet)
  • OnSerialized (lorsque vous avez terminé de sérialiser un objet)

System.Text.Json expose les mêmes notifications pendant la sérialisation et la désérialisation. Pour les utiliser, implémentez une ou plusieurs des interfaces suivantes à partir de l’espace de noms System.Text.Json.Serialization :

Voici un exemple qui recherche une propriété null et écrit des messages au début et à la fin de la sérialisation et de la désérialisation :

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=

Le code OnDeserializing n’a pas accès à la nouvelle instance d’OCT. Pour manipuler la nouvelle instance d’OCT au début de la désérialisation, placez ce code dans le constructeur de l’OCT.

Setters et getters de propriétés non publiques

Newtonsoft.Json peut utiliser des setters et getters de propriétés privées et internes via l’attribut JsonProperty.

System.Text.Json prend en charge les setters et getters de propriétés privées et internes via l’attribut [JsonInclude]. Pour obtenir un exemple de code, consultez Accesseurs de propriétés non publiques.

Remplir des objets existants

La méthode JsonConvert.PopulateObject dans Newtonsoft.Json désérialise un document JSON sur une instance existante d’une classe au lieu de créer une nouvelle instance. System.Text.Json crée toujours une instance du type cible à l’aide du constructeur public sans paramètre par défaut. Les convertisseurs personnalisés peuvent désérialiser vers une instance existante.

Réutiliser plutôt que remplacer les propriétés

À compter de .NET 8, System.Text.Json prend en charge la réutilisation des propriétés initialisées plutôt que de les remplacer. Il existe des différences de comportement, que vous pouvez lire dans la proposition d’API.

Pour plus d’informations, consultez Remplir les propriétés initialisées.

Le paramètre ObjectCreationHandling dans Newtonsoft.Json vous permet de spécifier que les objets dans les propriétés doivent être réutilisés plutôt que remplacés lors de la désérialisation. System.Text.Json remplace toujours les objets dans les propriétés. Les convertisseurs personnalisés peuvent fournir cette fonctionnalité, ou vous pouvez effectuer une mise à niveau vers .NET 8 qui fournit des fonctionnalités de remplissage.

Remplir les propriétés sans aucun setter

À compter de .NET 8, System.Text.Json prend en charge le remplissage des propriétés, notamment celles qui n’ont pas de setter. Pour plus d’informations, consultez Remplir les propriétés initialisées.

Pendant la désérialisation, Newtonsoft.Json ajoute des objets à une collection même si la propriété n’a pas de setter. System.Text.Json ignore les propriétés qui n’ont pas de setters. Les convertisseurs personnalisés peuvent fournir cette fonctionnalité, ou vous pouvez effectuer une mise à niveau vers .NET 8 qui peut remplir les propriétés en lecture seule.

Stratégie d’attribution de noms avec tirets

System.Text.Json inclut une stratégie intégrée d’affectation de noms avec des tirets. Toutefois, il existe des différences de comportement avec Newtonsoft.Json pour certaines entrées. Le tableau suivant présente certaines de ces différences lors de la conversion d’entrée à l’aide de la stratégie JsonNamingPolicy.SnakeCaseLower.

Entrée Résultat Newtonsoft.Json Résultat System.Text.Json
« AB1 » « a_b1 » « ab1 »
« SHA512Managed » « sh_a512_managed » « sha512_managed »
« abc123DEF456 » « abc123_de_f456 » « abc123_def456 »
« KEBAB-CASE » « keba_b-_case » « kebab-case »

La seule stratégie intégrée d’attribution de noms de propriété dans System.Text.Json est la casse mixte. Newtonsoft.Json peut convertir des noms de propriétés en noms à tirets. Une stratégie d’affectation de noms personnalisée peut fournir cette fonctionnalité. Sinon, effectuez une mise à niveau vers .NET 8 ou version ultérieure, qui inclut des stratégies intégrées d’affectation de noms avec des tirets.

Attributs System.Runtime.Serialization

Les attributs System.Runtime.Serialization comme DataContractAttribute, DataMemberAttribute et IgnoreDataMemberAttribute vous permettent de définir un contrat de données. Un contrat de données est un accord en bonne et due forme entre un service et un client qui décrit de manière abstraite les données à échanger. Le contrat de données définit précisément les propriétés sérialisées pour l’échange.

System.Text.Json n’a pas de prise en charge intégrée pour ces attributs. Toutefois, à partir de .NET 7, vous pouvez utiliser un programme de résolution de type personnalisé pour ajouter cette prise en charge. Pour obtenir un exemple, consultez ZCS.DataContractResolver.

Nombres octaux

Newtonsoft.Json traite les nombres avec un zéro en tête en tant que nombres octaux. System.Text.Json n’autorise pas les zéros principaux, car la spécification RFC 8259 ne les autorise pas.

Gérer les membres manquants

Pendant la désérialisation, si le JSON inclut des propriétés manquantes dans le type cible, Newtonsoft.Json peut être configuré pour lever des exceptions. System.Text.Json ignore par défaut les propriétés supplémentaires dans le JSON, sauf lorsque vous utilisez l’attribut [JsonExtensionData].

Dans .NET 8 et versions ultérieures, vous pouvez définir votre préférence pour ignorer ou interdire les propriétés JSON non mappées en tirant parti de l’un des moyens suivants :

JsonObjectAttribute

Newtonsoft.Json a un attribut (JsonObjectAttribute) qui peut être appliqué au niveau du type pour contrôler les membres sérialisés, la façon dont les valeurs null sont gérées et si tous les membres sont requis. System.Text.Json n’a aucun attribut équivalent pouvant être appliqué à un type. Pour certains comportements, tels que la gestion des valeurs null, vous pouvez configurer le même comportement sur le JsonSerializerOptions global ou individuellement pour chaque propriété.

Prenons l’exemple suivant qui utilise Newtonsoft.Json.JsonObjectAttribute pour indiquer que toutes les propriétés null doivent être ignorées :

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

Dans System.Text.Json, vous pouvez définir le comportement pour tous les types et propriétés :

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

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

Vous pouvez également définir le comportement séparément sur chaque propriété :

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

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

Considérez ensuite l’exemple suivant qui utilise Newtonsoft.Json.JsonObjectAttribute pour spécifier que toutes les propriétés de membre doivent se trouver dans le fichier JSON :

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

Vous pouvez obtenir le même comportement dans System.Text.Json en ajoutant le modificateur C# required ou le JsonRequiredAttributeà chaque propriété. Pour plus d’informations, consultez Propriétés requises.

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

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

TraceWriter

Newtonsoft.Json vous permet de déboguer à l’aide d’un TraceWriter pour afficher les journaux générés par la sérialisation ou la désérialisation. System.Text.Json ne fait pas de journalisation.

JsonDocument et JsonElement comparés à JToken (comme JObject, JArray)

System.Text.Json.JsonDocument permet d’analyser et de créer un modèle DOM (Document Object Model) en lecture seule à partir de charges utiles JSON existantes. Le DOM fournit un accès aléatoire aux données dans une charge utile JSON. Les éléments JSON qui composent la charge utile sont accessibles via le type JsonElement. Le type JsonElement fournit des API pour convertir du texte JSON en types .NET courants. JsonDocument expose une propriété RootElement.

À partir de .NET 6, vous pouvez analyser et générer un DOM mutable à partir de charges utiles JSON existantes à l’aide du type JsonNode et d’autres types dans l’espace de noms System.Text.Json.Nodes. Pour plus d’informations, voir Utiliser JsonNode.

JsonDocument est IDisposable

JsonDocument génère une vue en mémoire des données dans une mémoire tampon mise en pool. Par conséquent, contrairement à JObject ou JArray à partir de Newtonsoft.Json, le type JsonDocument implémente IDisposable et doit être utilisé à l’intérieur d’un bloc using. Pour plus d’informations, consultez JsonDocument est IDisposable.

JsonDocument est en lecture seule

Le DOM System.Text.Json ne peut pas ajouter, supprimer ou modifier des éléments JSON. Il est conçu de cette façon pour les performances et pour réduire les allocations pour l’analyse des tailles de charge utile JSON courantes (c’est-à-dire, < 1 Mo).

JsonElement est un struct d’union

JsonDocument expose le RootElement en tant que propriété de type JsonElement, qui est un type struct d’union qui englobe n’importe quel élément JSON. Newtonsoft.Json utilise des types hiérarchiques dédiés comme JObject, JArray, JToken, et ainsi de suite. JsonElement est ce que vous pouvez rechercher et énumérer, et vous pouvez utiliser JsonElement pour matérialiser des éléments JSON en types .NET.

À partir de .NET 6, vous pouvez utiliser le type JsonNode et les types dans l’espace de noms System.Text.Json.Nodes qui correspondent à JObject, JArray et JToken. Pour plus d’informations, voir Utiliser JsonNode.

Comment rechercher des sous-éléments dans JsonDocument et JsonElement

Les recherches de jetons JSON à l’aide de JObject ou JArray à partir de Newtonsoft.Json ont tendance à être relativement rapides, car il s’agit de recherches dans un dictionnaire. Par comparaison, les recherches sur JsonElement nécessitent une recherche séquentielle des propriétés et sont donc relativement lentes (par exemple lors de l’utilisation de TryGetProperty). System.Text.Json est conçu pour réduire le temps d’analyse initial plutôt que le temps de recherche. Pour plus d’informations, consultez Comment rechercher des sous-éléments dans JsonDocument et JsonElement.

Utf8JsonReader vs. JsonTextReader

System.Text.Json.Utf8JsonReader est un lecteur hautes performances et à faible allocation de type forward-only pour le texte JSON codé au format UTF-8 et lu à partir d’un ReadOnlySpan<byte> ou ReadOnlySequence<byte>. Utf8JsonReader est un type de bas niveau, permettant de générer des analyseurs et des désérialiseurs personnalisés.

Utf8JsonReader est un struct de référence

La JsonTextReader dans Newtonsoft.Json est une classe. Le type Utf8JsonReader diffère en cela qu’il s’agit d’un struct de référence. Pour plus d’informations, consultez Limitations ref struct pour Utf8JsonReader.

Lire des valeurs null dans des types de valeurs pouvant être null

Newtonsoft.Json fournit des API qui retournent Nullable<T>, comme ReadAsBoolean, qui gère un NullTokenType pour vous en retournant un bool?. Les API intégrées System.Text.Json retournent uniquement des types de valeurs non nullables. Pour plus d’informations, consultez Lire des valeurs null dans des types de valeurs nullables.

Multi-cible pour la lecture de JSON

Si vous devez continuer à utiliser Newtonsoft.Json pour certaines infrastructures cibles, vous pouvez utiliser plusieurs cibles et avoir deux implémentations. Toutefois, cela n’est pas anodin et nécessiterait des #ifdefs et une duplication de la source. Une façon de partager autant de code que possible consiste à créer un wrapper ref struct autour de Utf8JsonReader et Newtonsoft.Json.JsonTextReader. Ce wrapper unifierait la surface publique tout en isolant les différences de comportement. Cela vous permet d’isoler les modifications principalement apportées à la construction du type, ainsi que de transmettre le nouveau type par référence. Voici le modèle que la bibliothèque Microsoft.Extensions.DependencyModel suit :

Utf8JsonWriter vs. JsonTextWriter

System.Text.Json.Utf8JsonWriter fournit un moyen d’écrire du texte JSON encodé en UTF-8 à partir de types .NET courants, comme String, Int32, et DateTime. L’enregistreur est un type de bas niveau, permettant de générer des sérialiseurs personnalisés.

Écrire des valeurs brutes

Newtonsoft.Json a une méthode WriteRawValue qui écrit json brut où une valeur est attendue. System.Text.Json a un équivalent direct : Utf8JsonWriter.WriteRawValue. Pour plus d’informations, consultez Écriture de JSON brut.

Personnaliser le format JSON

JsonTextWriter inclut les paramètres suivants, pour lesquels Utf8JsonWriter n’a pas d’équivalent :

  • QuoteChar : spécifie le caractère à utiliser pour entourer les valeurs de chaîne. Utf8JsonWriter utilise toujours des guillemets doubles.
  • QuoteName : spécifie s’il faut ou non entourer les noms de propriétés de guillemets. Utf8JsonWriter les entoure toujours de guillemets.

À compter de .NET 9, vous pouvez personnaliser le caractère et la taille de retrait pour Utf8JsonWriter, en utilisation des options exposées par le struct JsonWriterOptions :

  • JsonWriterOptions.IndentCharacter
  • JsonWriterOptions.IndentSize

JsonTextWriter inclut les paramètres suivants, pour lesquels Utf8JsonWriter n’a pas d’équivalent :

  • Indentation : spécifie le nombre de caractères de la mise en retrait. Utf8JsonWriter met toujours en retrait de 2 caractères.
  • IndentChar : spécifie le caractère à utiliser pour la mise en retrait. Utf8JsonWriter utilise toujours des espaces blancs.
  • QuoteChar : spécifie le caractère à utiliser pour entourer les valeurs de chaîne. Utf8JsonWriter utilise toujours des guillemets doubles.
  • QuoteName : spécifie s’il faut ou non entourer les noms de propriétés de guillemets. Utf8JsonWriter les entoure toujours de guillemets.

Il n’existe aucune solution de contournement qui vous permettrait de personnaliser le JSON généré par Utf8JsonWriter de ces manières.

Écrire des valeurs d’intervalle de temps, d’URI ou de char

JsonTextWriter fournit des méthodes WriteValue pour les valeurs TimeSpan, Uri et char. Utf8JsonWriter n’a pas de méthodes équivalentes. Au lieu de cela, formatez ces valeurs en chaînes (en appelant ToString(), par exemple), puis appelez WriteStringValue.

Multi-cible pour l’écriture de JSON

Si vous devez continuer à utiliser Newtonsoft.Json pour certaines infrastructures cibles, vous pouvez utiliser plusieurs cibles et avoir deux implémentations. Toutefois, cela n’est pas anodin et nécessiterait des #ifdefs et une duplication de la source. Une façon de partager autant de code que possible consiste à créer un wrapper autour de Utf8JsonWriter et Newtonsoft.Json.JsonTextWriter. Ce wrapper unifierait la surface publique tout en isolant les différences de comportement. Cela vous permet d’isoler les modifications principalement apportées à la construction du type. La bibliothèque Microsoft.Extensions.DependencyModel suit :

TypeNameHandling.All non pris en charge

La décision d’exclure une fonctionnalité équivalente de TypeNameHandling.All dans System.Text.Json était intentionnelle. Autoriser une charge utile JSON à spécifier ses propres informations de type est une source courante de vulnérabilités dans les applications web. En particulier, la configuration de Newtonsoft.Json avec TypeNameHandling.All permet au client distant d’incorporer une application exécutable entière dans la charge utile JSON elle-même, de sorte que pendant la désérialisation, l’application web extrait et exécute le code incorporé. Pour plus d’informations, consultez le PowerPoint Attaques JSON du vendredi 13 et Détails des attaques JSON du vendredi 13.

Requêtes de chemin JSON non prises en charge

Le DOM JsonDocument ne prend pas en charge l’interrogation à l’aide du chemin JSON.

Dans un DOM JsonNode, chaque instance de JsonNode a une méthode GetPath qui retourne un chemin d’accès à ce nœud. Toutefois, il n’existe aucune API intégrée pour gérer les requêtes basées sur des chaînes de requête de chemin JSON.

Pour plus d’informations, consultez le problème GitHub dotnet/runtime #31068.

Certaines limites ne sont pas configurables

System.Text.Json définit des limites qui ne peuvent pas être modifiées pour certaines valeurs, comme la taille maximale du jeton en caractères (166 Mo) et en base 64 (125 Mo). Pour plus d’informations, consultez JsonConstants dans le code source et le problème GitHub dotnet/runtime #39953.

NaN, Infinity, -Infinity

Newtonsoft analyse les jetons de chaîne JSON NaN, Infinity et -Infinity. Avec System.Text.Json, utilisez JsonNumberHandling.AllowNamedFloatingPointLiterals. Pour plus d’informations sur l’utilisation de ce paramètre, consultez Autoriser ou écrire des nombres entre guillemets.

Ressources complémentaires