Condividi tramite


Eseguire la migrazione da Newtonsoft.Json a System.Text.Json

Questo articolo illustra come eseguire la migrazione da Newtonsoft.Json a System.Text.Json.

Lo spazio dei nomi System.Text.Json fornisce funzionalità per la serializzazione e la deserializzazione da JavaScript Object Notation (JSON). La libreria System.Text.Json è inclusa nel runtime per .NET Core 3.1 e versioni successive. Per altri framework di destinazione, installare il pacchetto NuGet System.Text.Json. Il pacchetto supporta:

  • .NET Standard 2.0 e versioni successive
  • .NET Framework 4.6.2 e versioni successive
  • .NET Core 2.0, 2.1 e 2.2

Suggerimento

È possibile usare l'assistenza IA per eseguire la migrazione da Newtonsoft.Json con GitHub Copilot.

System.Text.Json si concentra principalmente su prestazioni, sicurezza e conformità agli standard. Presenta alcune differenze principali nel comportamento predefinito e non mira ad avere parità di funzionalità con Newtonsoft.Json. Per alcuni scenari, System.Text.Json attualmente non dispone di funzionalità predefinite, ma esistono soluzioni alternative consigliate. Per altri scenari, le soluzioni alternative non sono pratiche.

Il team System.Text.Json sta investendo nell'aggiunta delle funzionalità più richieste. Se l'applicazione dipende da una funzionalità mancante, valutare la possibilità di segnalare un problema nel repository GitHub dotnet/runtime per scoprire se è possibile ricevere supporto per lo scenario in questione.

La maggior parte di questo articolo descrive come usare l'API JsonSerializer, ma include anche indicazioni su come usare i tipi JsonDocument (che rappresenta il Document Object Model o DOM), Utf8JsonReader e Utf8JsonWriter.

In Visual Basic non è possibile usare Utf8JsonReader, il che significa anche che non è possibile scrivere convertitori personalizzati. La maggior parte delle soluzioni alternative qui presentate richiede la scrittura di convertitori personalizzati. È possibile scrivere un convertitore personalizzato in C# e registrarlo in un progetto Visual Basic. Per altre informazioni, vedere Supporto di Visual Basic.

Tabella delle differenze

Nella tabella seguente sono elencate le caratteristiche Newtonsoft.Json e gli equivalenti System.Text.Json. Gli equivalenti rientrano nelle seguenti categorie:

  • ✔️ Supportato dalla funzionalità predefinita. Ottenere un comportamento simile da System.Text.Json potrebbe richiedere l'uso di un attributo o di un'opzione globale.
  • ⚠️ Non supportato, ma è possibile una soluzione alternativa. Le soluzioni alternative sono i convertitori personalizzati che potrebbero non fornire la parità completa con la funzionalità Newtonsoft.Json. Per alcuni di questi, viene fornito come esempio il codice di esempio. Se si fa affidamento su queste funzionalità Newtonsoft.Json, la migrazione richiederà modifiche ai modelli a oggetti .NET o altre modifiche al codice.
  • ❌ Non supportato e la soluzione alternativa non è pratica né possibile. Se si fa affidamento su queste funzionalità Newtonsoft.Json, la migrazione non sarà possibile senza modifiche significative.
Funzionalità di Newtonsoft.Json System.Text.Json equivalent
Deserializzazione senza distinzione tra maiuscole e minuscole per impostazione predefinita ✔️ Impostazione globale PropertyNameCaseInsensitive
Nomi delle proprietà camel-case ✔️ Impostazione globale PropertyNamingPolicy
Nomi delle proprietà snake-case ✔️ Criteri di denominazione snake case
Escape di caratteri minimo ✔️ Escape dei caratteri rigidi, configurabile
Impostazione globale NullValueHandling.Ignore ✔️ Opzione globale DefaultIgnoreCondition
Consentire commenti ✔️ Impostazione globale ReadCommentHandling
Consentire virgole finali ✔️ Impostazione globale AllowTrailingCommas
Registrazione del convertitore personalizzato ✔️ L'ordine di precedenza è diverso
Profondità massima predefinita 64, configurabile ✔️ Profondità massima predefinita 64, configurabile
Impostazione globale PreserveReferencesHandling ✔️ Impostazione globale ReferenceHandling
Serializzare o deserializzare numeri tra virgolette ✔️ Impostazione globale NumberHandling, attributo [JsonNumberHandling]
Deserializzare in classi e struct non modificabili ✔️ JsonConstructor, record C# 9
Supporto per i campi ✔️ Impostazione globale IncludeFields, attributo [JsonInclude]
Impostazione globale DefaultValueHandling ✔️ Opzione globale DefaultIgnoreCondition
Impostazione NullValueHandling su [JsonProperty] ✔️ Attributo JsonIgnore
Impostazione DefaultValueHandling su [JsonProperty] ✔️ Attributo JsonIgnore
Deserializzare Dictionary con chiave non stringa ✔️ Supportati
Supporto per setter e getter di proprietà non pubblici ✔️ Attributo JsonInclude
Attributo [JsonConstructor] ✔️ Attributo [JsonConstructor]
Impostazione globale ReferenceLoopHandling ✔️ Impostazione globale ReferenceHandling
Callback ✔️ Callback
NaN, Infinity, -Infinity ✔️ Supportati
Impostazione Required sull'attributo [JsonProperty] ✔️ Attributo [JsonRequired] e modificatore richiesto C#
DefaultContractResolver per ignorare le proprietà ✔️ Classe DefaultJsonTypeInfoResolver
Serializzazione polimorfica ✔️ Attributo [JsonDerivedType]
Deserializzazione polimorfica ✔️ Discriminante del tipo sull'attributo [JsonDerivedType]
Deserializzare il valore di enumerazione stringa ✔️ Deserializzare i valori di enumerazione stringa
Impostazione globale MissingMemberHandling ✔️ Gestire i membri mancanti
Popolare le proprietà senza setter ✔️ Popolare le proprietà senza setter
Impostazione globale ObjectCreationHandling ✔️ Riutilizzare anziché sostituire le proprietà
Supporto per un'ampia gamma di tipi ⚠️ Alcuni tipi richiedono convertitori personalizzati
Deserializzare il tipo dedotto alle proprietà object ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare un valore letterale JSON null in tipi valore che non ammette i valori Null ⚠️ Non supportato, soluzione alternativa, esempio
DateTimeZoneHandling, impostazioni DateFormatString ⚠️ Non supportato, soluzione alternativa, esempio
Metodo JsonConvert.PopulateObject ⚠️ Non supportato, soluzione alternativa
Supporto per gli attributi System.Runtime.Serialization ⚠️ Non supportato, soluzione alternativa, esempio
JsonObjectAttribute ⚠️ Non supportato, soluzione alternativa
Consentire nomi di proprietà senza virgolette Non supportato dalla progettazione
Consentire virgolette singole intorno ai valori stringa Non supportato dalla progettazione
Consentire valori JSON non stringa per le proprietà stringa Non supportato dalla progettazione
Impostazione globale TypeNameHandling.All Non supportato dalla progettazione
Supporto per le query JsonPath Non supportato
Limiti configurabili Non supportato
Funzionalità di Newtonsoft.Json System.Text.Json equivalent
Deserializzazione senza distinzione tra maiuscole e minuscole per impostazione predefinita ✔️ Impostazione globale PropertyNameCaseInsensitive
Nomi delle proprietà camel-case ✔️ Impostazione globale PropertyNamingPolicy
Escape di caratteri minimo ✔️ Escape dei caratteri rigidi, configurabile
Impostazione globale NullValueHandling.Ignore ✔️ Opzione globale DefaultIgnoreCondition
Consentire commenti ✔️ Impostazione globale ReadCommentHandling
Consentire virgole finali ✔️ Impostazione globale AllowTrailingCommas
Registrazione del convertitore personalizzato ✔️ L'ordine di precedenza è diverso
Profondità massima predefinita 64, configurabile ✔️ Profondità massima predefinita 64, configurabile
Impostazione globale PreserveReferencesHandling ✔️ Impostazione globale ReferenceHandling
Serializzare o deserializzare numeri tra virgolette ✔️ Impostazione globale NumberHandling, attributo [JsonNumberHandling]
Deserializzare in classi e struct non modificabili ✔️ JsonConstructor, record C# 9
Supporto per i campi ✔️ Impostazione globale IncludeFields, attributo [JsonInclude]
Impostazione globale DefaultValueHandling ✔️ Opzione globale DefaultIgnoreCondition
Impostazione NullValueHandling su [JsonProperty] ✔️ Attributo JsonIgnore
Impostazione DefaultValueHandling su [JsonProperty] ✔️ Attributo JsonIgnore
Deserializzare Dictionary con chiave non stringa ✔️ Supportati
Supporto per setter e getter di proprietà non pubblici ✔️ Attributo JsonInclude
Attributo [JsonConstructor] ✔️ Attributo [JsonConstructor]
Impostazione globale ReferenceLoopHandling ✔️ Impostazione globale ReferenceHandling
Callback ✔️ Callback
NaN, Infinity, -Infinity ✔️ Supportati
Impostazione Required sull'attributo [JsonProperty] ✔️ Attributo [JsonRequired] e modificatore richiesto C#
DefaultContractResolver per ignorare le proprietà ✔️ Classe DefaultJsonTypeInfoResolver
Serializzazione polimorfica ✔️ Attributo [JsonDerivedType]
Deserializzazione polimorfica ✔️ Discriminante del tipo sull'attributo [JsonDerivedType]
Deserializzare il valore di enumerazione stringa ✔️ Deserializzare i valori di enumerazione stringa
Supporto per un'ampia gamma di tipi ⚠️ Alcuni tipi richiedono convertitori personalizzati
Deserializzare il tipo dedotto alle proprietà object ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare un valore letterale JSON null in tipi valore che non ammette i valori Null ⚠️ Non supportato, soluzione alternativa, esempio
DateTimeZoneHandling, impostazioni DateFormatString ⚠️ Non supportato, soluzione alternativa, esempio
Metodo JsonConvert.PopulateObject ⚠️ Non supportato, soluzione alternativa
Impostazione globale ObjectCreationHandling ⚠️ Non supportato, soluzione alternativa
Aggiungere a raccolte senza setter ⚠️ Non supportato, soluzione alternativa
Nomi delle proprietà snake-case ⚠️ Non supportato, soluzione alternativa
Supporto per gli attributi System.Runtime.Serialization ⚠️ Non supportato, soluzione alternativa, esempio
Impostazione globale MissingMemberHandling ⚠️ Non supportato, soluzione alternativa, esempio
JsonObjectAttribute ⚠️ Non supportato, soluzione alternativa
Consentire nomi di proprietà senza virgolette Non supportato dalla progettazione
Consentire virgolette singole intorno ai valori stringa Non supportato dalla progettazione
Consentire valori JSON non stringa per le proprietà stringa Non supportato dalla progettazione
Impostazione globale TypeNameHandling.All Non supportato dalla progettazione
Supporto per le query JsonPath Non supportato
Limiti configurabili Non supportato
Funzionalità di Newtonsoft.Json System.Text.Json equivalent
Deserializzazione senza distinzione tra maiuscole e minuscole per impostazione predefinita ✔️ Impostazione globale PropertyNameCaseInsensitive
Nomi delle proprietà camel-case ✔️ Impostazione globale PropertyNamingPolicy
Escape di caratteri minimo ✔️ Escape dei caratteri rigidi, configurabile
Impostazione globale NullValueHandling.Ignore ✔️ Opzione globale DefaultIgnoreCondition
Consentire commenti ✔️ Impostazione globale ReadCommentHandling
Consentire virgole finali ✔️ Impostazione globale AllowTrailingCommas
Registrazione del convertitore personalizzato ✔️ L'ordine di precedenza è diverso
Profondità massima predefinita 64, configurabile ✔️ Profondità massima predefinita 64, configurabile
Impostazione globale PreserveReferencesHandling ✔️ Impostazione globale ReferenceHandling
Serializzare o deserializzare numeri tra virgolette ✔️ Impostazione globale NumberHandling, attributo [JsonNumberHandling]
Deserializzare in classi e struct non modificabili ✔️ JsonConstructor, record C# 9
Supporto per i campi ✔️ Impostazione globale IncludeFields, attributo [JsonInclude]
Impostazione globale DefaultValueHandling ✔️ Opzione globale DefaultIgnoreCondition
Impostazione NullValueHandling su [JsonProperty] ✔️ Attributo JsonIgnore
Impostazione DefaultValueHandling su [JsonProperty] ✔️ Attributo JsonIgnore
Deserializzare Dictionary con chiave non stringa ✔️ Supportati
Supporto per setter e getter di proprietà non pubblici ✔️ Attributo JsonInclude
Attributo [JsonConstructor] ✔️ Attributo [JsonConstructor]
Impostazione globale ReferenceLoopHandling ✔️ Impostazione globale ReferenceHandling
Callback ✔️ Callback
NaN, Infinity, -Infinity ✔️ Supportati
Deserializzare il valore di enumerazione stringa ✔️ Deserializzare i valori di enumerazione stringa
Supporto per un'ampia gamma di tipi ⚠️ Alcuni tipi richiedono convertitori personalizzati
Serializzazione polimorfica ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzazione polimorfica ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare il tipo dedotto alle proprietà object ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare un valore letterale JSON null in tipi valore che non ammette i valori Null ⚠️ Non supportato, soluzione alternativa, esempio
Impostazione Required sull'attributo [JsonProperty] ⚠️ Non supportato, soluzione alternativa, esempio
DefaultContractResolver per ignorare le proprietà ⚠️ Non supportato, soluzione alternativa, esempio
DateTimeZoneHandling, impostazioni DateFormatString ⚠️ Non supportato, soluzione alternativa, esempio
Metodo JsonConvert.PopulateObject ⚠️ Non supportato, soluzione alternativa
Impostazione globale ObjectCreationHandling ⚠️ Non supportato, soluzione alternativa
Aggiungere a raccolte senza setter ⚠️ Non supportato, soluzione alternativa
Nomi delle proprietà snake-case ⚠️ Non supportato, soluzione alternativa
JsonObjectAttribute ⚠️ Non supportato, soluzione alternativa
Supporto per gli attributi System.Runtime.Serialization Non supportato
Impostazione globale MissingMemberHandling Non supportato
Consentire nomi di proprietà senza virgolette Non supportato dalla progettazione
Consentire virgolette singole intorno ai valori stringa Non supportato dalla progettazione
Consentire valori JSON non stringa per le proprietà stringa Non supportato dalla progettazione
Impostazione globale TypeNameHandling.All Non supportato dalla progettazione
Supporto per le query JsonPath Non supportato
Limiti configurabili Non supportato

Questo non è un elenco completo delle funzionalità Newtonsoft.Json. L'elenco include molti degli scenari richiesti nei problemi di GitHub o nei post di StackOverflow. Se si implementa una soluzione alternativa per uno degli scenari qui elencati che non dispone attualmente di codice di esempio e se si vuole condividere la soluzione, selezionare Questa pagina nella sezione Feedback nella parte inferiore di questa pagina. Ciò crea un problema nel repository GitHub di questa documentazione e lo elenca anche nella sezione Feedback in questa pagina.

Differenze nel comportamento predefinito

System.Text.Json è rigoroso per impostazione predefinita ed evita qualsiasi ipotesi o interpretazione per conto del chiamante, enfatizzando il comportamento deterministico. La libreria è progettata intenzionalmente in questo modo per garantire prestazioni e sicurezza. Newtonsoft.Json è flessibile per impostazione predefinita. Questa differenza fondamentale nella progettazione è alla base di molte delle seguenti differenze specifiche nel comportamento predefinito.

Deserializzazione senza distinzione tra maiuscole e minuscole

Durante la deserializzazione, Newtonsoft.Json esegue la corrispondenza del nome della proprietà senza distinzione tra maiuscole e minuscole per impostazione predefinita. L'impostazione predefinita System.Text.Json fa distinzione tra maiuscole e minuscole, il che offre prestazioni migliori perché esegue una corrispondenza esatta. Per ricevere informazioni su come eseguire la corrispondenza senza distinzione tra maiuscole e minuscole, vedere Corrispondenza di proprietà senza distinzione tra maiuscole e minuscole.

Se si usa indirettamente System.Text.Json usando ASP.NET Core, non è necessario eseguire alcuna operazione per ottenere un comportamento come Newtonsoft.Json. ASP.NET Core specifica le impostazioni per i nomi delle proprietà camel case e la corrispondenza senza distinzione tra maiuscole e minuscole quando usa System.Text.Json.

ASP.NET Core abilita inoltre la deserializzazione dei numeri tra virgolette per impostazione predefinita.

Escape di caratteri minimo

Durante la serializzazione, Newtonsoft.Json è relativamente permissivo nel lasciar passare i caratteri senza eliminarli. Ciò significa che non li sostituisce con \uxxxx dove xxxx è il punto di codice del carattere. Nei casi in cui esegue l'escape, lo fa emettendo un \ prima del carattere (ad esempio, " diventa \"). System.Text.Json esegue l'escape di più caratteri per impostazione predefinita per fornire protezioni approfondite contro scripting intersito (XSS) o attacchi di divulgazione di informazioni e lo fa usando la sequenza di sei caratteri. System.Text.Json esegue l'escape di tutti i caratteri non ASCII per impostazione predefinita, pertanto non è necessario eseguire alcuna operazione se si usa StringEscapeHandling.EscapeNonAscii in Newtonsoft.Json. System.Text.Json esegue anche l'escape dei caratteri con distinzione HTML, per impostazione predefinita. Per ricevere ulteriori informazioni su come eseguire l'override del comportamento predefinito System.Text.Json, consultare l'articolo Come personalizzare la codifica dei caratteri.

Commenti

Durante la deserializzazione, Newtonsoft.Json ignora i commenti nel codice JSON per impostazione predefinita. L'impostazione predefinita System.Text.Json prevede la generazione di eccezioni per i commenti perché la specifica RFC 8259 non li include. Per ricevere ulteriori informazioni su come consentire i commenti, vedere Consenti commenti e virgole finali.

Virgole finali

Durante la deserializzazione, Newtonsoft.Json ignora le virgole finali per impostazione predefinita. Ignora anche più virgole finali, ad esempio [{"Color":"Red"},{"Color":"Green"},,]. L'impostazione predefinita System.Text.Json prevede la generazione di eccezioni per le virgole finali perché la specifica RFC 8259 non li include. Per ricevere ulteriori informazioni su come consentire a System.Text.Json di accettarli, vedere Consenti commenti e virgole finali. Non è possibile consentire più virgole finali.

Precedenza nella registrazione dei convertitori

La precedenza di registrazione Newtonsoft.Json per i convertitori personalizzati è la seguente:

  • Attributo sulla proprietà
  • Attributo sul tipo
  • Raccolta Convertitori

Questo ordine indica che un convertitore personalizzato nella raccolta Converters viene sottoposto a override da un convertitore registrato applicando un attributo a livello di tipo. Entrambe le registrazioni vengono sottoposte a override da un attributo a livello di proprietà.

La precedenza di registrazione System.Text.Json per i convertitori personalizzati è la seguente:

  • Attributo sulla proprietà
  • Raccolta Converters
  • Attributo sul tipo

La differenza è che un convertitore personalizzato nella raccolta Converters esegue l'override di un attributo a livello di tipo. L'intenzione alla base di questo ordine di precedenza è fare in modo che le modifiche in fase di esecuzione eseguano l'override delle scelte in fase di progettazione. Non è possibile modificare la precedenza.

Per ottenere ulteriori informazioni sulla registrazione del convertitore personalizzato, vedere Registrare un convertitore personalizzato.

Profondità massima

La versione più recente di ha un limite massimo di Newtonsoft.Json profondità di 64 per impostazione predefinita. System.Text.Json ha anche un limite predefinito di 64 ed è configurabile impostando JsonSerializerOptions.MaxDepth.

Se si usa indirettamente System.Text.Json usando ASP.NET Core, il limite di profondità massimo predefinito è 32. Il valore predefinito è lo stesso dell'associazione di modelli ed è impostato nella classe JsonOptions.

Stringhe JSON (nomi di proprietà e valori stringa)

Durante la deserializzazione, Newtonsoft.Json accetta nomi di proprietà racchiusi tra virgolette doppie, virgolette singole o senza virgolette. Accetta valori stringa racchiusi tra virgolette doppie o virgolette singole. Ad esempio, Newtonsoft.Json accetta il codice JSON seguente:

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

System.Text.Json accetta solo nomi di proprietà e valori stringa tra virgolette doppie perché tale formato è richiesto dalla specifica RFC 8259 ed è l'unico formato considerato JSON valido.

Un valore racchiuso tra virgolette singole restituisce un'eccezione JsonException con il messaggio seguente:

''' is an invalid start of a value.

Valori non stringa per le proprietà stringa

Newtonsoft.Json accetta valori non stringa, ad esempio un numero o i valori letterali true e false, per la deserializzazione alla proprietà di tipo stringa. Ecco un esempio di JSON che Newtonsoft.Json deserializza correttamente alla classe seguente:

{
  "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 non deserializza i valori non stringa in proprietà stringa. Un valore non stringa ricevuto per un campo stringa restituisce un'eccezione JsonException con il messaggio seguente:

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

Scenari con JsonSerializer

Alcuni degli scenari seguenti non sono supportati dalla funzionalità predefinita, ma sono possibili soluzioni alternative. Le soluzioni alternative sono convertitori personalizzati, che potrebbero non fornire la parità completa con le funzionalità Newtonsoft.Json. Per alcuni di questi, viene fornito come esempio il codice di esempio. Se si fa affidamento su queste funzionalità Newtonsoft.Json, la migrazione richiederà modifiche ai modelli a oggetti .NET o altre modifiche al codice.

Per alcuni degli scenari seguenti, le soluzioni alternative non sono pratiche né possibili. Se si fa affidamento su queste funzionalità Newtonsoft.Json, la migrazione non sarà possibile senza modifiche significative.

Consentire o scrivere numeri tra virgolette

Newtonsoft.Json può serializzare o deserializzare i numeri rappresentati da stringhe JSON (racchiuse tra virgolette). Ad esempio, può accettare {"DegreesCelsius":"23"} anziché {"DegreesCelsius":23}. Per abilitare questo comportamento in System.Text.Json, impostare JsonSerializerOptions.NumberHandling su WriteAsString o AllowReadingFromString oppure usare l'attributo [JsonNumberHandling].

Se si usa indirettamente System.Text.Json usando ASP.NET Core, non è necessario eseguire alcuna operazione per ottenere un comportamento come Newtonsoft.Json. ASP.NET Core specifica le impostazioni predefinite Web quando usa System.Text.Json e le impostazioni predefinite Web consentono numeri tra virgolette.

Per ottenere ulteriori informazioni, vedere Consentire o scrivere numeri tra virgolette.

Specificare il costruttore da usare durante la deserializzazione

L'attributo Newtonsoft.Json[JsonConstructor] consente di specificare il costruttore da chiamare durante la deserializzazione a Plain Old CLR Object.

System.Text.Json ha anche un attributo [JsonConstructor]. Per ottenere ulteriori informazioni, consultare l'articolo Usare tipi e proprietà non modificabili.

Ignorare in modo condizionale le proprietà

Newtonsoft.Json offre diversi modi per ignorare in modo condizionale una proprietà sulla serializzazione o la deserializzazione:

  • DefaultContractResolver consente di selezionare le proprietà da includere o ignorare in base a criteri arbitrari.
  • Le impostazioni NullValueHandling e DefaultValueHandling su JsonSerializerSettings consentono di specificare che tutte le proprietà con valore Null o con valore predefinito devono essere ignorate.
  • Le impostazioni NullValueHandling e DefaultValueHandling sull'attributo [JsonProperty] consentono di specificare le singole proprietà che devono essere ignorate se impostate su Null o sul valore predefinito.

System.Text.Json fornisce le modalità seguenti per ignorare le proprietà o i campi durante la serializzazione:

Inoltre, in .NET 7 e versioni successive, è possibile personalizzare il contratto JSON per ignorare le proprietà in base a criteri arbitrari. Per ottenere ulteriori informazioni, vedere Contratti personalizzati.

Queste opzioni non consentono di ignorare le proprietà selezionate in base a criteri arbitrari valutati in fase di esecuzione.

Campi pubblici e non pubblici

Newtonsoft.Json può serializzare e deserializzare i campi e le proprietà.

In System.Text.Json, usare l'impostazione globale JsonSerializerOptions.IncludeFields o l'attributo [JsonInclude] per includere campi pubblici durante la serializzazione o la deserializzazione. Per visualizzare alcuni esempi, consultare l'articolo Includere campi.

Mantenere i riferimenti agli oggetti e i cicli di handle

Per impostazione predefinita, Newtonsoft.Json serializza gli oggetti per valore. Ad esempio, se un oggetto contiene due proprietà che contengono un riferimento allo stesso oggettoPerson, i valori delle proprietà dell'oggetto Person vengono duplicati nel codice JSON.

Newtonsoft.Json ha un'impostazione PreserveReferencesHandling su JsonSerializerSettings che consente di serializzare in base al riferimento:

  • I metadati dell'identificatore vengono aggiunti al codice JSON creato per il primo oggetto Person.
  • Il codice JSON creato per il secondo oggetto Person contiene un riferimento a tale identificatore anziché ai valori delle proprietà.

Newtonsoft.Json include anche un'impostazione ReferenceLoopHandling che consente di ignorare i riferimenti circolari anziché generare un'eccezione.

Per mantenere i riferimenti e gestire riferimenti circolari in System.Text.Json, impostare JsonSerializerOptions.ReferenceHandler su Preserve. L'impostazione ReferenceHandler.Preserve equivale a PreserveReferencesHandling = PreserveReferencesHandling.All in Newtonsoft.Json.

L'opzione ReferenceHandler.IgnoreCycles ha un comportamento simile a Newtonsoft.JsonReferenceLoopHandling.Ignore. Una differenza è che l'implementazione System.Text.Json sostituisce i cicli di riferimento con il token JSON null anziché ignorare il riferimento all'oggetto. Per altre informazioni, vedere Ignorare i riferimenti circolari.

Analogamente a Newtonsoft.JsonReferenceResolver, la classe System.Text.Json.Serialization.ReferenceResolver definisce il comportamento che prevede il mantenimento dei riferimenti alla serializzazione e alla deserializzazione. Creare una classe derivata per specificare il comportamento personalizzato. Per un esempio, vedere GuidReferenceResolver.

Alcune funzionalità correlate Newtonsoft.Json non sono supportate:

Per ottenere ulteriori informazioni, vedere Mantenere i riferimenti e gestire riferimenti circolari.

Dizionario con chiave non stringa

Sia Newtonsoft.Json che System.Text.Json supportano raccolte di tipo Dictionary<TKey, TValue>. Tuttavia in System.Text.Json, TKey deve essere un tipo primitivo, non un tipo personalizzato. Per ottenere ulteriori informazioni, consultare la sezione Tipi di chiave supportati.

Attenzione

Deserializzazione in un oggetto Dictionary<TKey, TValue> in cui TKey viene tipizzato come qualsiasi elemento diverso da string, potrebbe introdurre una vulnerabilità di sicurezza nell'applicazione che la utilizza. Per ottenere ulteriori informazioni, vedere dotnet/runtime#4761.

Tipi senza supporto predefinito

System.Text.Json non fornisce il supporto predefinito per i tipi seguenti:

I convertitori personalizzati possono essere implementati per i tipi che non dispongono del supporto predefinito.

Serializzazione polimorfica

Newtonsoft.Json esegue automaticamente la serializzazione polimorfica. A partire da .NET 7, System.Text.Json supporta la serializzazione polimorfica tramite l'attributo JsonDerivedTypeAttribute. Per altre informazioni, vedere Serializzare le proprietà delle classi derivate.

Deserializzazione polimorfica

Newtonsoft.Json ha un'impostazione TypeNameHandling che aggiunge metadati del nome di tipo al codice JSON durante la serializzazione. Usa i metadati durante la deserializzazione per eseguire la deserializzazione polimorfica. A partire da .NET 7, System.Text.Json si basa su informazioni del discriminatore del tipo per eseguire la deserializzazione polimorfica. Questi metadati vengono generati nel codice JSON e quindi usati durante la deserializzazione per determinare se deserializzare nel tipo di base o in un tipo derivato. Per altre informazioni, vedere Serializzare le proprietà delle classi derivate.

Per supportare la deserializzazione polimorfica nelle versioni precedenti di .NET, creare un convertitore come l'esempio illustrato in Come scrivere convertitori personalizzati.

Deserializzare i valori di enumerazione stringa

Per impostazione predefinita, System.Text.Json non supporta la deserializzazione dei valori di enumerazione delle stringhe, al contrario di Newtonsoft.Json. Ad esempio, il codice seguente genera 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
}

Tuttavia, è possibile abilitare la deserializzazione dei valori di enumerazione stringa usando il convertitore JsonStringEnumConverter. Per ottenere ulteriori informazioni, vedi Enumerazioni come stringhe.

Deserializzazione delle proprietà dell'oggetto

Quando Newtonsoft.Json deserializza in Object, esegue le seguenti operazioni:

  • Deduce il tipo di valori primitivi nel payload JSON (diverso da null) e restituisce l'oggetto archiviato string, long, double, boolean o DateTime come oggetto boxed. I valori primitivi sono valori JSON singoli, ad esempio un numero JSON, una stringa, true, false, o null.
  • Restituisce un oggetto JObject o JArray per i valori complessi nel payload JSON. I valori complessi sono raccolte di coppie chiave-valore JSON tra parentesi graffe ({}) o elenchi di valori racchiusi tra parentesi quadre ([]). Le proprietà e i valori all'interno delle parentesi graffe o quadre possono avere proprietà o valori aggiuntivi.
  • Restituisce un riferimento Null quando il payload ha il valore letterale JSON null.

System.Text.Json archivia un oggetto boxed JsonElement per i valori primitivi e complessi ogni volta che viene deserializzato in Object, ad esempio:

  • Proprietà object.
  • Un valore del dizionario object.
  • Un valore della matrice object.
  • Una radice object.

Tuttavia, System.Text.Json tratta null allo stesso modo di Newtonsoft.Json e restituisce un riferimento Null quando il payload contiene il valore letterale JSON null.

Per implementare l'inferenza dei tipi per le proprietà object, creare un convertitore come illustrato nell'esempio in Come scrivere convertitori personalizzati.

Deserializzare Null in un tipo che non ammette i valori Null

Nello scenario seguente,Newtonsoft.Json non genera un'eccezione:

  • NullValueHandling è impostato su Ignore e
  • Durante la deserializzazione, il codice JSON contiene un valore Null per un tipo di valore che non ammette i valori Null.

Nello stesso scenario, System.Text.Json genera un'eccezione. L'impostazione di gestione dei valori Null corrispondente in System.Text.Json è JsonSerializerOptions.IgnoreNullValues = true.

Se si è proprietari del tipo di destinazione, la soluzione alternativa migliore consiste nel far sì che la proprietà in questione ammetta i valori Null (ad esempio, modificare int in int?).

Un'altra soluzione alternativa consiste nel creare un convertitore per il tipo. Nell'esempio seguente vengono gestisti i valori Null per i tipi 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);
    }
}

Registrare questo convertitore personalizzato usando un attributo nella proprietà o aggiungendo il convertitore alla raccolta Converters.

Nota: il convertitore precedente gestisce i valori Null in modo diverso rispetto a Newtonsoft.Json quello per i POCO che specificano i valori predefiniti. Si supponga, ad esempio, che il codice seguente rappresenti l'oggetto di destinazione:

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

Si supponga che il codice JSON seguente venga deserializzato usando il convertitore precedente:

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

Dopo la deserializzazione, la proprietà Date ha 1/1/0001 (default(DateTimeOffset)), ciò significa che il valore impostato nel costruttore viene sovrascritto. Con gli stessi POCO e JSON, la deserializzazione Newtonsoft.Json lascerebbe 1/1/2001 nella proprietà Date.

Deserializzare in classi e struct non modificabili

Newtonsoft.Json può deserializzare in classi e struct non modificabili perché può usare costruttori con parametri.

In System.Text.Json, usare l'attributo [JsonConstructor] per specificare l'uso di un costruttore con parametri. Anche i record in C# 9 non sono modificabili e sono supportati come destinazioni di deserializzazione. Per ottenere ulteriori informazioni, consultare l'articolo Usare tipi e proprietà non modificabili.

Proprietà obbligatorie

In Newtonsoft.Json, specificare che una proprietà è obbligatoria impostando Required sull'attributo [JsonProperty]. Newtonsoft.Json genera un'eccezione se non viene ricevuto alcun valore nel codice JSON per una proprietà contrassegnata come obbligatoria.

A partire da .NET 7, è possibile usare il modificatore C# required o l'attributo JsonRequiredAttribute in una proprietà obbligatoria. System.Text.Json genera un'eccezione se il payload JSON non contiene un valore per la proprietà contrassegnata. Per altre informazioni, vedere Proprietà obbligatorie.

System.Text.Json non genera un'eccezione se non viene ricevuto alcun valore per una delle proprietà del tipo di destinazione. Ad esempio, se si dispone di una classe WeatherForecast:

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

Il codice JSON seguente viene deserializzato senza errori:

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

Per fare in modo che la deserializzazione abbia esito negativo se non è presente alcuna proprietà Date nel codice JSON, scegliere una delle opzioni seguenti:

Il codice del convertitore di esempio seguente genera un'eccezione se la proprietà Date non è impostata dopo il completamento della deserializzazione:

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

Registrare questo convertitore personalizzato aggiungendo il convertitore alla raccolta JsonSerializerOptions.Converters.

Questo modello di chiamata ricorsiva al convertitore richiede di registrare il convertitore usando JsonSerializerOptions, non usando un attributo. Se si registra il convertitore usando un attributo, il convertitore personalizzato richiama sé stesso in modo ricorsivo. Il risultato è un ciclo infinito che termina con un'eccezione di overflow dello stack.

Quando si registra il convertitore usando l'oggetto opzioni, evitare un ciclo infinito non passando l'oggetto opzioni quando si chiama in modo ricorsivo Serialize o Deserialize. L'oggetto opzioni contiene la raccolta Converters. Se lo si passa a Serialize o Deserialize, il convertitore personalizzato chiama sé stesso, creando un ciclo infinito che genera un'eccezione di overflow dello stack. Se le opzioni predefinite non sono realizzabili, creare una nuova istanza delle opzioni con le impostazioni necessarie. Questo approccio sarà lento perché ogni nuova istanza memorizza nella cache in modo indipendente.

Esiste un modello alternativo che può usare la registrazione JsonConverterAttribute nella classe da convertire. In questo approccio il codice del convertitore chiama Serialize o Deserialize su una classe che deriva dalla classe da convertire. Alla classe derivata non è applicato un oggetto JsonConverterAttribute. Nell'esempio seguente di questa alternativa, è possibile constatare che:

  • WeatherForecastWithRequiredPropertyConverterAttribute è la classe da deserializzare e a cui è applicato JsonConverterAttribute.
  • WeatherForecastWithoutRequiredPropertyConverterAttribute è la classe derivata che non ha l'attributo del convertitore.
  • Il codice nel convertitore chiama Serialize e Deserialize su WeatherForecastWithoutRequiredPropertyConverterAttribute per evitare un ciclo infinito. Questo approccio comporta un costo delle prestazioni per la serializzazione a causa di un'istanza aggiuntiva dell'oggetto e della copia dei valori delle proprietà.

Ecco i tipi di 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
{
}

Ed ecco il convertitore:

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

Il convertitore di proprietà obbligatorio richiede una logica aggiuntiva se è necessario gestire attributi come [JsonIgnore] o opzioni diverse, come i codificatori personalizzati. Inoltre, il codice di esempio non gestisce le proprietà per le quali viene impostato un valore predefinito nel costruttore. E questo approccio non distingue tra gli scenari seguenti:

  • Una proprietà non è presente nel codice JSON.
  • Una proprietà per un tipo che non ammette valori Null è presente in JSON, ma il valore è l'impostazione predefinita per il tipo, ad esempio zero per un oggetto int.
  • Una proprietà per un tipo di valore che ammette i valori Null è presente nel codice JSON, ma il valore è Null.

Nota

Se si usa System.Text.Json da un controller ASP.NET Core, potrebbe essere possibile usare un attributo [Required] sulle proprietà della classe del modello anziché implementare un convertitore System.Text.Json.

Specificare il formato della data

Newtonsoft.Json offre diversi modi per controllare il modo in cui le proprietà dei tipi DateTime e DateTimeOffset vengono serializzate e deserializzate:

  • L'impostazione DateTimeZoneHandling può essere usata per serializzare tutti i valori DateTime come date UTC.
  • L'impostazione DateFormatString e i convertitori DateTime possono essere usati per personalizzare il formato delle stringhe di data.

System.Text.Json supporta ISO 8601-1:2019, incluso il profilo RFC 3339. Questo formato è ampiamente adottato, non ambiguo e consente di effettuare round trip in modo preciso. Per usare qualsiasi altro formato, creare un convertitore personalizzato. Ad esempio, i convertitori seguenti serializzano e deserializzano JSON che usa il formato dell'epoca Unix con o senza una differenza di fuso orario (valori come /Date(1590863400000-0700)/ o /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);
    }
}

Per altre informazioni, vedere Supporto di DateTime e DateTimeOffset in System.Text.Json.

Callback

Newtonsoft.Json consente di eseguire un codice personalizzato in diversi punti del processo di serializzazione o deserializzazione:

  • OnDeserializing (quando inizia a deserializzare un oggetto)
  • OnDeserialized (al termine della deserializzazione di un oggetto)
  • OnSerializing (quando inizia a deserializzare un oggetto)
  • OnSerialized (al termine della deserializzazione di un oggetto)

System.Text.Json espone le stesse notifiche durante la serializzazione e la deserializzazione. Per usarle, implementare una o più delle interfacce seguenti dallo spazio dei nomi System.Text.Json.Serialization:

Di seguito si riporta un esempio che verifica la presenza di una proprietà Null e scrive messaggi all'inizio e alla fine della serializzazione e della deserializzazione:

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=

Il codice OnDeserializing non ha accesso alla nuova istanza POCO. Per modificare la nuova istanza POCO all'inizio della deserializzazione, inserire tale codice nel costruttore POCO.

Setter e getter di proprietà non pubblici

Newtonsoft.Json può usare setter di proprietà privati e interni e getter tramite l'attributo JsonProperty.

System.Text.Jsonsupporta setter di proprietà privati e interni e getter tramite l'attributo [JsonInclude]. Per consultare un codice di esempio, vedere la sezione Funzioni di accesso alle proprietà non pubbliche.

Popolare gli oggetti esistenti

Il metodo JsonConvert.PopulateObject in Newtonsoft.Json deserializza un documento JSON in un'istanza esistente di una classe, anziché creare una nuova istanza. System.Text.Json crea sempre una nuova istanza del tipo di destinazione usando il costruttore pubblico senza parametri predefinito. I convertitori personalizzati possono deserializzare in un'istanza esistente.

Riutilizzare anziché sostituire le proprietà

A partire da .NET 8, System.Text.Json supporta il riutilizzo delle proprietà inizializzate anziché la loro sostituzione. Esistono alcune differenze nel comportamento che è possibile leggere nella proposta dell'API.

Per altre informazioni, vedere Popolare le proprietà inizializzate.

L'impostazione ObjectCreationHandling in Newtonsoft.Json consente di specificare che gli oggetti nelle proprietà devono essere riutilizzati anziché sostituiti durante la deserializzazione. System.Text.Json sostituisce sempre gli oggetti nelle proprietà. I convertitori personalizzati possono fornire questa funzionalità oppure è possibile eseguire l'aggiornamento a .NET 8, che fornisce funzionalità di popolamento.

Popolare le proprietà senza setter

A partire da .NET 8, System.Text.Json supporta il popolamento delle proprietà, incluse quelle che non hanno un setter. Per altre informazioni, vedere Popolare le proprietà inizializzate.

Durante la deserializzazione, Newtonsoft.Json aggiunge oggetti a una raccolta anche se la proprietà non dispone di setter. System.Text.Json ignora le proprietà che non hanno setter. I convertitori personalizzati possono fornire questa funzionalità oppure è possibile eseguire l'aggiornamento a .NET 8, che fornisce funzionalità di popolamento.

Criteri di denominazione snake case

System.Text.Json include un criterio di denominazione predefinito per snake case. Esistono tuttavia alcune differenze di comportamento con Newtonsoft.Json per alcuni input. La tabella seguente illustra alcune di queste differenze durante la conversione dell'input usando i criteri JsonNamingPolicy.SnakeCaseLower.

Input Risultato Newtonsoft.Json Risultato System.Text.Json
"AB1" "a_b1" "ab1"
"SHA512Managed" "sh_a512_managed" "sha512_managed"
"abc123DEF456" "abc123_de_f456" "abc123_def456"
"CASO KEBAB" "keba_b-_case" "kebab-case"

L'unico criterio di denominazione delle proprietà predefinito in System.Text.Json è per camel case. Newtonsoft.Json può convertire i nomi delle proprietà in snake case. Un criterio di denominazione personalizzato può fornire questa funzionalità o eseguire l'aggiornamento a .NET 8 o versioni successive, che include i criteri di denominazione di snake case predefiniti.

Attributi System.Runtime.Serialization

Attributi System.Runtime.Serialization come DataContractAttribute, DataMemberAttribute e IgnoreDataMemberAttribute consentono di definire un contratto di dati . Un contratto dati è un accordo formale tra un servizio e un client che descrive astrattamente i dati da scambiare. Il contratto di dati definisce con precisione le proprietà serializzate per lo scambio.

System.Text.Json non dispone del supporto predefinito per questi attributi. Tuttavia, a partire da .NET 7, è possibile usare un resolver di tipi personalizzato per aggiungere supporto. Per visualizzare un esempio, vedere ZCS.DataContractResolver.

Numeri ottali

Newtonsoft.Json considera i numeri con uno zero iniziale come numeri ottali. System.Text.Json non consente zero iniziali perché non consentiti dalla specifica RFC 8259.

Gestire i membri mancanti

Se il codice JSON da deserializzare include proprietà mancanti nel tipo di destinazione, Newtonsoft.Json può essere configurato per generare eccezioni. Per impostazione predefinita, System.Text.Json ignora le proprietà aggiuntive nel codice JSON, tranne quando si usa l'attributo [JsonExtensionData].

In .NET 8 e versioni successive è possibile impostare le preferenze per ignorare o disabilitare le proprietà JSON non mappate usando uno dei seguenti metodi:

JsonObjectAttribute

Newtonsoft.Json dispone di un attributo JsonObjectAttribute che può essere applicato a livello di tipo per controllare quali membri vengono serializzati, come vengono gestiti i valori null e se sono obbligatori tutti i membri. System.Text.Json non dispone di un attributo equivalente che può essere applicato a un tipo. Per alcuni comportamenti, ad esempio la gestione dei valori null, è possibile configurare lo stesso comportamento nel contesto globale JsonSerializerOptions o singolarmente in ogni proprietà.

Si consideri l'esempio seguente che usa Newtonsoft.Json.JsonObjectAttribute per specificare che tutte le proprietà null devono essere ignorate:

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

In System.Text.Json, è possibile impostare il comportamento per tutti i tipi e per tutte le proprietà :

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

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

In alternativa, è possibile impostare il comportamento in ogni proprietà separatamente:

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

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

Si consideri quindi l'esempio seguente che usa Newtonsoft.Json.JsonObjectAttribute per specificare che tutte le proprietà dei membri devono essere presenti nel codice JSON:

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

È possibile ottenere lo stesso comportamento in System.Text.Json aggiungendo il modificatore C# required o JsonRequiredAttribute a ogni proprietà. Per altre informazioni, vedere Proprietà obbligatorie.

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

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

TraceWriter

Newtonsoft.Json consente di eseguire il debug usando un oggetto TraceWriter per visualizzare i log generati dalla serializzazione o dalla deserializzazione. System.Text.Json non esegue la registrazione.

JsonDocument e JsonElement confrontati rispetto a JToken (ad esempio JObject, JArray)

System.Text.Json.JsonDocument offre la possibilità di analizzare e compilare un DOM (Document Object Model) di sola lettura da payload JSON esistenti. DOM fornisce l'accesso casuale ai dati in un payload JSON. È possibile accedere agli elementi JSON che compongono il payload tramite il tipo JsonElement. Il JsonElement tipo fornisce API per convertire il testo JSON in tipi .NET comuni. JsonDocument espone una proprietà RootElement.

A partire da .NET 6, è possibile analizzare e compilare un DOM modificabile da payload JSON esistenti usando il tipo JsonNode e altri tipi nello spazio dei nomi System.Text.Json.Nodes. Per ottenere ulteriori informazioni, vedere Usare JsonNode.

JsonDocument è IDisposable

JsonDocument crea una vista in memoria dei dati in un buffer in pool. Pertanto, a differenza di JObject o JArray da Newtonsoft.Json, il tipo JsonDocument implementa IDisposable e deve essere usato all'interno di un blocco using. Per altre informazioni, vedere JsonDocument è IDisposable.

JsonDocument è di sola lettura

Il DOM System.Text.Json non può aggiungere, rimuovere o modificare elementi JSON. È progettato in questo modo per le prestazioni e per ridurre le allocazioni per l'analisi delle dimensioni comuni del payload JSON (ovvero < 1 MB).

JsonElement è uno struct di unione

JsonDocument espone RootElement come proprietà di tipo JsonElement, che è un tipo di struct di unione che include qualsiasi elemento JSON. Newtonsoft.Json usa tipi gerarchici dedicati come JObject,JArray, JTokene così via. JsonElement è ciò che è possibile cercare ed enumerare ed è possibile usare JsonElement per materializzare gli elementi JSON in tipi .NET.

A partire da .NET 6, è possibile usare il tipo JsonNode e i tipi nello spazio dei nomi System.Text.Json.Nodes che corrispondono a JObject,JArray e JToken. Per ottenere ulteriori informazioni, vedere Usare JsonNode.

Come cercare elementi secondari in JsonDocument e JsonElement

Le ricerche di token JSON che usano JObject o JArray da Newtonsoft.Json tendono a essere relativamente veloci perché sono ricerche in un dizionario. A confronto, le ricerche in JsonElement richiedono una ricerca sequenziale delle proprietà e quindi sono relativamente lente (ad esempio quando si usa TryGetProperty). System.Text.Json è progettato per ridurre al minimo il tempo di analisi iniziale anziché il tempo di ricerca. Per ottenere ulteriori informazioni, vedere Come cercare elementi secondari in JsonDocument e JsonElement.

Utf8JsonReader vs. JsonTextReader

System.Text.Json.Utf8JsonReader è un lettore forward-only a prestazioni elevate e allocazione ridotta per il testo JSON con codifica UTF-8, letto da un byte ReadOnlySpan<> o da un byte ReadOnlySequence<>. Utf8JsonReader è un tipo di basso livello che può essere usato per creare parser e deserializzatori personalizzati.

Utf8JsonReader è uno struct di riferimento

JsonTextReader in Newtonsoft.Json è una classe. Il tipo Utf8JsonReader è diverso poiché è uno struct di riferimento. Per ottenere ulteriori informazioni, vedere Limitazioni degli struct di riferimento per Utf8JsonReader.

Leggere i valori Null in tipi di valore che ammettono i valori Null

Newtonsoft.Json fornisce API che restituiscono Nullable<T>, ad esempio ReadAsBoolean, che gestisce un oggetto Null TokenType per l'utente restituendo un oggetto bool?. Le API predefinite System.Text.Json restituiscono solo tipi valore che non ammettono i valori Null. Per ottenere ulteriori informazioni, vedere Leggere i valori Null in tipi di valore che ammettono i valori Null.

Multi-target per la lettura di JSON

Se è necessario continuare a usare Newtonsoft.Json per determinati framework di destinazione, è possibile usare più partizioni e avere due implementazioni. Tuttavia, questo non è semplice e richiederebbe alcuni #ifdefs e la duplicazione di origine. Un modo per condividere il maggior numero possibile di codici consiste nel creare un wrapper ref struct intorno a Utf8JsonReader e Newtonsoft.Json.JsonTextReader. Questo wrapper unifica l'area di superficie pubblica isolando le differenze comportamentali. Ciò consente di isolare principalmente le modifiche alla costruzione del tipo, oltre a passare il nuovo tipo per riferimento. Questo è il modello seguito dalla libreria Microsoft.Extensions.DependencyModel :

Utf8JsonWriter vs. JsonTextWriter

System.Text.Json.Utf8JsonWriter è un modo ad alte prestazioni per scrivere testo JSON con codifica UTF-8 da tipi .NET comuni come String, Int32e DateTime. Il writer è un tipo di basso livello che può essere usato per creare serializzatori personalizzati.

Scrivere valori non elaborati

Newtonsoft.Json dispone di un metodo WriteRawValue che scrive codice JSON non elaborato in cui è previsto un valore. System.Text.Json ha come equivalente diretto Utf8JsonWriter.WriteRawValue. Per ottenere ulteriori informazioni, vedere la sezioneScrivere codice JSON non elaborato.

Personalizzare il formato JSON

JsonTextWriter include le impostazioni seguenti, per le quali Utf8JsonWriter non ha un equivalente:

  • QuoteChar: specifica il carattere da usare per racchiudere i valori stringa. Utf8JsonWriter usa sempre le virgolette doppie.
  • QuoteName: specifica se racchiudere o meno i nomi delle proprietà tra virgolette. Utf8JsonWriter li racchiude sempre tra virgolette.

A partire da .NET 9, è possibile personalizzare il carattere di rientro e le dimensioni per Utf8JsonWriter utilizzando le opzioni esposte dallo struct JsonWriterOptions:

JsonTextWriter include le impostazioni seguenti, per le quali Utf8JsonWriter non ha un equivalente:

  • Indentation: specifica il numero di caratteri per impostare un rientro. Utf8JsonWriter rientra sempre di 2 caratteri.
  • IndentChar: specifica il carattere da utilizzare per il rientro. Utf8JsonWriter usa sempre spazi vuoti.
  • QuoteChar: specifica il carattere da usare per racchiudere i valori stringa. Utf8JsonWriter usa sempre le virgolette doppie.
  • QuoteName: specifica se racchiudere o meno i nomi delle proprietà tra virgolette. Utf8JsonWriter li racchiude sempre tra virgolette.

Non esistono soluzioni alternative che consentono di personalizzare il codice JSON prodotto da Utf8JsonWriter in questi modi.

Scrivere valori Timespan, Uri o char

JsonTextWriter fornisce metodi WriteValue per i valori TimeSpan, Uri e char. Utf8JsonWriter non dispone di metodi equivalenti. Al contrario, formattare questi valori come stringhe (chiamando ad esempio ToString()) e chiamare WriteStringValue.

Multi-destinazione per la scrittura di JSON

Se è necessario continuare a usare Newtonsoft.Json per determinati framework di destinazione, è possibile usare più partizioni e avere due implementazioni. Tuttavia, questo non è semplice e richiederebbe alcuni #ifdefs e la duplicazione di origine. Un modo per condividere il maggior numero possibile di codici consiste nel creare un wrapper Utf8JsonWriter intorno a Newtonsoft.Json.JsonTextWriter. Questo wrapper unifica l'area di superficie pubblica isolando le differenze comportamentali. In questo modo è possibile isolare principalmente le modifiche alla costruzione del tipo. La libreria Microsoft.Extensions.DependencyModel segue:

TypeNameHandling.All non supportato

La decisione di escludere la funzionalità equivalente TypeNameHandling.All da System.Text.Json è stata intenzionale. Consentire a un payload JSON di specificare le proprie informazioni sul tipo è una fonte comune di vulnerabilità nelle applicazioni Web. In particolare, la configurazione Newtonsoft.Json con TypeNameHandling.All consente al client remoto di incorporare un'intera applicazione eseguibile all'interno del payload JSON stesso, in modo che durante la deserializzazione l'applicazione Web estrae ed esegue il codice incorporato. Per ottenere ulteriori informazioni, consultare Friday 13th JSON attacks PowerPoint e Friday the 13th JSON attacks details.

Query di percorso JSON non supportate

Il DOM JsonDocument non supporta l'esecuzione di query tramite JSON Path.

In un DOM JsonNode ogni istanza JsonNode ha un metodo GetPath che restituisce un percorso a tale nodo. Non esiste tuttavia alcuna API predefinita per gestire le query basate su stringhe di query JSON Path.

Per ottenere ulteriori informazioni, vedere problema GitHub di dotnet/runtime #31068.

Alcuni limiti non configurabili

System.Text.Json imposta i limiti che non possono essere modificati per alcuni valori, ad esempio la dimensione massima del token in caratteri (166 MB) e in base 64 (125 MB). Per ottenere ulteiori informazioni, vedere JsonConstants nel codice sorgente e nel problema di GitHub dotnet/runtime #39953.

NaN, Infinity, -Infinity

Newtonsoft analizza i token di stringa JSON NaN, Infinity e -Infinity. Con System.Text.Json, usare JsonNumberHandling.AllowNamedFloatingPointLiterals. Per ottenere maggiori informazioni su come usare questa impostazione, vedere la sezione Consentire o scrivere numeri tra virgolette.

Usare GitHub Copilot per eseguire la migrazione

È possibile ottenere assistenza per la codifica da GitHub Copilot per convertire il codice all'interno dell'IDE.

Se si usa Visual Studio 2022 versione 17.8 o successiva, è possibile provare GitHub Copilot supportato da IA in Visual Studio per facilitare la migrazione del codice Newtonsoft.Json da usare System.Text.Json. Inviare la domanda e il codice da convertire come richiesta nella finestra di chat copilot, come nell'esempio seguente.

Nota

GitHub Copilot è supportato dall'IA, quindi sono possibili sorprese ed errori. Assicurarsi di verificare il codice o i suggerimenti generati. Per maggiori informazioni sull'uso generale di GitHub Copilot, sull'impatto sul prodotto, sulla supervisione umana e sulla privacy, vedere le Domande frequenti su GitHub Copilot.

Richiesta di esempio per Copilot Chat

convert the following code to use System.Text.Json
Product product = new Product();

product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };

string output = JsonConvert.SerializeObject(product);
Console.WriteLine(output);

Esempio di risposta di Copilot Chat

To convert the code to use System.Text.Json, you can use the JsonSerializer class. Here's the updated code:
using System;
using System.Text.Json;

public class Product
{
    public string Name { get; set; }
    public DateTime ExpiryDate { get; set; }
    public decimal Price { get; set; }
    public string[] Sizes { get; set; }
}

public class Program
{
    public static void Main()
    {
        Product product = new Product();

        product.Name = "Apple";
        product.ExpiryDate = new DateTime(2024, 08, 08);
        product.Price = 3.99M;
        product.Sizes = new string[] { "Small", "Medium", "Large" };

        string output = JsonSerializer.Serialize(product);
        Console.WriteLine(output);
    }
}
This code uses System.Text.Json.JsonSerializer.Serialize to convert the Product object to a JSON string.

Si noti che i risultati potrebbero essere diversi da quanto illustrato in queste risposte di esempio. I modelli di IA non sono deterministici, il che significa che possono restituire risposte diverse quando viene posta la stessa domanda. Questo potrebbe essere dovuto ad apprendimento e adattamento aggiuntivo nel tempo, alla variazione della lingua, ai cambiamenti nel contesto, ad esempio alla cronologia delle chat e altro ancora.

Screenshot animato che mostra l'uso di GitHub Copilot Chat in Visual Studio per eseguire la migrazione da newtonsoft

È possibile usare funzionalità di chat, ad esempio comandi barra, riferimenti e thread, per impostare la finalità e ottenere risposte migliori con il contesto con ambito. Ad esempio, se il file di codice filename è aperto nell'IDE, è possibile fare riferimento al file nel prompt a Copilot Chat con "convert #filename to use System.Text.Json". In alternativa, è possibile fare riferimento alla soluzione con "convert @workspace to use System.Text.Json" nella finestra di chat o nella chat inline.

Risorse aggiuntive