Tipos com suporte no System.Text.Json

Este artigo fornece uma visão geral de quais tipos têm suporte para serialização e desserialização.

Tipos que serializam como objetos JSON

Os seguintes tipos são serializados como objetos JSON:

  • Classes*
  • Structs
  • Interfaces
  • Registros e registros de struct

* Tipos que não são de dicionário que implementam IEnumerable<T> serializar como matrizes JSON. Tipos de dicionário, que implementam IEnumerable<T>, serializam como objetos JSON.

O snippet de código a seguir mostra a serialização de um struct simples.

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

Tipos que serializam como matrizes JSON

Os tipos de coleção .NET são serializados como matrizes JSON. System.Text.Json.JsonSerializer dá suporte a um tipo de coleção para serialização se ele:

O serializador chama o método GetEnumerator() e grava os elementos.

A desserialização é mais complicada e não tem suporte para alguns tipos de coleção.

As seções a seguir são organizadas por namespace e mostram quais tipos têm suporte para serialização e desserialização.

Namespace System.Array

Tipo Serialização Desserialização
Matrizes unidimensionais* ✔️ ✔️
matrizes multidimensionais ❌ ❌
matrizes Jagged ✔️ ✔️

* byte[] é tratado especialmente e serializa como uma cadeia de caracteres base64, não uma matriz JSON.

Namespace System.Collections

Tipo Serialização Desserialização
ArrayList ✔️ ✔️
BitArray ✔️ ❌
DictionaryEntry ✔️ ✔️
Hashtable ✔️ ✔️
ICollection ✔️ ✔️
IDictionary ✔️ ✔️
IEnumerable ✔️ ✔️
IList ✔️ ✔️
Queue ✔️ ✔️
SortedList ✔️ ✔️
Stack * ✔️ ✔️

* Consulte suporte de ida e volta para tipos de Stack.

Namespace System.Collections.Generic

Tipo Serialização Desserialização
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> ‡ ✔️ ✔️

* Consulte tipos de chave com suporte.

† Consulte a seção a seguir no IAsyncEnumerable<T>.

‡ Consulte Suporte de ida e volta para tipos de Stack.

§ dá System.Text.Json suporte em versões IReadOnlySet<T> .NET 11 e posteriores. Quando você desserializa a interface, o serializador cria uma HashSet<T> instância. Para metadados gerados, JsonMetadataServices.CreateIReadOnlySetInfo cria o contrato de coleção.

IAsyncEnumerable<T>

Os exemplos a seguir usam fluxos como uma representação de qualquer fonte assíncrona de dados. A origem pode ser arquivos em um computador local ou resultados de uma consulta de banco de dados ou chamada à API do serviço Web.

Serialização de fluxo

System.Text.Json dá suporte à serialização de valores IAsyncEnumerable<T> como matrizes JSON, conforme mostrado no exemplo a seguir:

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> valores têm suporte apenas pelos métodos de serialização assíncronos, como JsonSerializer.SerializeAsync.

Em .NET 11 e versões posteriores, JsonSerializer.SerializeAsyncEnumerable grava uma IAsyncEnumerable<T> sequência em um Stream ou um PipeWriter. Com o padrão topLevelValues: false, o método grava uma única matriz JSON de nível raiz. Em vez disso, defina topLevelValues: true para gravar linhas JSON , em que cada elemento é um valor de nível superior separado:

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

O método grava um único LF (feed de linha), \napós cada valor, incluindo o último. Ele sempre usa LF, independentemente de JsonSerializerOptions.NewLine. O método ignora JsonSerializerOptions.WriteIndented, portanto, cada valor permanece em uma linha.

Desserialização de fluxo

O método DeserializeAsyncEnumerable dá suporte à desserialização de streaming, conforme mostrado no exemplo a seguir:

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

Por padrão, JsonSerializer.DeserializeAsyncEnumerable lê elementos de uma única matriz JSON de nível raiz. Defina topLevelValues: true para ler uma sequência de valores de nível superior separados pelo espaço em branco. Esse formato de entrada é um superconjunto de Linhas JSON. As sobrecargas aceitam um Stream ou um PipeReader.

O método DeserializeAsync dá suporte a IAsyncEnumerable<T>, mas sua assinatura não permite streaming. Ele retorna o resultado final como um único valor, conforme mostrado no exemplo a seguir.

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

Neste exemplo, o desserializador armazena todos os buffers IAsyncEnumerable<T> conteúdo na memória antes de retornar o objeto desserializado. Esse comportamento é necessário porque o desserializador precisa ler todo o conteúdo JSON antes de retornar um resultado.

Namespace System.Collections.Immutable

Tipo Serialização Desserialização
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> * ✔️ ✔️

* Consulte suporte de ida e volta para tipos de Stack.

† Consulte tipos de chave com suporte.

Namespace System.Collections.Specialized

Tipo Serialização Desserialização
BitVector32 ✔️ ❌*
HybridDictionary ✔️ ✔️
IOrderedDictionary ✔️ ❌
ListDictionary ✔️ ✔️
NameValueCollection ✔️ ❌
StringCollection ✔️ ❌
StringDictionary ✔️ ❌

* Quando BitVector32 é desserializada, a propriedade Data é ignorada porque não tem um setter público. Nenhuma exceção é gerada.

Namespace System.Collections.Concurrent

Tipo Serialização Desserialização
BlockingCollection<T> ✔️ ❌
ConcurrentBag<T> ✔️ ❌
ConcurrentDictionary<TKey,TValue> † ✔️ ✔️
ConcurrentQueue<T> ✔️ ✔️
ConcurrentStack<T> * ✔️ ✔️

* Consulte suporte de ida e volta para tipos de Stack.

† Consulte tipos de chave com suporte.

Namespace System.Collections.ObjectModel

Tipo Serialização Desserialização
Collection<T> ✔️ ✔️
cadeia de<KeyedCollection,> * TValue ✔️ ❌
ObservableCollection<T> ✔️ ✔️
ReadOnlyCollection<T> ✔️ ❌
ReadOnlyDictionary<TKey,TValue> ✔️ ❌
ReadOnlyObservableCollection<T> ✔️ ❌

* Não há suporte para chaves nãostring.

Coleções personalizadas

Qualquer tipo de coleção que não esteja em um dos namespaces anteriores é considerado uma coleção personalizada. Esses tipos incluem tipos e tipos definidos pelo usuário definidos pelo ASP.NET Core. Por exemplo, Microsoft.Extensions.Primitives está nesse grupo.

Todas as coleções personalizadas (tudo o que deriva de IEnumerable) têm suporte para serialização, desde que haja suporte para seus tipos de elementos.

Suporte à desserialização

Uma coleção personalizada terá suporte para desserialização se ela:

Problemas conhecidos

Há problemas conhecidos com as seguintes coleções personalizadas:

Para obter mais informações sobre problemas conhecidos, consulte o problemas abertos no System.Text.Json.

Tipos de chave com suporte

Quando usadas como chaves de tipos Dictionary e SortedList, os seguintes tipos têm suporte interno:

  • BFloat16(.NET 11 e posterior)
  • Boolean
  • Byte
  • DateTime
  • DateTimeOffset
  • Decimal
  • Decimal32(.NET 11 e posterior)
  • Decimal64(.NET 11 e posterior)
  • Decimal128(.NET 11 e posterior)
  • Double
  • Enum
  • Guid
  • Int16
  • Int32
  • Int64
  • Object (somente na serialização e se o tipo de runtime for um dos tipos com suporte nesta lista).)
  • SByte
  • Single
  • String
  • TimeSpan
  • UInt16
  • UInt32
  • UInt64
  • Uri
  • Version

Além disso, os métodos JsonConverter<T>.WriteAsPropertyName(Utf8JsonWriter, T, JsonSerializerOptions) e JsonConverter<T>.ReadAsPropertyName(Utf8JsonReader, Type, JsonSerializerOptions) permitem adicionar suporte à chave de dicionário para qualquer tipo de sua escolha.

Tipos de ponto flutuante BFloat16 e decimal

A partir do .NET 11, System.Text.Json inclui conversores internos para os BFloat16tipos , e Decimal64Decimal32Decimal128 . Valores finitos serializam como números JSON.

Esses tipos se comportam como os outros tipos numéricos internos:

JsonMetadataServices expõe as propriedades do conversor para metadados gerados pela origem. As propriedades são JsonMetadataServices.BFloat16Converter, JsonMetadataServices.Decimal32Converter, JsonMetadataServices.Decimal64Converter e JsonMetadataServices.Decimal128Converter.

Uniões discriminadas em F#

A partir do .NET 11, System.Text.Json serializa e desserializa uniões discriminadas do F#, incluindo uniões de classe, struct e recursivas:

type Shape =
    | Point
    | Circle of radius: float
  • Um caso sem campos é serializado como uma cadeia de caracteres JSON que contém o nome da caixa, como "Point".
  • Um caso que tem campos serializa como um objeto JSON. O objeto contém um $type discriminatório seguido pelos campos nomeados do caso, como {"$type":"Circle","radius":3.14}.

JsonSerializerOptions.PropertyNamingPolicy aplica-se a nomes de maiúsculas e minúsculas. Um nível JsonPropertyNameAttribute de caso tem precedência. Para usar um nome de propriedade discriminatório diferente de $type, defina JsonPolymorphicAttribute.TypeDiscriminatorPropertyName.

Importante

O apoio sindical discriminado em F# é somente reflexão. Ele requer código dinâmico e metadados de reflexão untrimmed. Você não pode usá-lo com System.Text.Json geração de origem ou AOT nativo.

Tipos sem suporte

Não há suporte para os seguintes tipos para serialização:

Namespace System.Data

Não há conversores internos para DataSet, DataTablee tipos relacionados no namespace System.Data. Desserializar esses tipos de entrada não confiável não é seguro, conforme explicado em as diretrizes de segurança. No entanto, você pode escrever um conversor personalizado para dar suporte a esses tipos. Para obter um código de conversor personalizado de exemplo que serializa e desserializa um DataTable, consulte RoundtripDataTable.cs.

Consulte também