Tipi supportati in System.Text.Json

Questo articolo offre una panoramica dei tipi supportati per la serializzazione e la deserializzazione.

Tipi serializzati come oggetti JSON

I tipi seguenti vengono serializzati come oggetti JSON:

  • * classi
  • Struct
  • Interfacce
  • Record e record struct

* Tipi non di dizionario che implementano IEnumerable<T> serializzare come matrici JSON. Tipi di dizionario, che implementano IEnumerable<T>, serializzare come oggetti JSON.

Il frammento di codice seguente illustra la serializzazione di uno struct semplice.

public static void Main()
{
    var coordinates = new Coords(1.0, 2.0);
    string json = JsonSerializer.Serialize(coordinates);
    Console.WriteLine(json);

    // Output:
    // {"X":1,"Y":2}
}

public readonly struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; }
    public double Y { get; }
}

Tipi serializzati come matrici JSON

I tipi di raccolta .NET vengono serializzati come matrici JSON. System.Text.Json.JsonSerializer supporta un tipo di raccolta per la serializzazione, se:

Il serializzatore chiama il metodo GetEnumerator() e scrive gli elementi.

La deserializzazione è più complessa e non è supportata per alcuni tipi di raccolta.

Le sezioni seguenti sono organizzate in base allo spazio dei nomi e mostrano quali tipi sono supportati per la serializzazione e la deserializzazione.

Spazio dei nomi System.Array

Digitare Serializzazione Deserializzazione
Matrici unidimensionali* ✔️ ✔️
matrici multidimensionali ❌ ❌
matrici frastagliati ✔️ ✔️

* byte[] viene gestito appositamente e serializza come stringa base64, non come matrice JSON.

Spazio dei nomi System.Collections

Digitare Serializzazione Deserializzazione
ArrayList ✔️ ✔️
BitArray ✔️ ❌
DictionaryEntry ✔️ ✔️
Hashtable ✔️ ✔️
ICollection ✔️ ✔️
IDictionary ✔️ ✔️
IEnumerable ✔️ ✔️
IList ✔️ ✔️
Queue ✔️ ✔️
SortedList ✔️ ✔️
Stack * ✔️ ✔️

* Per i tipi di , vedere round trip di supporto .

Spazio dei nomi System.Collections.Generic

Digitare Serializzazione Deserializzazione
Dictionary<TKey,TValue> * ✔️ ✔️
HashSet<T> ✔️ ✔️
IAsyncEnumerable<T> † ✔️ ✔️
ICollection<T> ✔️ ✔️
IDictionary<TKey,TValue> * ✔️ ✔️
IEnumerable<T> ✔️ ✔️
IList<T> ✔️ ✔️
IReadOnlyCollection<T> ✔️ ✔️
IReadOnlyDictionary<TKey,TValue> * ✔️ ✔️
IReadOnlyList<T> ✔️ ✔️
IReadOnlySet<T> § ✔️ ✔️
ISet<T> ✔️ ✔️
KeyValuePair<TKey,TValue> ✔️ ✔️
LinkedList<T> ✔️ ✔️
LinkedListNode<T> ✔️ ❌
List<T> ✔️ ✔️
Queue<T> ✔️ ✔️
SortedDictionary<TKey,TValue> * ✔️ ✔️
SortedList<TKey,TValue> * ✔️ ✔️
SortedSet<T> ✔️ ✔️
Stack<T> * ✔️ ✔️

* Vedere tipi di chiave supportati.

† Vedere la sezione seguente in IAsyncEnumerable<T>.

* Vedere round trip supporto per Stack tipi.

§ System.Text.Json supporta IReadOnlySet<T> in .NET 11 e versioni successive. Quando si deserializza l'interfaccia, il serializzatore crea un'istanza HashSet<T> di . Per i metadati generati, JsonMetadataServices.CreateIReadOnlySetInfo crea il contratto di raccolta.

IAsyncEnumerable<T>

Negli esempi seguenti vengono usati flussi come rappresentazione di qualsiasi origine dati asincrona. L'origine può essere file in un computer locale o risultati da una query di database o una chiamata API del servizio Web.

Serializzazione dei flussi

System.Text.Json supporta la serializzazione dei valori IAsyncEnumerable<T> come matrici JSON, come illustrato nell'esempio seguente:

using System.Text.Json;

namespace IAsyncEnumerableSerialize;

public class Program
{
    public static async Task Main()
    {
        using Stream stream = Console.OpenStandardOutput();
        var data = new { Data = PrintNumbers(3) };
        await JsonSerializer.SerializeAsync(stream, data);
    }

    static async IAsyncEnumerable<int> PrintNumbers(int n)
    {
        for (int i = 0; i < n; i++)
        {
            await Task.Delay(1000);
            yield return i;
        }
    }
}
// output:
//  {"Data":[0,1,2]}

IAsyncEnumerable<T> valori sono supportati solo dai metodi di serializzazione asincroni, ad esempio JsonSerializer.SerializeAsync.

In .NET 11 e versioni successive, JsonSerializer.SerializeAsyncEnumerable scrive una IAsyncEnumerable<T> sequenza in un Stream oggetto o in .PipeWriter Con il valore predefinito topLevelValues: false, il metodo scrive una singola matrice JSON a livello radice. Impostare topLevelValues: true invece per scrivere righe JSON , dove ogni elemento è un valore di primo livello separato:

{"id":1,"name":"apple"}
{"id":2,"name":"banana"}

Il metodo scrive un singolo feed di riga (LF), \n, dopo ogni valore, incluso l'ultimo. Usa sempre LF, indipendentemente da JsonSerializerOptions.NewLine. Il metodo ignora JsonSerializerOptions.WriteIndented, quindi ogni valore rimane su una riga.

Deserializzazione dei flussi

Il metodo DeserializeAsyncEnumerable supporta la deserializzazione di streaming, come illustrato nell'esempio seguente:

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

namespace IAsyncEnumerableDeserialize;

public class Program
{
    public static async Task Main()
    {
        using var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));
        await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable<int>(stream))
        {
            Console.WriteLine(item);
        }
    }
}
// output:
//0
//1
//2
//3
//4

Per impostazione predefinita, JsonSerializer.DeserializeAsyncEnumerable legge gli elementi da una singola matrice JSON a livello radice. Impostare topLevelValues: true per leggere una sequenza di valori di primo livello separati da spazi vuoti. Questo formato di input è un superset di righe JSON. Gli overload accettano un oggetto Stream o un oggetto PipeReader.

Il metodo DeserializeAsync supporta IAsyncEnumerable<T>, ma la firma non consente lo streaming. Restituisce il risultato finale come singolo valore, come illustrato nell'esempio seguente.

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

namespace IAsyncEnumerableDeserializeNonStreaming;

public class MyPoco
{
    public IAsyncEnumerable<int>? Data { get; set; }
}

public class Program
{
    public static async Task Main()
    {
        using var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{""Data"":[0,1,2,3,4]}"));
        MyPoco? result = await JsonSerializer.DeserializeAsync<MyPoco>(stream)!;
        await foreach (int item in result!.Data!)
        {
            Console.WriteLine(item);
        }
    }
}
// output:
//0
//1
//2
//3
//4

In questo esempio, il deserializzatore memorizza tutti i IAsyncEnumerable<T> contenuto in memoria prima di restituire l'oggetto deserializzato. Questo comportamento è necessario perché il deserializzatore deve leggere l'intero payload JSON prima di restituire un risultato.

Spazio dei nomi System.Collections.Immutable

Digitare Serializzazione Deserializzazione
IImmutableDictionary<TKey,TValue> † ✔️ ✔️
IImmutableList<T> ✔️ ✔️
IImmutableQueue<T> ✔️ ✔️
IImmutableSet<T> ✔️ ✔️
IImmutableStack<T> * ✔️ ✔️
ImmutableArray<T> ✔️ ✔️
ImmutableDictionary<TKey,TValue> † ✔️ ✔️
ImmutableHashSet<T> ✔️ ✔️
ImmutableQueue<T> ✔️ ✔️
ImmutableSortedDictionary<TKey,TValue> † ✔️ ✔️
ImmutableSortedSet<T> ✔️ ✔️
ImmutableStack<T> * ✔️ ✔️

* Per i tipi di , vedere round trip di supporto .

† Vedere Tipi di chiave supportati.

Spazio dei nomi System.Collections.Specialized

Digitare Serializzazione Deserializzazione
BitVector32 ✔️ ❌*
HybridDictionary ✔️ ✔️
IOrderedDictionary ✔️ ❌
ListDictionary ✔️ ✔️
NameValueCollection ✔️ ❌
StringCollection ✔️ ❌
StringDictionary ✔️ ❌

* Quando BitVector32 viene deserializzato, la proprietà Data viene ignorata perché non ha un setter pubblico. Non viene generata alcuna eccezione.

Spazio dei nomi System.Collections.Concurrent

Digitare Serializzazione Deserializzazione
BlockingCollection<T> ✔️ ❌
ConcurrentBag<T> ✔️ ❌
ConcurrentDictionary<TKey,TValue> † ✔️ ✔️
ConcurrentQueue<T> ✔️ ✔️
ConcurrentStack<T> * ✔️ ✔️

* Per i tipi di , vedere round trip di supporto .

† Vedere Tipi di chiave supportati.

Spazio dei nomi System.Collections.ObjectModel

Digitare Serializzazione Deserializzazione
Collection<T> ✔️ ✔️
stringa<KeyedCollection,> * TValue ✔️ ❌
ObservableCollection<T> ✔️ ✔️
ReadOnlyCollection<T> ✔️ ❌
ReadOnlyDictionary<TKey,TValue> ✔️ ❌
ReadOnlyObservableCollection<T> ✔️ ❌

* Le chiavi nonstring non sono supportate.

Raccolte personalizzate

Qualsiasi tipo di raccolta che non si trova in uno degli spazi dei nomi precedenti viene considerato una raccolta personalizzata. Tali tipi includono tipi e tipi definiti dall'utente definiti da ASP.NET Core. Ad esempio, Microsoft.Extensions.Primitives si trova in questo gruppo.

Tutte le raccolte personalizzate (tutto ciò che deriva da IEnumerable) sono supportate per la serializzazione, purché siano supportati i relativi tipi di elemento.

Supporto della deserializzazione

Una raccolta personalizzata è supportata per la deserializzazione, se:

Problemi noti

Esistono problemi noti con le raccolte personalizzate seguenti:

Per altre informazioni sui problemi noti, vedere i problemi aperti di in System.Text.Json.

Tipi di chiave supportati

Se usato come chiavi di Dictionary e tipi di SortedList, i tipi seguenti hanno il supporto predefinito:

  • BFloat16(.NET 11 e versioni successive)
  • Boolean
  • Byte
  • DateTime
  • DateTimeOffset
  • Decimal
  • Decimal32(.NET 11 e versioni successive)
  • Decimal64(.NET 11 e versioni successive)
  • Decimal128(.NET 11 e versioni successive)
  • Double
  • Enum
  • Guid
  • Int16
  • Int32
  • Int64
  • Object (solo in caso di serializzazione e se il tipo di runtime è uno dei tipi supportati in questo elenco).
  • SByte
  • Single
  • String
  • TimeSpan
  • UInt16
  • UInt32
  • UInt64
  • Uri
  • Version

Inoltre, i metodi JsonConverter<T>.WriteAsPropertyName(Utf8JsonWriter, T, JsonSerializerOptions) e JsonConverter<T>.ReadAsPropertyName(Utf8JsonReader, Type, JsonSerializerOptions) consentono di aggiungere il supporto della chiave del dizionario per qualsiasi tipo di scelta.

Tipi A virgola mobile BFloat16 e decimali

A partire da .NET 11, System.Text.Json include convertitori predefiniti per i BFloat16tipi , Decimal32Decimal64, e Decimal128 . I valori finiti vengono serializzati come numeri JSON.

Questi tipi si comportano come gli altri tipi numerici predefiniti:

JsonMetadataServices espone le proprietà del convertitore per i metadati generati dall'origine. Tali proprietà sono JsonMetadataServices.BFloat16Converter, JsonMetadataServices.Decimal32Converter, JsonMetadataServices.Decimal64Converter, e JsonMetadataServices.Decimal128Converter.

Unioni discriminate F#

A partire da .NET 11, System.Text.Json serializza e deserializza unioni discriminate F#, tra cui classi, struct e unioni ricorsive:

type Shape =
    | Point
    | Circle of radius: float
  • Un caso senza campi serializza come stringa JSON che contiene il nome del case, ad esempio "Point".
  • Caso con campi serializzati come oggetto JSON. L'oggetto contiene un $type discriminare seguito dai campi denominati del case, ad esempio {"$type":"Circle","radius":3.14}.

JsonSerializerOptions.PropertyNamingPolicy si applica ai nomi di maiuscole e minuscole e ai nomi dei campi. Un livello JsonPropertyNameAttribute di maiuscole e minuscole ha la precedenza. Per usare un nome di proprietà discriminatorio diverso da $type, impostare JsonPolymorphicAttribute.TypeDiscriminatorPropertyName.

Importante

Il supporto dell'unione discriminata F# è di sola reflection. Richiede codice dinamico e metadati di reflection non verificati. Non è possibile usarlo con System.Text.Json la generazione di origine o native AOT.

Tipi non supportati

I tipi seguenti non sono supportati per la serializzazione:

Spazio dei nomi System.Data

Non sono disponibili convertitori predefiniti per DataSet, DataTablee i tipi correlati nello spazio dei nomi System.Data. La deserializzazione di questi tipi da un input non attendibile non è sicura, come illustrato in le linee guida per la sicurezza. Tuttavia, è possibile scrivere un convertitore personalizzato per supportare questi tipi. Per il codice del convertitore personalizzato di esempio che serializza e deserializza un DataTable, vedere RoundtripDataTable.cs.

Vedere anche