Confrontare Newtonsoft.Json con System.Text.Jsone eseguire la migrazione a System.Text.Json

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

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

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

System.Text.Json si concentra principalmente sulla conformità delle prestazioni, della sicurezza e degli standard. Presenta alcune differenze chiave nel comportamento predefinito e non mira ad avere la parità delle funzionalità con Newtonsoft.Json. Per alcuni scenari, attualmente non è disponibile alcuna funzionalità predefinita, System.Text.Json ma sono disponibili soluzioni alternative consigliate. Per altri scenari, le soluzioni alternative sono impraticabili.

Stiamo investendo nell'aggiunta delle funzionalità più spesso richieste. Se l'applicazione dipende da una funzionalità mancante, è consigliabile archiviare un problema nel repository dotnet/runtime GitHub per scoprire se è possibile aggiungere il supporto per lo scenario. Per scoprire cosa è già pianificato, vedere il problema epico #43620 .

La maggior parte di questo articolo descrive come usare l'API JsonSerializer , ma include anche indicazioni su come usare ( JsonDocument che rappresenta il modello a oggetti documento o DOM), Utf8JsonReadere Utf8JsonWriter i tipi.

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

Tabella delle differenze tra Newtonsoft.Json e System.Text.Json

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

  • Supportato dalla funzionalità predefinita. Ottenere un comportamento simile da System.Text.Json può richiedere l'uso di un attributo o di un'opzione globale.
  • Non supportato, la soluzione alternativa è possibile. Le soluzioni alternative sono convertitori personalizzati, che potrebbero non fornire parità completa con Newtonsoft.Json funzionalità. Per alcuni di questi, il codice di esempio viene fornito come esempi. Se si basa su queste Newtonsoft.Json funzionalità, la migrazione richiederà modifiche ai modelli a oggetti .NET o ad altre modifiche al codice.
  • Non supportata, la soluzione alternativa non è pratica o possibile. Se si fa affidamento su queste Newtonsoft.Json funzionalità, la migrazione non sarà possibile senza modifiche significative.
Funzionalità di Newtonsoft.Json System.Text.Json Equivalente
Deserializzazione senza distinzione tra maiuscole e minuscole per impostazione predefinita ✔️ Impostazione globale PropertyNameCaseInsensitive
Nomi delle proprietà Camel-case ✔️ Impostazione globale PropertyNamingPolicy
Fuga di caratteri minima ✔️ Escape di caratteri rigorosi, configurabile
NullValueHandling.Ignore impostazione globale ✔️ Opzione globale DefaultIgnoreCondition
Consenti commenti ✔️ Impostazione globale ReadCommentHandling
Consenti virgole finali ✔️ Impostazione globale AllowTrailingCommas
Registrazione del convertitore personalizzata ✔️ L'ordine di precedenza è diverso
Nessuna profondità massima per impostazione predefinita ✔️ Profondità massima predefinita 64, configurabile
PreserveReferencesHandling impostazione globale ✔️ Impostazione globale ReferenceHandling
Serializzare o deserializzare i numeri nelle virgolette ✔️ Impostazione globale NumberHandling, [JsonNumberHandling] attributo
Deserializzare le classi e gli struct non modificabili ✔️ JsonConstructor, record C# 9
Supporto per i campi ✔️ Impostazione globale IncludeFields, attributo [JsonInclude]
DefaultValueHandling impostazione globale ✔️ Impostazione globale DefaultIgnoreCondition
NullValueHandling impostazione su [JsonProperty] ✔️ Attributo JsonIgnore
DefaultValueHandling impostazione su [JsonProperty] ✔️ Attributo JsonIgnore
Deserializzare Dictionary con chiave non stringa ✔️ Supportato
Supporto per setter di proprietà non pubblici e getters ✔️ Attributo JsonInclude
Attributo [JsonConstructor] ✔️ Attributo [JsonConstructor]
ReferenceLoopHandling impostazione globale ✔️ Impostazione globale ReferenceHandling
Callback ✔️ Callback
NaN, Infinito, -Infinito ✔️ Supportato
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 object proprietà ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare il valore letterale JSON null ai tipi di valore non nullable ⚠️ Non supportato, soluzione alternativa, esempio
Requiredimpostazione sull'attributo [JsonProperty] ⚠️ Non supportato, soluzione alternativa, esempio
DefaultContractResolver per ignorare le proprietà ⚠️ Non supportato, soluzione alternativa, esempio
DateTimeZoneHandling, DateFormatString impostazioni ⚠️ Non supportato, soluzione alternativa, esempio
Metodo JsonConvert.PopulateObject ⚠️ Non supportato, soluzione alternativa
ObjectCreationHandling impostazione globale ⚠️ Non supportato, soluzione alternativa
Aggiungere alle raccolte senza setter ⚠️ Non supportato, soluzione alternativa
Nomi delle proprietà snake-case ⚠️ Non supportato, soluzione alternativa
Supporto per System.Runtime.Serialization gli attributi Non supportato
MissingMemberHandling impostazione globale Non supportato
Consenti nomi di proprietà senza virgolette Non supportato
Consenti virgolette singole intorno ai valori di stringa Non supportato
Consenti valori JSON non stringa per le proprietà stringa Non supportato
TypeNameHandling.All impostazione globale Non supportato
Supporto per JsonPath le query Non supportato
Limiti configurabili Non supportato
Funzionalità di Newtonsoft.Json System.Text.Json Equivalente
Deserializzazione senza distinzione tra maiuscole e minuscole per impostazione predefinita ✔️ Impostazione globale PropertyNameCaseInsensitive
Nomi delle proprietà Camel-case ✔️ Impostazione globale PropertyNamingPolicy
Fuga di caratteri minima ✔️ Escape di caratteri rigorosi, configurabile
NullValueHandling.Ignore impostazione globale ✔️ Opzione globale DefaultIgnoreCondition
Consenti commenti ✔️ Impostazione globale ReadCommentHandling
Consenti virgole finali ✔️ Impostazione globale AllowTrailingCommas
Registrazione del convertitore personalizzata ✔️ L'ordine di precedenza è diverso
Nessuna profondità massima per impostazione predefinita ✔️ Profondità massima predefinita 64, configurabile
PreserveReferencesHandling impostazione globale ✔️ Impostazione globale ReferenceHandling
Serializzare o deserializzare i numeri nelle virgolette ✔️ Impostazione globale NumberHandling, [JsonNumberHandling] attributo
Deserializzare le classi e gli struct non modificabili ✔️ JsonConstructor, record C# 9
Supporto per i campi ✔️ Impostazione globale IncludeFields, attributo [JsonInclude]
DefaultValueHandling impostazione globale ✔️ Impostazione globale DefaultIgnoreCondition
NullValueHandling impostazione su [JsonProperty] ✔️ Attributo JsonIgnore
DefaultValueHandling impostazione su [JsonProperty] ✔️ Attributo JsonIgnore
Deserializzare Dictionary con chiave non stringa ✔️ Supportato
Supporto per setter di proprietà non pubblici e getters ✔️ Attributo JsonInclude
Attributo [JsonConstructor] ✔️ Attributo [JsonConstructor]
NaN, Infinito, -Infinito ✔️ Supportato
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 object proprietà ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare il valore letterale JSON null ai tipi di valore non nullable ⚠️ Non supportato, soluzione alternativa, esempio
Requiredimpostazione sull'attributo [JsonProperty] ⚠️ Non supportato, soluzione alternativa, esempio
DefaultContractResolver per ignorare le proprietà ⚠️ Non supportato, soluzione alternativa, esempio
DateTimeZoneHandling, DateFormatString impostazioni ⚠️ Non supportato, soluzione alternativa, esempio
Callback ⚠️ Non supportato, soluzione alternativa, esempio
Metodo JsonConvert.PopulateObject ⚠️ Non supportato, soluzione alternativa
ObjectCreationHandling impostazione globale ⚠️ Non supportato, soluzione alternativa
Aggiungere alle raccolte senza setter ⚠️ Non supportato, soluzione alternativa
Nomi delle proprietà snake-case ⚠️ Non supportato, soluzione alternativa
ReferenceLoopHandling impostazione globale Non supportato
Supporto per System.Runtime.Serialization gli attributi Non supportato
MissingMemberHandling impostazione globale Non supportato
Consenti nomi di proprietà senza virgolette Non supportato
Consenti virgolette singole intorno ai valori di stringa Non supportato
Consenti valori JSON non stringa per le proprietà stringa Non supportato
TypeNameHandling.All impostazione globale Non supportato
Supporto per JsonPath le query Non supportato
Limiti configurabili Non supportato
Funzionalità di Newtonsoft.Json System.Text.Json Equivalente
Deserializzazione senza distinzione tra maiuscole e minuscole per impostazione predefinita ✔️ Impostazione globale PropertyNameCaseInsensitive
Nomi delle proprietà Camel-case ✔️ Impostazione globale PropertyNamingPolicy
Fuga di caratteri minima ✔️ Escape di caratteri rigorosi, configurabile
NullValueHandling.Ignore impostazione globale ✔️ Opzione globale IgnoreNullValues
Consenti commenti ✔️ Impostazione globale ReadCommentHandling
Consenti virgole finali ✔️ Impostazione globale AllowTrailingCommas
Registrazione del convertitore personalizzata ✔️ L'ordine di precedenza è diverso
Nessuna profondità massima per impostazione predefinita ✔️ Profondità massima predefinita 64, configurabile
Supporto per un'ampia gamma di tipi ⚠️ Alcuni tipi richiedono convertitori personalizzati
Deserializzare le stringhe come numeri ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare Dictionary con chiave non stringa ⚠️ Non supportato, soluzione alternativa, esempio
Serializzazione polimorfica ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzazione polimorfica ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare il tipo dedotto alle object proprietà ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare il valore letterale JSON null ai tipi di valore non nullable ⚠️ Non supportato, soluzione alternativa, esempio
Deserializzare le classi e gli struct non modificabili ⚠️ Non supportato, soluzione alternativa, esempio
Attributo [JsonConstructor] ⚠️ Non supportato, soluzione alternativa, esempio
Requiredimpostazione sull'attributo [JsonProperty] ⚠️ Non supportato, soluzione alternativa, esempio
NullValueHandlingimpostazione sull'attributo [JsonProperty] ⚠️ Non supportato, soluzione alternativa, esempio
DefaultValueHandlingimpostazione sull'attributo [JsonProperty] ⚠️ Non supportato, soluzione alternativa, esempio
DefaultValueHandling impostazione globale ⚠️ Non supportato, soluzione alternativa, esempio
DefaultContractResolver per ignorare le proprietà ⚠️ Non supportato, soluzione alternativa, esempio
DateTimeZoneHandling, DateFormatString impostazioni ⚠️ Non supportato, soluzione alternativa, esempio
Callback ⚠️ Non supportato, soluzione alternativa, esempio
Supporto per campi pubblici e non pubblici ⚠️ Non supportato, soluzione alternativa
Supporto per setter di proprietà non pubblici e getters ⚠️ Non supportato, soluzione alternativa
Metodo JsonConvert.PopulateObject ⚠️ Non supportato, soluzione alternativa
ObjectCreationHandling impostazione globale ⚠️ Non supportato, soluzione alternativa
Aggiungere alle raccolte senza setter ⚠️ Non supportato, soluzione alternativa
Nomi delle proprietà snake-case ⚠️ Non supportato, soluzione alternativa
NaN, Infinito, -Infinito ⚠️ Non supportato, soluzione alternativa
PreserveReferencesHandling impostazione globale Non supportato
ReferenceLoopHandling impostazione globale Non supportato
Supporto per System.Runtime.Serialization gli attributi Non supportato
MissingMemberHandling impostazione globale Non supportato
Consenti nomi di proprietà senza virgolette Non supportato
Consenti virgolette singole intorno ai valori di stringa Non supportato
Consenti valori JSON non stringa per le proprietà stringa Non supportato
TypeNameHandling.All impostazione globale Non supportato
Supporto per JsonPath le query Non supportato
Limiti configurabili Non supportato

Questo non è un elenco esaustivo di Newtonsoft.Json funzionalità. L'elenco include molti degli scenari richiesti nei post di GitHub o StackOverflow. Se si implementa una soluzione alternativa per uno degli scenari elencati qui che attualmente non hanno 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 di GitHub della documentazione e lo elenca anche nella sezione Commenti e suggerimenti in questa pagina.

Differenze nel comportamento predefinito di JsonSerializer rispetto a Newtonsoft.Json

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

Deserializzazione senza distinzione tra maiuscole e minuscole

Durante la deserializzazione, Newtonsoft.Json il nome della proprietà senza distinzione tra maiuscole e minuscole corrisponde per impostazione predefinita. Il System.Text.Json valore predefinito è distinzione tra maiuscole e minuscole, che offre prestazioni migliori perché esegue una corrispondenza esatta. Per informazioni su come eseguire la corrispondenza senza distinzione tra maiuscole e minuscole, vedere Corrispondenza delle proprietà senza distinzione tra maiuscole e minuscole.

Se si usa indirettamente usando System.Text.Json 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-casing e la corrispondenza senza distinzione tra maiuscole e minuscole quando viene usata System.Text.Json.

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

Fuga di caratteri minima

Durante la serializzazione, Newtonsoft.Json è relativamente permissivo di lasciare che i caratteri attraverso senza scappatorli. Ovvero, non li sostituisce con \uxxxx dove xxxx è il punto di codice del carattere. Dove viene eseguito l'escape, lo fa emettendo un \ prima del carattere (ad esempio, " diventa \"). System.Text.Json escape di più caratteri per impostazione predefinita per fornire protezioni approfondite dalla difesa contro gli attacchi XSS (cross-site scripting) o di divulgazione delle informazioni e quindi usando la sequenza di sei caratteri. System.Text.Json escape di tutti i caratteri non ASCII per impostazione predefinita, quindi non è necessario eseguire alcuna operazione se si usa StringEscapeHandling.EscapeNonAscii in Newtonsoft.Json. System.Text.Json escape anche di caratteri sensibili HTML, per impostazione predefinita. Per informazioni su come eseguire l'override del comportamento predefinito System.Text.Json , vedere Personalizzare la codifica dei caratteri.

Commenti

Durante la deserializzazione, Newtonsoft.Json ignora i commenti in JSON per impostazione predefinita. Il System.Text.Json valore predefinito consiste nel generare eccezioni per i commenti perché la specifica RFC 8259 non li include. Per informazioni su come consentire 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"},,]). Il System.Text.Json valore predefinito consiste nel generare eccezioni per le virgole finali perché la specifica RFC 8259 non consente loro. Per informazioni su come System.Text.Json accettarli, vedere Consenti commenti e virgole finali. Non è possibile consentire più virgole finali.

Precedenza della registrazione del convertitore

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

  • Attributo sulla proprietà
  • Attributo in base al tipo
  • Insieme Convertitori

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

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

  • Attributo sulla proprietà
  • Converters Collezione
  • Attributo in base al tipo

La differenza è che un convertitore personalizzato nell'insieme esegue l'override Converters di un attributo a livello di tipo. L'intenzione di questo ordine di precedenza consiste nel apportare modifiche in fase di esecuzione e sostituire le scelte di progettazione. Non è possibile modificare la precedenza.

Per altre 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à pari a 64 per impostazione predefinita. System.Text.Json ha anche un limite predefinito di 64 ed è configurabile impostando JsonSerializerOptions.MaxDepth.

Se si usa System.Text.Json indirettamente usando ASP.NET Core, il limite massimo di profondità predefinito è 32. Il valore predefinito è lo stesso per l'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à circondati da virgolette doppie, virgolette singole o senza virgolette. Accetta valori stringa circondati da 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 in virgolette doppie perché tale formato è richiesto dalla specifica RFC 8259 ed è l'unico formato considerato JSON valido.

Un valore racchiuso tra virgolette singole genera un oggetto 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 alle proprietà della stringa di tipo. 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 deserializzare i valori non stringa nelle proprietà stringa. Un valore non stringa ricevuto per un campo stringa genera un'eccezione JsonException con il messaggio seguente:

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

Scenari che usano 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 parità completa con Newtonsoft.Json funzionalità. Per alcuni di questi, il codice di esempio viene fornito come esempi. Se si basa su queste Newtonsoft.Json funzionalità, la migrazione richiederà modifiche ai modelli a oggetti .NET o ad altre modifiche al codice.

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

Consenti o scrivi numeri nelle virgolette

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

Se si usa indirettamente usando System.Text.Json 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 con virgolette.

Per altre informazioni, vedere Consenti o scrivi numeri in virgolette.

Newtonsoft.Json può serializzare o deserializzare i numeri rappresentati da stringhe JSON (circondate da virgolette). Ad esempio, può accettare: {"DegreesCelsius":"23"} anziché {"DegreesCelsius":23}. Per abilitare tale comportamento in System.Text.Json .NET Core 3.1, implementare un convertitore personalizzato come l'esempio seguente. Il convertitore gestisce le proprietà definite come long:

  • Le serializza come stringhe JSON.
  • Accetta numeri e numeri JSON all'interno di virgolette durante la deserializzazione.
using System.Buffers;
using System.Buffers.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class LongToStringConverter : JsonConverter<long>
    {
        public override long Read(
            ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.String)
            {
                ReadOnlySpan<byte> span =
                    reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;

                if (Utf8Parser.TryParse(span, out long number, out int bytesConsumed) &&
                    span.Length == bytesConsumed)
                {
                    return number;
                }

                if (long.TryParse(reader.GetString(), out number))
                {
                    return number;
                }
            }

            return reader.GetInt64();
        }

        public override void Write(
            Utf8JsonWriter writer, long longValue, JsonSerializerOptions options) =>
            writer.WriteStringValue(longValue.ToString());
    }
}

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

Specificare il costruttore da usare durante la deserializzazione

L'attributo Newtonsoft.Json[JsonConstructor] consente di specificare il costruttore da chiamare durante la deserializzazione di un oggetto POCO.

System.Text.Jsonha anche un attributo [JsonConstructor]. Per altre informazioni, vedere Tipi e record non modificabili.

System.Text.Json in .NET Core 3.1 supporta solo costruttori senza parametri. Come soluzione alternativa, è possibile chiamare il costruttore necessario in un convertitore personalizzato. Vedere l'esempio di Deserialize per le classi e gli struct non modificabili.

Ignorare in modo condizionale una proprietà

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

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

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

System.Text.Json in .NET Core 3.1 offre i modi seguenti per ignorare le proprietà durante la serializzazione:

Queste opzioni non consentono di:

  • Ignorare le proprietà selezionate in base ai criteri arbitrari valutati in fase di esecuzione.
  • Ignorare tutte le proprietà con il valore predefinito per il tipo.
  • Ignorare le proprietà selezionate con il valore predefinito per il tipo.
  • Ignorare le proprietà selezionate se il valore è Null.
  • Ignorare le proprietà selezionate in base ai criteri arbitrari valutati in fase di esecuzione.

Per tale funzionalità, è possibile scrivere un convertitore personalizzato. Ecco un esempio POCO e un convertitore personalizzato che illustra questo approccio:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class WeatherForecastRuntimeIgnoreConverter : JsonConverter<WeatherForecast>
    {
        public override WeatherForecast Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException();
            }

            var wf = new WeatherForecast();

            while (reader.Read())
            {
                if (reader.TokenType == JsonTokenType.EndObject)
                {
                    return wf;
                }

                if (reader.TokenType == JsonTokenType.PropertyName)
                {
                    string propertyName = reader.GetString()!;
                    reader.Read();
                    switch (propertyName)
                    {
                        case "Date":
                            DateTimeOffset date = reader.GetDateTimeOffset();
                            wf.Date = date;
                            break;
                        case "TemperatureCelsius":
                            int temperatureCelsius = reader.GetInt32();
                            wf.TemperatureCelsius = temperatureCelsius;
                            break;
                        case "Summary":
                            string summary = reader.GetString()!;
                            wf.Summary = string.IsNullOrWhiteSpace(summary) ? "N/A" : summary;
                            break;
                    }
                }
            }

            throw new JsonException();
        }

        public override void Write(Utf8JsonWriter writer, WeatherForecast wf, JsonSerializerOptions options)
        {
            writer.WriteStartObject();

            writer.WriteString("Date", wf.Date);
            writer.WriteNumber("TemperatureCelsius", wf.TemperatureCelsius);
            if (!string.IsNullOrWhiteSpace(wf.Summary) && wf.Summary != "N/A")
            {
                writer.WriteString("Summary", wf.Summary);
            }

            writer.WriteEndObject();
        }
    }
}

Il convertitore causa l'omesso della Summary proprietà dalla serializzazione se il relativo valore è Null, una stringa vuota o "N/A".

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

Questo approccio richiede una logica aggiuntiva se:

  • POCO include proprietà complesse.
  • È necessario gestire gli attributi, ad esempio o opzioni, ad esempio [JsonIgnore] codificatori personalizzati.

Campi pubblici e non pubblici

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

In System.Text.Jsonusare l'impostazione globale o l'attributo [JsonInclude] per includere campi pubblici durante la serializzazione o la JsonSerializerOptions.IncludeFields deserializzazione. Per un esempio, vedere Includere i campi.

System.Text.Json in .NET Core 3.1 funziona solo con le proprietà pubbliche. I convertitori personalizzati possono fornire questa funzionalità.

Mantenere i riferimenti agli oggetti e i cicli di handle

Per impostazione predefinita, Newtonsoft.Json serializza in base al valore. Ad esempio, se un oggetto contiene due proprietà che contengono un riferimento allo stesso Person oggetto, i valori delle proprietà dell'oggetto Person vengono duplicati in 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 Person oggetto.
  • Il codice JSON creato per il secondo Person oggetto 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 null token JSON anziché ignorare il riferimento all'oggetto. Per altre informazioni, vedere Ignorare i riferimenti circolari.

Analogamente a ReferenceResolver, la System.Text.Json.Serialization.ReferenceResolverNewtonsoft.Json classe definisce il comportamento di conservazione 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:

System.Text.Json in .NET Core 3.1 supporta solo la serializzazione per valore e genera un'eccezione per i 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.JsonTKey , deve essere un tipo primitivo, non un tipo personalizzato. Per altre informazioni, vedere Tipi di chiavi supportati.

Attenzione

Deserializzazione in un punto Dictionary<TKey, TValue> in cui TKey viene digitato come qualsiasi elemento diverso da quello che string potrebbe introdurre una vulnerabilità di sicurezza nell'applicazione che usa. Per altre informazioni, vedere dotnet/runtime#4761.

Newtonsoft.Json supporta raccolte di tipo Dictionary<TKey, TValue>. Il supporto predefinito per le raccolte di dizionari in System.Text.Json .NET Core 3.1 è limitato a Dictionary<string, TValue>. Ovvero, la chiave deve essere una stringa.

Per supportare un dizionario con un numero intero o un altro tipo come chiave in .NET Core 3.1, creare un convertitore come l'esempio in Come scrivere convertitori personalizzati.

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. Per informazioni sulle funzionalità di serializzazione polimorfica limitate di System.Text.Json, vedere Serializzare le proprietà delle classi derivate.

La soluzione alternativa descritta è necessario definire le proprietà che possono contenere classi derivate come tipo object. Se non è possibile, un'altra opzione consiste nel creare un convertitore con un Write metodo per l'intera gerarchia dei tipi di ereditarietà come l'esempio in Come scrivere convertitori personalizzati.

Deserializzazione polimorfica

Newtonsoft.Json ha un'impostazione TypeNameHandling che aggiunge metadati dei nomi di tipo al codice JSON durante la serializzazione. Usa i metadati durante la deserializzazione per eseguire la deserializzazione polimorfica. System.Text.Json può eseguire un intervallo limitato di serializzazione polimorfica , ma non di deserializzazione polimorfica.

Per supportare la deserializzazione polimorfica, creare un convertitore come l'esempio in Come scrivere convertitori personalizzati.

Deserializzazione delle proprietà dell'oggetto

Quando Newtonsoft.Json deserializza in Object, è:

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

System.Text.Json archivia JsonElement una casella per valori primitivi e complessi ogni volta che deserializzazione in Object, ad esempio:

  • Proprietà object.
  • Valore object del dizionario.
  • Valore object della matrice.
  • objectRadice .

Tuttavia, System.Text.Json tratta lo stesso di Newtonsoft.Json e restituisce null un riferimento Null quando il payload ha il null valore letterale JSON in esso.

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

Deserialize null a tipo non nullable

Newtonsoft.Json non genera un'eccezione nello scenario seguente:

  • NullValueHandling è impostato su Ignoree
  • Durante la deserializzazione, il codice JSON contiene un valore Null per un tipo di valore non nullable.

Nello stesso scenario genera System.Text.Json un'eccezione. L'impostazione di gestione null corrispondente in System.Text.Json è JsonSerializerOptions.IgnoreNullValues = true.)

Se si possiede il tipo di destinazione, la soluzione alternativa migliore consiste nel rendere la proprietà in questione nullable (ad esempio, passare int a int?).

Un'altra soluzione alternativa consiste nel creare un convertitore per il tipo, ad esempio l'esempio seguente che gestisce i valori Null per DateTimeOffset i tipi:

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 Converters raccolta.

Nota: Il convertitore precedente gestisce 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 Date proprietà ha 1/1/0001 (default(DateTimeOffset)), ovvero il valore impostato nel costruttore viene sovrascritto. Dato lo stesso CODICE POCO e JSON, Newtonsoft.Json la deserializzazione lascerebbe 1/1/2001 nella Date proprietà.

Deserializzare le classi e gli struct non modificabili

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

In System.Text.Jsonusare l'attributo [JsonConstructor] per specificare l'uso di un costruttore con parametri. I record in C# 9 sono anche non modificabili e sono supportati come destinazioni di deserializzazione. Per altre informazioni, vedere Tipi e record non modificabili.

System.Text.Json in .NET Core 3.1 supporta solo costruttori senza parametri pubblici. Come soluzione alternativa, è possibile chiamare un costruttore con parametri in un convertitore personalizzato.

Ecco uno struct non modificabile con più parametri del costruttore:

public readonly struct ImmutablePoint
{
    public ImmutablePoint(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; }
    public int Y { get; }
}

Ecco un convertitore che serializza e deserializza questo struct:

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

namespace SystemTextJsonSamples
{
    public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
    {
        private readonly JsonEncodedText _xName = JsonEncodedText.Encode("X");
        private readonly JsonEncodedText _yName = JsonEncodedText.Encode("Y");

        private readonly JsonConverter<int> _intConverter;

        public ImmutablePointConverter(JsonSerializerOptions options) => 
            _intConverter = options?.GetConverter(typeof(int)) is JsonConverter<int> intConverter
                ? intConverter
                : throw new InvalidOperationException();

        public override ImmutablePoint Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException();
            };

            int? x = default;
            int? y = default;

            // Get the first property.
            reader.Read();
            if (reader.TokenType != JsonTokenType.PropertyName)
            {
                throw new JsonException();
            }

            if (reader.ValueTextEquals(_xName.EncodedUtf8Bytes))
            {
                x = ReadProperty(ref reader, options);
            }
            else if (reader.ValueTextEquals(_yName.EncodedUtf8Bytes))
            {
                y = ReadProperty(ref reader, options);
            }
            else
            {
                throw new JsonException();
            }

            // Get the second property.
            reader.Read();
            if (reader.TokenType != JsonTokenType.PropertyName)
            {
                throw new JsonException();
            }

            if (x.HasValue && reader.ValueTextEquals(_yName.EncodedUtf8Bytes))
            {
                y = ReadProperty(ref reader, options);
            }
            else if (y.HasValue && reader.ValueTextEquals(_xName.EncodedUtf8Bytes))
            {
                x = ReadProperty(ref reader, options);
            }
            else
            {
                throw new JsonException();
            }

            reader.Read();

            if (reader.TokenType != JsonTokenType.EndObject)
            {
                throw new JsonException();
            }

            return new ImmutablePoint(x.GetValueOrDefault(), y.GetValueOrDefault());
        }

        private int ReadProperty(ref Utf8JsonReader reader, JsonSerializerOptions options)
        {
            Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);

            reader.Read();
            return _intConverter.Read(ref reader, typeof(int), options);
        }

        private void WriteProperty(Utf8JsonWriter writer, JsonEncodedText name, int intValue, JsonSerializerOptions options)
        {
            writer.WritePropertyName(name);
            _intConverter.Write(writer, intValue, options);
        }

        public override void Write(
            Utf8JsonWriter writer,
            ImmutablePoint point,
            JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            WriteProperty(writer, _xName, point.X, options);
            WriteProperty(writer, _yName, point.Y, options);
            writer.WriteEndObject();
        }
    }
}

Registrare questo convertitore personalizzato aggiungendo il convertitore alla Converters raccolta.

Per un esempio di convertitore simile che gestisce proprietà generice aperte, vedere il convertitore predefinito per coppie chiave-valore.

Proprietà obbligatorie

In Newtonsoft.Jsonspecificare che una proprietà è necessaria impostando Required sull'attributo [JsonProperty] . Newtonsoft.Json genera un'eccezione se non viene ricevuto alcun valore in JSON per una proprietà contrassegnata come richiesto.

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 WeatherForecast classe:

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 rendere la deserializzazione non riuscita se nessuna Date proprietà è presente nel codice JSON, scegliere una delle opzioni seguenti:

Il codice del convertitore di esempio seguente genera un'eccezione se la proprietà non è impostata dopo il completamento della Date 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 JsonSerializerOptions.Converters raccolta.

Questo modello di chiamata ricorsiva al convertitore richiede che il convertitore venga registrato usando , non usando JsonSerializerOptionsun attributo. Se si registra il convertitore usando un attributo, il convertitore personalizzato chiama in modo ricorsivo in se stesso. Il risultato è un ciclo infinito che termina in un'eccezione di overflow dello stack.

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

Esiste un modello alternativo che può usare JsonConverterAttribute la registrazione nella classe da convertire. In questo approccio, il codice del convertitore chiama Serialize o Deserialize su una classe che deriva dalla classe da convertire. La classe derivata non ha un'applicazione JsonConverterAttribute a tale classe. Nell'esempio seguente di questa alternativa:

  • WeatherForecastWithRequiredPropertyConverterAttribute è la classe da deserializzare e l'ha JsonConverterAttribute applicata.
  • WeatherForecastWithoutRequiredPropertyConverterAttribute è la classe derivata che non ha l'attributo convertitore.
  • Il codice nel convertitore chiama Serializee Deserialize su WeatherForecastWithoutRequiredPropertyConverterAttribute per evitare un ciclo infinito. È previsto un costo delle prestazioni per questo approccio sulla serializzazione a causa di un'istanza di oggetti aggiuntiva e la copia dei valori delle proprietà.

Ecco i WeatherForecast* tipi:

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

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à richiesto richiede una logica aggiuntiva se è necessario gestire gli attributi, ad esempio [JsonIgnore] o opzioni diverse, ad esempio codificatori personalizzati. Inoltre, il codice di esempio non gestisce le proprietà per cui viene impostato un valore predefinito nel costruttore. Questo approccio non distingue tra gli scenari seguenti:

  • Una proprietà manca dal codice JSON.
  • Una proprietà per un tipo non nullable è presente in JSON, ma il valore è il valore predefinito per il tipo, ad esempio zero per un intoggetto .
  • Una proprietà per un tipo di valore nullable è presente in JSON, ma il valore è Null.

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

Specificare il formato data

Newtonsoft.Jsonoffre diversi modi per controllare la modalità di serializzazione e DateTimeOffset deserializzazione delle proprietà dei DateTime tipi:

  • L'impostazione DateTimeZoneHandling può essere usata per serializzare tutti i DateTime valori come date UTC.
  • L'impostazione DateFormatString e DateTime i convertitori 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 fa round trip esattamente. Per usare qualsiasi altro formato, creare un convertitore personalizzato. Ad esempio, i convertitori seguenti serializzano e deserializzare JSON che usa il formato dell'epoca Unix con o senza un offset del fuso orario (valori come /Date(1590863400000-0700)/ o /Date(1590863400000)/):

sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
    static readonly DateTimeOffset s_epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
    static readonly Regex s_regex = new Regex("^/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 TimeSpan(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 = FormattableString.Invariant($"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");
        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
    static readonly DateTime s_epoch = new DateTime(1970, 1, 1, 0, 0, 0);
    static readonly Regex s_regex = new Regex("^/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 = FormattableString.Invariant($"/Date({unixTime})/");
        writer.WriteStringValue(formatted);
    }
}

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

Callback

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

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

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

Ecco un esempio che controlla una proprietà Null e scrive i 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 OnDeserializing codice non ha accesso alla nuova istanza POCO. Per modificare la nuova istanza POCO all'inizio della deserializzazione, inserire il codice nel costruttore POCO.

In System.Text.Jsonè possibile simulare i callback scrivendo un convertitore personalizzato. Nell'esempio seguente viene illustrato un convertitore personalizzato per un POCO. Il convertitore include il codice che visualizza un messaggio a ogni punto che corrisponde a un Newtonsoft.Json callback.

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

namespace SystemTextJsonSamples
{
    public class WeatherForecastCallbacksConverter : JsonConverter<WeatherForecast>
    {
        public override WeatherForecast Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // Place "before" code here (OnDeserializing),
            // but note that there is no access here to the POCO instance.
            Console.WriteLine("OnDeserializing");

            // Don't pass in options when recursively calling Deserialize.
            WeatherForecast forecast = JsonSerializer.Deserialize<WeatherForecast>(ref reader)!;

            // Place "after" code here (OnDeserialized)
            Console.WriteLine("OnDeserialized");

            return forecast;
        }

        public override void Write(
            Utf8JsonWriter writer,
            WeatherForecast forecast, JsonSerializerOptions options)
        {
            // Place "before" code here (OnSerializing)
            Console.WriteLine("OnSerializing");

            // Don't pass in options when recursively calling Serialize.
            JsonSerializer.Serialize(writer, forecast);

            // Place "after" code here (OnSerialized)
            Console.WriteLine("OnSerialized");
        }
    }
}

Registrare questo convertitore personalizzato aggiungendo il convertitore alla Converters raccolta.

Se si usa un convertitore personalizzato che segue l'esempio precedente:

  • Il OnDeserializing codice non ha accesso alla nuova istanza POCO. Per modificare la nuova istanza POCO all'inizio della deserializzazione, inserire il codice nel costruttore POCO.
  • Evitare un ciclo infinito registrando il convertitore nell'oggetto opzioni e non passando nell'oggetto opzioni quando si chiama Serialize in modo ricorsivo o Deserialize.

Per altre informazioni sui convertitori personalizzati che chiamano Serialize in modo ricorsivo o Deserialize, vedere la sezione Proprietà richieste in precedenza in questo articolo.

Setters e getters di proprietà non pubblici

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

System.Text.Jsonsupporta setters di proprietà privati e interni e getters tramite l'attributo [JsonInclude]. Per il codice di esempio, vedere Funzioni di accesso alle proprietà non pubbliche.

System.Text.Json in .NET Core 3.1 supporta solo setter pubblici. I convertitori personalizzati possono fornire questa funzionalità.

Popolare gli oggetti esistenti

Il JsonConvert.PopulateObject metodo 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à

L'impostazione Newtonsoft.JsonObjectCreationHandling 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à.

Aggiungere alle raccolte senza setter

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 setters. I convertitori personalizzati possono fornire questa funzionalità.

Criterio di denominazione del caso serpente

L'unico criterio di denominazione delle proprietà predefinito in System.Text.Json è per il caso camel. Newtonsoft.Json può convertire i nomi delle proprietà in caso di serpente. Un criterio di denominazione personalizzato può fornire questa funzionalità. Per altre informazioni, vedere GitHub problema dotnet/runtime #782.

Attributi System.Runtime.Serialization

System.Text.Json non supporta gli attributi dello System.Runtime.Serialization spazio dei nomi, ad esempio DataMemberAttribute e IgnoreDataMemberAttribute.

Numeri ottali

Newtonsoft.Json tratta i numeri con zero iniziale come numeri ottali. System.Text.Json non consente zeli iniziali perché la specifica RFC 8259 non le consente.

MissingMemberHandling

Newtonsoft.Json può essere configurato per generare eccezioni durante la deserializzazione se JSON include proprietà mancanti nel tipo di destinazione. System.Text.Jsonignora le proprietà aggiuntive nel codice JSON, tranne quando si usa l'attributo [JsonExtensionData]. Non esiste alcuna soluzione alternativa per la funzionalità membro mancante.

TraceWriter

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

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

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

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

JsonDocument è IDisposable

JsonDocument compila una visualizzazione in memoria dei dati in un buffer in pool. Pertanto, a differenza JObject di o JArray da Newtonsoft.Json, il JsonDocument tipo implementa IDisposable e deve essere usato all'interno di un blocco using. Per altre informazioni, vedere JsonDocument is IDisposable.For more information, see JsonDocument is IDisposable.

JsonDocument è di sola lettura

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).

Se lo scenario attualmente usa un DOM modificabile, una delle soluzioni alternative seguenti potrebbe essere fattibile:

  • Per compilare un JsonDocument oggetto da zero ( ovvero, senza passare un payload JSON esistente al Parse metodo ), scrivere il testo JSON usando Utf8JsonWriter e analizzare l'output da che per creare un nuovo JsonDocument.
  • Per modificare un oggetto esistente JsonDocument, usarlo per scrivere testo JSON, apportare modifiche durante la scrittura e analizzare l'output da tale oggetto per creare un nuovo JsonDocumentoggetto .
  • Per unire documenti JSON esistenti, equivalenti alle JObject.Merge API o JContainer.Merge da Newtonsoft.Json, vedere questo problema GitHub.

Queste soluzioni alternative sono necessarie solo per le versioni precedenti alla System.Text.Json 6.0. Nella versione 6.0 è possibile usare JsonNode per usare un DOM modificabile.

JsonElement è uno struct di unione

JsonDocument espone come RootElement 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 JsonNode tipi e tipi nello System.Text.Json.Nodes spazio dei nomi che corrispondono a JObject,JArray e JToken. Per altre informazioni, vedere Usare JsonNode.

Come cercare sottoelementi JsonDocument e JsonElement

Cerca i token JSON che usano JObject o JArray da Newtonsoft.Json tendono a essere relativamente veloci perché sono ricerche in un dizionario. Per confronto, le ricerche su 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 altre informazioni, vedere How to search a JsonDocument and JsonElement for sub-elements .For more information, see How to search a JsonDocument and JsonElement for sub-elements.

Utf8JsonReader rispetto a JsonTextReader

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

Utf8JsonReader è uno struct di riferimento

In JsonTextReaderNewtonsoft.Json è una classe . Il Utf8JsonReader tipo è diverso dal fatto che si tratta di uno struct ref. Per altre informazioni, vedere Utf8JsonReader è uno struct di riferimento.

Leggere i valori Null in tipi valore nullable

Newtonsoft.Json fornisce API che restituiscono Nullable<T>, ad esempio ReadAsBoolean, che gestisce automaticamente un NullTokenType oggetto restituendo un oggetto bool?. Le API predefinite System.Text.Json restituiscono solo tipi valore non nullable. Per altre informazioni, vedere Leggere i valori Null in tipi valore nullable.

Multitargeting

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 una duplicazione di origine e alcuni #ifdefs . Un modo per condividere il maggior numero possibile di codice consiste nel creare un ref struct wrapper intorno Utf8JsonReader a e Newtonsoft.JsonJsonTextReader. Questo wrapper unificare l'area di superficie pubblica isolando le differenze comportamentali. In questo modo è possibile isolare le modifiche principalmente alla costruzione del tipo, insieme al passaggio del nuovo tipo in base al riferimento. Questo è il modello che segue la libreria Microsoft.Extensions.DependencyModel :

Utf8JsonWriter rispetto a 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 compilare serializzatori personalizzati.

Scrivere valori non elaborati

Il Newtonsoft.JsonWriteRawValue metodo scrive json non elaborato in cui è previsto un valore. System.Text.Json ha un equivalente diretto: Utf8JsonWriter.WriteRawValue. Per altre informazioni, vedere Scrivere json non elaborato.

Il Newtonsoft.JsonWriteRawValue metodo scrive json non elaborato in cui è previsto un valore. Esiste un metodo equivalente, Utf8JsonWriter.WriteRawValue, in .NET 6. Per altre informazioni, vedere Scrivere json non elaborato.

Per le versioni precedenti alla 6.0, System.Text.Json non ha un metodo equivalente per la scrittura di JSON non elaborato. Tuttavia, la soluzione alternativa seguente garantisce che venga scritto solo JSON valido:

using JsonDocument doc = JsonDocument.Parse(string);
doc.WriteTo(writer);

Personalizzare il formato JSON

JsonTextWriter include le impostazioni seguenti, per cui Utf8JsonWriter non ha equivalenti:

  • Rientro: specifica il numero di caratteri da impostare come rientro. Utf8JsonWriter esegue sempre un rientro a 2 caratteri.
  • IndentChar : specifica il carattere da utilizzare per il rientro. Utf8JsonWriter usa sempre spazi vuoti.
  • QuoteChar : specifica il carattere da utilizzare per racchiudere i valori stringa. Utf8JsonWriter usa sempre virgolette doppie.
  • QuoteName : specifica se racchiudere o meno i nomi delle proprietà tra virgolette. Utf8JsonWriter li circonda sempre con 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 WriteValue metodi per i valori TimeSpan, Uri e char . Utf8JsonWriter non dispone di metodi equivalenti. Formattare invece questi valori come stringhe (chiamando ToString(), ad esempio) e chiamare WriteStringValue.

Multitargeting

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 una duplicazione di origine e alcuni #ifdefs . Un modo per condividere il maggior numero possibile di codice consiste nel creare un wrapper intorno Utf8JsonWriter a e NewtonsoftJsonTextWriter. Questo wrapper unificare l'area di superficie pubblica isolando le differenze comportamentali. In questo modo è possibile isolare le modifiche principalmente alla costruzione del tipo. La libreria Microsoft.Extensions.DependencyModel è la seguente:

TypeNameHandling.All non supportato

La decisione di escludere TypeNameHandling.Allla funzionalità equivalente da System.Text.Json è stata intenzionale. Consentire a un payload JSON di specificare le proprie informazioni sul tipo è un'origine 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 estraa ed esegue il codice incorporato. Per altre informazioni, vedere Venerdì 13 gli attacchi JSON PowerPoint e venerdì 13° attacco JSON.

Query di percorso JSON non supportate

Il JsonDocument DOM non supporta la query usando il percorso JSON.

In un JsonNode DOM JsonNode ogni istanza ha un metodo che restituisce un GetPath percorso a tale nodo. Ma non è disponibile alcuna API predefinita per gestire le query basate sulle stringhe di query percorso JSON.

Per altre informazioni, vedere il problema dotnet/runtime #31068 GitHub.

Alcuni limiti non configurabili

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

NaN, Infinito, -Infinito

Newtonsoft analizza NaN, Infinitye -Infinity i token di stringa JSON. In .NET Core 3.1 non supporta questi token, System.Text.Json ma è possibile scrivere un convertitore personalizzato per gestirli. In .NET 5 e versioni successive usare JsonNumberHandling.AllowNamedFloatingPointLiterals. Per informazioni su come usare questa impostazione, vedere Consenti o scrivi numeri nelle virgolette.

Risorse aggiuntive