Obsługiwane typy w System.Text.Json

Ten artykuł zawiera omówienie typów obsługiwanych w przypadku serializacji i deserializacji.

Typy serializujące jako obiekty JSON

Następujące typy serializują się jako obiekty JSON:

  • Klasy*
  • Struktury
  • Interfejsów
  • Rekordy i rekordy struktury

* Typy inne niż słowniki, które implementują IEnumerable<T> serializować jako tablice JSON. Typy słowników, które implementują IEnumerable<T>, serializują jako obiekty JSON.

Poniższy fragment kodu przedstawia serializacji prostej struktury.

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

Typy, które serializują się jako tablice JSON

Typy kolekcji .NET serializują się jako tablice JSON. System.Text.Json.JsonSerializer obsługuje typ kolekcji dla serializacji, jeśli:

Serializator wywołuje metodę GetEnumerator() i zapisuje elementy.

Deserializacja jest bardziej skomplikowana i nie jest obsługiwana w przypadku niektórych typów kolekcji.

Poniższe sekcje są uporządkowane według przestrzeni nazw i pokazują, które typy są obsługiwane w przypadku serializacji i deserializacji.

Przestrzeń nazw System.Array

Typ Szeregowanie Deserializacja
Tablice jednowymiarowe* ✔️ ✔️
tablic wielowymiarowych ❌ ❌
tablice jagged ✔️ ✔️

* byte[] program jest obsługiwany specjalnie i serializuje jako ciąg base64, a nie tablicę JSON.

Przestrzeń nazw System.Collections

Typ Szeregowanie Deserializacja
ArrayList ✔️ ✔️
BitArray ✔️ ❌
DictionaryEntry ✔️ ✔️
Hashtable ✔️ ✔️
ICollection ✔️ ✔️
IDictionary ✔️ ✔️
IEnumerable ✔️ ✔️
IList ✔️ ✔️
Queue ✔️ ✔️
SortedList ✔️ ✔️
Stack * ✔️ ✔️

* Zobacz Support round trip for Stack types.

System.Collections.Generic, przestrzeń nazw

Typ Szeregowanie Deserializacja
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> ‹ ✔️ ✔️

* Zobacz Obsługiwane typy kluczy.

† Zobacz następującą sekcję w IAsyncEnumerable<T>.

‹ Zobacz Pomoc techniczna dla typów Stack.

Program § System.Text.Json obsługuje IReadOnlySet<T> w wersji .NET 11 i nowszych. Podczas deserializacji interfejsu serializator tworzy HashSet<T> wystąpienie. W przypadku wygenerowanych metadanych JsonMetadataServices.CreateIReadOnlySetInfo tworzy kontrakt kolekcji.

IAsyncEnumerable<T>

W poniższych przykładach strumienie są używane jako reprezentacja dowolnego asynchronicznego źródła danych. Źródłem mogą być pliki na komputerze lokalnym lub wyniki z zapytania bazy danych lub wywołania interfejsu API usługi internetowej.

Serializacja strumienia

System.Text.Json obsługuje serializowanie wartości IAsyncEnumerable<T> jako tablic JSON, jak pokazano w poniższym przykładzie:

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> wartości są obsługiwane tylko przez metody serializacji asynchronicznej, takie jak JsonSerializer.SerializeAsync.

W .NET 11 lub nowszych wersjach JsonSerializer.SerializeAsyncEnumerable zapisuje IAsyncEnumerable<T> sekwencję w Stream obiekcie lub PipeWriter. Przy użyciu domyślnej topLevelValues: falsemetody metoda zapisuje tablicę JSON na poziomie jednego głównego. Ustaw topLevelValues: true opcję zapisu wierszy JSON , gdzie każdy element jest oddzielną wartością najwyższego poziomu:

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

Metoda zapisuje jednowierszowe źródło danych (LF), \n, po każdej wartości, w tym ostatnie. Zawsze używa on LF, niezależnie od JsonSerializerOptions.NewLine. Metoda ignoruje JsonSerializerOptions.WriteIndentedwartość , więc każda wartość pozostaje w jednym wierszu.

Deserializacja strumienia

Metoda DeserializeAsyncEnumerable obsługuje deserializacji przesyłania strumieniowego, jak pokazano w poniższym przykładzie:

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

Domyślnie JsonSerializer.DeserializeAsyncEnumerable odczytuje elementy z pojedynczej tablicy JSON na poziomie głównym. Ustaw topLevelValues: true wartość , aby odczytać sekwencję wartości najwyższego poziomu rozdzielanych odstępami. Ten format wejściowy jest nadzbiorem linii JSON. Przeciążenia akceptują element Stream lub PipeReader.

Metoda DeserializeAsync obsługuje IAsyncEnumerable<T>, ale jej podpis nie zezwala na przesyłanie strumieniowe. Zwraca końcowy wynik jako pojedynczą wartość, jak pokazano w poniższym przykładzie.

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

W tym przykładzie deserializator buforuje całą zawartość IAsyncEnumerable<T> w pamięci przed zwróceniem zdeserializowanego obiektu. To zachowanie jest konieczne, ponieważ deserializator musi odczytać cały ładunek JSON przed zwróceniem wyniku.

System.Collections.Niezmienna przestrzeń nazw

Typ Szeregowanie Deserializacja
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> * ✔️ ✔️

* Zobacz Support round trip for Stack types.

† Zobacz Obsługiwane typy kluczy.

System.Collections.Wyspecjalizowana przestrzeń nazw

Typ Szeregowanie Deserializacja
BitVector32 ✔️ ❌*
HybridDictionary ✔️ ✔️
IOrderedDictionary ✔️ ❌
ListDictionary ✔️ ✔️
NameValueCollection ✔️ ❌
StringCollection ✔️ ❌
StringDictionary ✔️ ❌

* W przypadku deserializacji BitVector32 właściwość Data jest pomijana, ponieważ nie ma publicznego zestawu. Nie jest zgłaszany żaden wyjątek.

System.Collections.Concurrent, przestrzeń nazw

Typ Szeregowanie Deserializacja
BlockingCollection<T> ✔️ ❌
ConcurrentBag<T> ✔️ ❌
ConcurrentDictionary<TKey,TValue> † ✔️ ✔️
ConcurrentQueue<T> ✔️ ✔️
ConcurrentStack<T> * ✔️ ✔️

* Zobacz Support round trip for Stack types.

† Zobacz Obsługiwane typy kluczy.

Przestrzeń nazw System.Collections.ObjectModel

Typ Szeregowanie Deserializacja
Collection<T> ✔️ ✔️
ciąg KeyedCollection<, TValue> * ✔️ ❌
ObservableCollection<T> ✔️ ✔️
ReadOnlyCollection<T> ✔️ ❌
ReadOnlyDictionary<TKey,TValue> ✔️ ❌
ReadOnlyObservableCollection<T> ✔️ ❌

* Klucze inne niżstring nie są obsługiwane.

Kolekcje niestandardowe

Każdy typ kolekcji, który nie znajduje się w jednej z poprzednich przestrzeni nazw, jest uważany za kolekcję niestandardową. Takie typy obejmują typy zdefiniowane przez użytkownika i typy zdefiniowane przez ASP.NET Core. Na przykład Microsoft.Extensions.Primitives znajduje się w tej grupie.

Wszystkie kolekcje niestandardowe (wszystkie kolekcje pochodzące z IEnumerable) są obsługiwane do serializacji, o ile ich typy elementów są obsługiwane.

Obsługa deserializacji

Kolekcja niestandardowa jest obsługiwana w przypadku deserializacji, jeśli:

Znane problemy

Istnieją znane problemy z następującymi kolekcjami niestandardowymi:

Aby uzyskać więcej informacji na temat znanych problemów, zobacz otwarte problemy w programie System.Text.Json.

Obsługiwane typy kluczy

W przypadku użycia jako kluczy typów Dictionary i SortedList następujące typy mają wbudowaną obsługę:

  • BFloat16(.NET 11 i nowszych)
  • Boolean
  • Byte
  • DateTime
  • DateTimeOffset
  • Decimal
  • Decimal32(.NET 11 i nowszych)
  • Decimal64(.NET 11 i nowszych)
  • Decimal128(.NET 11 i nowszych)
  • Double
  • Enum
  • Guid
  • Int16
  • Int32
  • Int64
  • Object (tylko w przypadku serializacji i jeśli typ środowiska uruchomieniowego jest jednym z obsługiwanych typów na tej liście).
  • SByte
  • Single
  • String
  • TimeSpan
  • UInt16
  • UInt32
  • UInt64
  • Uri
  • Version

Ponadto metody JsonConverter<T>.WriteAsPropertyName(Utf8JsonWriter, T, JsonSerializerOptions) i JsonConverter<T>.ReadAsPropertyName(Utf8JsonReader, Type, JsonSerializerOptions) umożliwiają dodanie obsługi klucza słownika dla dowolnego typu wybranego typu.

Typy zmiennoprzecinkowe BFloat16 i dziesiętne

Począwszy od .NET 11, System.Text.Json zawiera wbudowane konwertery dla BFloat16typów , Decimal32, Decimal64i Decimal128 . Skończone wartości serializują się jako liczby JSON.

Te typy zachowują się jak inne wbudowane typy liczbowe:

JsonMetadataServices uwidacznia właściwości konwertera dla metadanych generowanych przez źródło. Właściwości to JsonMetadataServices.BFloat16Converter, , JsonMetadataServices.Decimal32ConverterJsonMetadataServices.Decimal64Converter, i JsonMetadataServices.Decimal128Converter.

Związki dyskryminowane w języku F#

Począwszy od .NET 11, System.Text.Json serializuje i deserializuje związki dyskryminujące F#, w tym związki klasowe, struktury i rekursywne:

type Shape =
    | Point
    | Circle of radius: float
  • Wielkość liter bez pól serializuje się jako ciąg JSON zawierający nazwę przypadku, na przykład "Point".
  • Przypadek z polami serializuje się jako obiekt JSON. Obiekt zawiera $type dyskryminujące, po którym następuje nazwane pola sprawy, takie jak {"$type":"Circle","radius":3.14}.

JsonSerializerOptions.PropertyNamingPolicy dotyczy nazw liter i nazw pól. Poziom JsonPropertyNameAttribute wielkości liter ma pierwszeństwo. Aby użyć nazwy właściwości dyskryminującej innej niż $type, ustaw .JsonPolymorphicAttribute.TypeDiscriminatorPropertyName

Important

Obsługa unii dyskryminowanej w języku F# jest tylko odbiciem. Wymaga kodu dynamicznego i metadanych nietrimmed odbicia. Nie można jej używać z generowaniem źródła ani natywną funkcją System.Text.Json AOT.

Nieobsługiwane typy

Następujące typy nie są obsługiwane w przypadku serializacji:

Przestrzeń nazw System.Data

Brak wbudowanych konwerterów dla DataSet, DataTablei powiązanych typów w przestrzeni nazw System.Data. Deserializowanie tych typów z niezaufanych danych wejściowych nie jest bezpieczne, jak wyjaśniono w wskazówki dotyczące zabezpieczeń. Można jednak napisać konwerter niestandardowy, aby obsługiwać te typy. Aby uzyskać przykładowy kod konwertera niestandardowego, który serializuje i deserializuje DataTable, zobacz RoundtripDataTable.cs.

Zobacz też