Compartilhar via


Migrar de Newtonsoft.Json para System.Text.Json

Este artigo mostra como migrar de Newtonsoft.Json para System.Text.Json.

O namespace System.Text.Json apresenta funcionalidade para serialização e desserialização do JSON (JavaScript Object Notation). A biblioteca System.Text.Json está incluída no runtime do .NET Core 3.1 e versões posteriores. Para outras estruturas de destino, instale o pacote NuGet System.Text.Json. O pacote é compatível com:

  • .NET Standard 2.0 e versões posteriores
  • .NET Framework 4.6.2 e versões posteriores
  • .NET Core 2.0, 2.1 e 2.2

Dica

Você pode usar a assistência de IA para migrar do Newtonsoft.Json com o GitHub Copilot.

System.Text.Json se concentra principalmente na conformidade de padrões, segurança e desempenho. Ele tem algumas diferenças importantes no comportamento padrão e não tem como objetivo ter paridade de recursos com Newtonsoft.Json. Para alguns cenários, atualmente System.Text.Json não tem nenhuma funcionalidade interna, mas há soluções alternativas recomendadas. Para outros cenários, soluções alternativas são impraticáveis.

A equipe System.Text.Json está investindo na adição dos recursos que são mais solicitados. Se seu aplicativo depende de um recurso em falta, considere arquivar um problema no repositório GitHub do dotnet/runtime para saber se o suporte de seu cenário pode ser adicionado.

Este artigo trata principalmente de como usar a API JsonSerializer, mas também inclui diretrizes de como usar o JsonDocument (que representa o Modelo de Objeto do Documento ou DOM) tipos Utf8JsonReadere Utf8JsonWriter.

No Visual Basic, você não pode usar Utf8JsonReader, o que também significa que não pode gravar conversores personalizados. A maioria das soluções alternativas apresentadas aqui exige que você grave conversores personalizados. Você pode gravar um conversor personalizado em C# e registrá-lo em um projeto do Visual Basic. Para saber mais, confira Suporte para Visual Basic.

Tabela de diferenças

A tabela a seguir lista os recursos Newtonsoft.Json e equivalentes System.Text.Json . Os equivalentes correspondem às seguintes categorias:

  • ✔️ Compatível com a funcionalidade integrada. Para obter um comportamento de System.Text.Json parecido, pode ser necessário o uso de um atributo ou de uma opção global.
  • ⚠️ Não é compatível. É possível uma solução alternativa. As soluções alternativas são conversores personalizados, que podem não dar paridade completa com a funcionalidade Newtonsoft.Json. Para alguns, o código de exemplo é fornecido como exemplos. Se você depender desses recursos Newtonsoft.Json, a migração exige modificações em seus modelos de objeto .NET ou outras alterações de código.
  • ❌ Não é compatível. A solução alternativa não é prática ou possível. Se você contar com esses recursos Newtonsoft.Json, a migração não pode ocorrer sem alterações significativas.
Recurso do Newtonsoft.Json Equivalente System.Text.Json
Desserialização que não diferencia maiúsculas de minúsculas por padrão ✔️ Configuração global PropertyNameCaseInsensitive
Nomes de propriedade em minúsculas concatenadas ✔️ Configuração global PropertyNamingPolicy
Nomes de propriedade em snake-case ✔️ Política de nomenclatura Snake case
Escape mínimo de caracteres ✔️ Escape de caractere estrito e configurável
configuração globalNullValueHandling.Ignore ✔️ Opção global DefaultIgnoreCondition
Permitir comentários ✔️ Configuração global ReadCommentHandling
Permitir vírgulas à direita ✔️ Configuração global AllowTrailingCommas
Registro de conversor personalizado ✔️ Diferença na ordem de precedência
Profundidade máxima padrão 64, configurável ✔️ Profundidade máxima padrão 64 configurável
configuração globalPreserveReferencesHandling ✔️ Configuração global ReferenceHandling
Serializar ou desserializar números entre aspas ✔️ Configuração global NumberHandling, atributo [JsonNumberHandling]
Desserializar para classes e structs imutáveis ✔️ Registros JsonConstructor, C# 9
Suporte para campos ✔️ Configuração global IncludeFields, atributo [JsonInclude]
configuração globalDefaultValueHandling ✔️ Configuração global DefaultIgnoreCondition
Configuração NullValueHandling em [JsonProperty] ✔️ Atributo JsonIgnore
Configuração DefaultValueHandling em [JsonProperty] ✔️ Atributo JsonIgnore
Desserializar Dictionary com chave sem cadeia de caracteres ✔️ Com suporte
Suporte para setters e getters de propriedades não públicas ✔️ Atributo JsonInclude
Atributo [JsonConstructor] ✔️ Atributo [JsonConstructor]
configuração globalReferenceLoopHandling ✔️ Configuração global ReferenceHandling
Retornos de chamada ✔️ Retornos de chamada
NaN, Infinity, -Infinity ✔️ Com suporte
Configuração Required no atributo [JsonProperty] ✔️ Atributo [JsonRequired] e modificador necessário do C#
DefaultContractResolver para ignorar propriedades ✔️ DefaultJsonTypeInfoResolver class
Serialização polimórfica ✔️ [JsonDerivedType] attribute
Desserialização polimórfica ✔️ Discriminador de tipo no atributo [JsonDerivedType]
Desserializar o valor de enumeração da cadeia de caracteres ✔️ Desserializar os valores de enumeração da cadeia de caracteres
configuração globalMissingMemberHandling ✔️ Lidar com os membros ausentes
Preencher propriedades sem setters ✔️ Preencher propriedades sem setters
configuração globalObjectCreationHandling ✔️ Reutilizar em vez de substituir propriedades
Suporte para uma ampla gama de tipos ⚠️ Alguns tipos exigem conversores personalizados
Desserializar o tipo inferido para as propriedades object ⚠️ Sem suporte, solução alternativa, amostra
Desserializar o literal JSON null para tipos de valor não anuláveis ⚠️ Sem suporte, solução alternativa, amostra
ConfiguraçõesDateTimeZoneHandling, DateFormatString ⚠️ Sem suporte, solução alternativa, amostra
Método JsonConvert.PopulateObject ⚠️ Sem suporte, solução alternativa
Suporte para atributos System.Runtime.Serialization ⚠️ Sem suporte, solução alternativa, amostra
JsonObjectAttribute ⚠️ Sem suporte, solução alternativa
Permitir nomes de propriedade sem aspas Não há suporte por design
Permitir aspas simples em torno de valores de cadeia de caracteres Não há suporte por design
Permitir valores JSON não string para propriedades string Não há suporte por design
configuração globalTypeNameHandling.All Não há suporte por design
Suporte para consultas JsonPath Sem suporte
Limites configuráveis Sem suporte

Esta não é uma lista exaustiva de recursos Newtonsoft.Json. A lista inclui muitos dos cenários que foram solicitados em problemas do GitHub ou postagens do StackOverflow . Se implementar uma solução alternativa para um dos cenários listados aqui que atualmente não tem código de exemplo e quiser compartilhar sua solução, selecione Esta página na seção Comentários na parte inferior desta página. Isso cria um problema no repositório GitHub desta documentação e também lista na seção Comentários desta página.

Diferenças no comportamento padrão

System.Text.Json é estrito por padrão e evita qualquer detecção ou interpretação em nome do chamador, enfatizando o comportamento determinístico. A biblioteca foi projetada intencionalmente dessa forma por conta do desempenho e segurança. Newtonsoft.Json é flexível por padrão. Essa diferença fundamental no design mostra muitas das seguintes diferenças específicas no comportamento padrão.

Desserialização que não diferencia maiúsculas de minúsculas

Durante a desserialização, Newtonsoft.Json gera um nome da propriedade que não diferencia maiúsculas de minúsculas corresponde por padrão. O System.Text.Json por padrão diferencia maiúsculas de minúsculas, o que oferece melhor desempenho, pois tem uma correspondência exata. Para obter informações sobre como fazer correspondência sem diferenciar maiúsculas de minúsculas, consulte correspondência de propriedades que não diferencia maiúsculas de minúsculas.

Se você estiver usando System.Text.Json indiretamente pelo ASP.NET Core, não precisa fazer nada para obter um comportamento como Newtonsoft.Json. O ASP.NET Core especifica as configurações para nomes de propriedade em minúsculas concatenadas e correspondências que não diferenciam maiúsculas de minúsculas ao usar System.Text.Json.

Por padrão, o ASP.NET Core também permite desserializar os números entre aspas.

Escape mínimo de caracteres

Durante a serialização, Newtonsoft.Json é relativamente permitido deixar os caracteres passarem sem escape. Ou seja, não os substitui por \uxxxx onde xxxx é o ponto de código do caractere. Quando há escape, ele emite um \ antes que o caractere (por exemplo, " se torne \"). System.Text.Json escapa mais caracteres por padrão para fornecer proteções de defesa em profundidade contra XSS (cross-site scripting) ou ataques de divulgação de informações e, para isso, usa a sequência de seis caracteres. Por padrão, System.Text.Json escapa todos os caracteres não ASCII e você não precisa executar nada se estiver usando StringEscapeHandling.EscapeNonAscii no Newtonsoft.Json. Por padrão, System.Text.Json também escapa caracteres sensíveis a HTML. Para obter informações sobre como substituir o comportamento padrão System.Text.Json, consulte Personalizar codificação de caracteres.

Comentários

Durante a desserialização, Newtonsoft.Json ignora os comentários no JSON por padrão. O System.Text.Json padrão é lançar exceções para comentários porque a especificação RFC 8259 não inclui exceções. Para obter informações sobre como permitir comentários, consulte Permitir comentários e vírgulas à direita.

Vírgulas à direita

Durante a desserialização, Newtonsoft.Json ignora vírgulas à direita por padrão. O código também ignora várias vírgulas à direita (por exemplo, [{"Color":"Red"},{"Color":"Green"},,]). O System.Text.Json padrão gera exceções para vírgulas à direita porque a especificação RFC 8259 não permite exceções. Para obter informações sobre como System.Text.Json aceita as exceções, consulte Permitir comentários e vírgulas à direita. Não há como permitir várias vírgulas à direita.

Precedência de registro do conversor

A precedência de registro de Newtonsoft.Json para conversores personalizados é:

  • Atributo na propriedade
  • Atributo no tipo
  • Coleção Conversores

Essa ordem significa que um conversor personalizado na coleção Converters é substituído por um conversor registrado aplicando um atributo no nível do tipo. Ambos os registros são substituídos por um atributo no nível da propriedade.

A precedência de registro de System.Text.Json para conversores personalizados é diferente:

  • Atributo na propriedade
  • Converters collection
  • Atributo no tipo

A diferença aqui é que um conversor personalizado na coleção Converters substitui um atributo no nível do tipo. A intenção dessa ordem de precedência é fazer com que as alterações no tempo de execução substituam as opções no tempo de design. Não há como alterar a precedência.

Para obter mais informações sobre o registro de conversor personalizado, consulte Registrar um conversor personalizado.

Profundidade máxima

Por padrão, a versão mais recente de Newtonsoft.Json tem um limite máximo de profundidade de 64. System.Text.Json também tem um limite padrão de 64 e pode ser configurado pela configuração JsonSerializerOptions.MaxDepth.

Se você estiver usando System.Text.Json indiretamente pelo ASP.NET Core, o limite máximo de profundidade padrão será 32. O valor padrão é igual para model binding e é definido na classe JsonOptions.

Cadeias de caracteres JSON (nomes de propriedade e valores de cadeia de caracteres)

Durante a desserialização, Newtonsoft.Json aceita nomes de propriedade com aspas duplas, aspas simples ou sem aspas. O código aceita valores de cadeia de caracteres com aspas duplas ou aspas simples. Por exemplo, Newtonsoft.Json aceita o seguinte JSON:

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

System.Text.Json aceita apenas nomes de propriedade e valores de cadeia de caracteres em aspas duplas porque esse formato é exigido pela especificação RFC 8259 e é o único formato considerado JSON válido.

Um valor entre aspas simples resulta em um JsonException com a seguinte mensagem:

''' is an invalid start of a value.

Valores não cadeia de caracteres para propriedades de cadeia de caracteres

Newtonsoft.Json aceita valores não cadeia de caracteres, como um número ou literais true e false, para desserialização para propriedades do tipo string. Veja aqui um exemplo de JSON que Newtonsoft.Json desserializa com êxito para a seguinte classe:

{
  "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 não desserializa valores não cadeia de caracteres em propriedades de cadeia de caracteres. Um valor não cadeia de caracteres recebido para um campo de cadeia de caracteres resulta em um JsonException com a seguinte mensagem:

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

Cenários usando JsonSerializer

Alguns dos cenários a seguir não são suportados pela funcionalidade integrada, mas são possíveis soluções alternativas. As soluções alternativas são conversores personalizados, que podem não oferecer paridade completa com a funcionalidade Newtonsoft.Json. Para alguns, o código de exemplo é fornecido como exemplos. Se você depender desses recursos Newtonsoft.Json, a migração exige modificações em seus modelos de objeto .NET ou outras alterações de código.

Para alguns dos cenários a seguir, soluções alternativas não são práticas ou possíveis. Se você contar com esses recursos Newtonsoft.Json, a migração não pode ocorrer sem alterações significativas.

Permitir ou gravar números entre aspas

Newtonsoft.Json pode serializar ou desserializar números representados por cadeias de caracteres JSON (com aspas). Por exemplo, ele pode aceitar: {"DegreesCelsius":"23"} em vez de {"DegreesCelsius":23}. Para habilitar esse comportamento em System.Text.Json, defina JsonSerializerOptions.NumberHandling para WriteAsString ou AllowReadingFromString, ou use o atributo [JsonNumberHandling].

Se você estiver usando System.Text.Json indiretamente pelo ASP.NET Core, não precisa fazer nada para obter um comportamento como Newtonsoft.Json. O ASP.NET Core especifica os padrões da Web quando usa System.Text.Json, e os padrões da Web permitem números entre aspas.

Para obter mais informações, consulte Permitir ou gravar números entre aspas.

Especificar construtor a ser usado na desserialização

O atributo Newtonsoft.Json[JsonConstructor] permite especificar qual construtor chamar para desserializar para um POCO.

System.Text.Json também tem um atributo [JsonConstructor]. Para obter mais informações, consulte Tipos imutáveis e Registros.

Ignorar condicionalmente uma propriedade

Newtonsoft.Json tem várias formas de ignorar condicionalmente uma propriedade em serialização ou desserialização:

  • DefaultContractResolver permite selecionar propriedades para incluir ou ignorar com base em critérios arbitrários.
  • As configurações NullValueHandling e DefaultValueHandling em JsonSerializerSettings permitem especificar que todas as propriedades de valor nulo ou valor padrão devem ser ignoradas.
  • As configurações NullValueHandling e DefaultValueHandling no atributo [JsonProperty] permitem especificar propriedades individuais que devem ser ignoradas quando definidas como valor nulo ou valor padrão.

System.Text.Json apresenta as seguintes formas de ignorar propriedades ou campos durante a serialização:

Além disso, no .NET 7 e em versões posteriores, você pode personalizar o contrato JSON para ignorar propriedades com base em critérios arbitrários. Para obter mais informações, consulte Contratos personalizados.

Campos públicos e não públicos

Newtonsoft.Json pode serializar e desserializar campos, além das propriedades.

No System.Text.Json, use a configuração global JsonSerializerOptions.IncludeFields ou o atributo [JsonInclude] para incluir campos públicos na serialização ou desserialização. Para obter um exemplo, consulte Incluir campos.

Preservar referências de objeto e loops de identificador

Por padrão, Newtonsoft.Json serializa por valor. Por exemplo, se um objeto contiver duas propriedades com uma referência ao mesmo objeto Person, os valores das propriedades do objeto Person serão duplicados no JSON.

Newtonsoft.Json tem uma configuração PreserveReferencesHandling no JsonSerializerSettings que permite serializar por referência:

  • Um metadados de identificador é adicionado ao JSON criado para o primeiro objeto Person.
  • O JSON foi criado para o segundo objeto Person que contém uma referência a esse identificador em vez de valores da propriedade.

Newtonsoft.Json também tem uma configuração ReferenceLoopHandling que permite ignorar referências circulares em vez de abrir uma exceção.

Para preservar referências e lidar com referências circulares em System.Text.Json, defina JsonSerializerOptions.ReferenceHandler como Preserve. A configuração ReferenceHandler.Preserve é equivalente a PreserveReferencesHandling = PreserveReferencesHandling.All em Newtonsoft.Json.

A opção ReferenceHandler.IgnoreCycles tem um comportamento semelhante a Newtonsoft.JsonReferenceLoopHandling.Ignore. Uma diferença é que a implementação System.Text.Json substitui loops de referência pelo token JSON null em vez de ignorar a referência de objeto. Para obter mais informações, consulte Ignorar referências circulares.

Assim como o Newtonsoft.JsonReferenceResolver, a classe System.Text.Json.Serialization.ReferenceResolver define o comportamento de preservar referências sobre serialização e desserialização. Crie uma classe derivada para especificar o comportamento personalizado. Para um exemplo, consulte GuidReferenceResolver.

Não há suporte para alguns recursos Newtonsoft.Json relacionados:

Para obter mais informações, consulte Preservar referências e lidar com referências circulares.

Dicionário sem chave de cadeia de caracteres

Ambos Newtonsoft.Json e System.Text.Json suportam coleções de tipo Dictionary<TKey, TValue>. Para obter informações sobre tipos de chave com suporte, consulte tipos de chave com suporte.

Cuidado

Desserializar para um Dictionary<TKey, TValue> local em que TKey é digitado como qualquer outra coisa que string não possa introduzir uma vulnerabilidade de segurança no aplicativo de consumo. Para obter mais informações, confira dotnet/runtime#4761.

Tipos sem suporte interno

System.Text.Json não oferece suporte interno para os seguintes tipos:

Conversores personalizados podem ser implementados para tipos que não têm suporte interno.

Serialização polimórfica

Newtonsoft.Json faz a serialização polimórfica automaticamente. A partir do .NET 7, System.Text.Json oferece suporte à serialização polimórfica por meio do atributo JsonDerivedTypeAttribute. Para obter mais informações, consulte Serializar propriedades de classes derivadas.

Desserialização polimórfica

Newtonsoft.Json tem uma configuração TypeNameHandling que adiciona metadados de nome de tipo ao JSON durante a serialização. Ele usa os metadados durante a desserialização para fazer a desserialização polimórfica. A partir do .NET 7, System.Text.Json depende de informações discriminadas de tipo para executar a desserialização polimórfica. Esses metadados são emitidos no JSON e, em seguida, usados durante a desserialização para determinar se deseja desserializar para o tipo base ou um tipo derivado. Para obter mais informações, consulte Serializar propriedades de classes derivadas.

Para oferecer suporte à desserialização polimórfica nas versões .NET anteriores, crie um conversor como o exemplo Como gravar conversores personalizados.

Desserializar os valores de enumeração da cadeia de caracteres

Por padrão, System.Text.Json não dá suporte para a desserialização de valores de enumeração de cadeia de caracteres, enquanto Newtonsoft.Json dá. Por exemplo, o código a seguir gera um JsonException:

string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";
var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.

class MyObj
{
    public string Text { get; set; } = "";
    public MyEnum Enum { get; set; }
}

enum MyEnum
{
    One,
    Two,
    Three
}

Entretanto, você pode habilitar a desserialização de valores de enumeração de cadeia de caracteres utilizando o conversor JsonStringEnumConverter. Para obter mais informações, consulte Enumerações como cadeia de caracteres.

Desserialização das propriedades do objeto

Quando Newtonsoft.Json desserializa para Object:

  • Infere o tipo de valores primitivos no conteúdo JSON (diferente de null) e retorna o string, long, double, boolean ou DateTime armazenado como um objeto em caixa. Valores primitivos são valores JSON únicos, como um número JSON, cadeia de caracteres, true, false ou null.
  • Retorna um JObject ou JArray para valores complexos no conteúdo JSON. Valores complexos são coleções de pares chave-valor JSON dentro de chaves ({}) ou listas de valores entre colchetes ([]). As propriedades e os valores dentro das chaves ou colchetes podem ter propriedades ou valores adicionais.
  • Retorna uma referência nula quando o conteúdo tem o literal JSON null.

System.Text.Json armazena um box para valores primitivos e complexos JsonElement sempre que desserializar para Object, por exemplo:

  • Uma propriedade object.
  • Um valor de dicionário object.
  • Um valor de matriz object.
  • Um object raiz.

No entanto, System.Text.Json trata null o mesmo que Newtonsoft.Json e retorna uma referência nula quando o conteúdo tem o literal JSON null.

Para implementar a inferência de tipo para propriedades object, crie um conversor como o exemplo em Como gravar conversores personalizados.

Desserializar o tipo nulo para não anulável

Newtonsoft.Json não gera uma exceção no seguinte cenário:

  • NullValueHandling é definido como Ignore, e
  • Durante a desserialização, o JSON contém um valor nulo para um tipo de valor não anulável.

No mesmo cenário, System.Text.Json gera uma exceção. (A configuração correspondente de tratamento nulo em System.Text.Json é JsonSerializerOptions.IgnoreNullValues = true.)

Se você possui o tipo de destino, a melhor solução alternativa é tornar essa propriedade anulável (por exemplo, alterar int para int?).

Outra solução alternativa é criar um conversor para o tipo, como o exemplo a seguir que lida com valores nulos para tipos DateTimeOffset:

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

namespace SystemTextJsonSamples
{
    public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
    {
        public override DateTimeOffset Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options) =>
            reader.TokenType == JsonTokenType.Null
                ? default
                : reader.GetDateTimeOffset();

        public override void Write(
            Utf8JsonWriter writer,
            DateTimeOffset dateTimeValue,
            JsonSerializerOptions options) =>
            writer.WriteStringValue(dateTimeValue);
    }
}

Registre esse conversor personalizado usando um atributo na propriedade ou adicionando o conversor à coleção Converters.

Nota: o conversor anterior manipula valores nulos de forma diferente do que Newtonsoft.Json para POCOs que especificam valores padrão. Por exemplo, suponha que o código a seguir represente seu objeto de destino:

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

E suponha que o JSON a seguir é desserializado usando o conversor anterior:

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

Após a desserialização, a propriedade Date tem 1/1/0001 (default(DateTimeOffset)) ou seja, o valor definido no construtor é substituído. Considerando o mesmo POCO e JSON, a desserialização Newtonsoft.Json deixaria 1/1/2001 na propriedade Date.

Desserializar para classes e structs imutáveis

Newtonsoft.Json pode desserializar para classes e structs imutáveis porque pode usar construtores que têm parâmetros.

No System.Text.Json, use o atributo [JsonConstructor] para especificar o uso de um construtor parametrizado. Os registros no C# 9 também são imutáveis e têm suporte como destinos de desserialização. Para obter mais informações, consulte Tipos imutáveis e Registros.

Propriedades obrigatórias

No Newtonsoft.Json, você especifica que uma propriedade é necessária pela configuração Required no atributo [JsonProperty]. Newtonsoft.Json gera uma exceção se nenhum valor for recebido no JSON para uma propriedade marcada como necessária.

A partir do .NET 7, você pode usar o modificador C# required ou o atributo JsonRequiredAttribute em uma propriedade necessária. System.Text.Json gerará uma exceção se o conteúdo JSON não contiver um valor para a propriedade marcada. Para obter mais informações, consulte Propriedades necessárias.

Especificar formato de data

Newtonsoft.Json oferece várias formas de controlar como as propriedades de tipos DateTime e DateTimeOffset são serializados e desserializados:

  • A configuração DateTimeZoneHandling pode ser usada para serializar todos os valores DateTime como datas UTC.
  • A configuração DateFormatString e os conversores DateTime podem ser usados para personalizar o formato das cadeias de caracteres de data.

System.Text.Json dá suporte à ISO 8601-1:2019, incluindo o perfil RFC 3339. Esse formato é amplamente adotado, inequívoco e faz viagens de ida e volta com precisão. Para usar qualquer outro formato, crie um conversor personalizado. Por exemplo, os conversores a seguir serializam e desserializam o JSON que usa o formato Unix epoch com ou sem um deslocamento de fuso horário (valores como /Date(1590863400000-0700)/ ou /Date(1590863400000)/):

sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
    static readonly DateTimeOffset s_epoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);

    public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
                || !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
                || !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
        {
            throw new JsonException();
        }

        int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
        TimeSpan utcOffset = new(hours * sign, minutes * sign, 0);

        return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
    }

    public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
        TimeSpan utcOffset = value.Offset;

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");

        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
    static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
        {
            throw new JsonException();
        }

        return s_epoch.AddMilliseconds(unixTime);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime})/");
        writer.WriteStringValue(formatted);
    }
}

Para obter mais informações, confira Suporte para DateTime e DateTimeOffset em System.Text.Json.

Retornos de chamada

Newtonsoft.Json permite executar código personalizado em vários pontos no processo de serialização ou desserialização:

  • OnDeserializing (ao começar a desserializar um objeto)
  • OnDeserializing (ao terminar de desserializar um objeto)
  • OnSerializing (ao começar a serializar um objeto)
  • OnSerializing (ao terminar de serializar um objeto)

System.Text.Json expõe as mesmas notificações durante a serialização e desserialização. Para usá-las, implemente uma ou mais das seguintes interfaces do namespace System.Text.Json.Serialization:

Aqui está um exemplo que verifica uma propriedade nula e grava mensagens no início e no final da serialização e desserialização:

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=

O código OnDeserializing não tem acesso à nova instância POCO. Para lidar com a nova instância POCO no início da desserialização, coloque esse código no construtor POCO.

Suporte para setters e getters de propriedades não públicas

Newtonsoft.Json pode usar setters e getters de propriedade privada e interna por meio do atributo JsonProperty.

System.Text.Json suporta setters e getters de propriedade privada e interna por meio do atributo [JsonInclude]. Para obter o código de exemplo, consulte Acessadores de propriedade não pública.

Preencher objetos existentes

O método JsonConvert.PopulateObject no Newtonsoft.Json desserializa um documento JSON para uma instância existente de uma classe, em vez de criar uma nova instância. System.Text.Json sempre cria uma nova instância do tipo de destino usando o construtor público sem parâmetros padrão. Conversores personalizados podem desserializar para uma instância existente.

Reutilizar em vez de substituir propriedades

A partir do .NET 8, System.Text.Json dá suporte à reutilização de propriedades inicializadas em vez de substituí-las. Há algumas diferenças de comportamento, sobre as quais você pode ler na proposta de API.

Para obter mais informações, consulte Popular propriedades inicializadas.

Preencher propriedades sem setters

A partir do .NET 8, System.Text.Json dá suporte à população de propriedades, incluindo aquelas que não têm um setter. Para obter mais informações, consulte Popular propriedades inicializadas.

Política de nomenclatura Snake case

System.Text.Json inclui uma política de nomenclatura interna para snake case. No entanto, há algumas diferenças de comportamento com Newtonsoft.Json para algumas entradas. A tabela a seguir mostra algumas dessas diferenças ao converter a entrada usando a política JsonNamingPolicy.SnakeCaseLower.

Entrada resultado Newtonsoft.Json resultado System.Text.Json
"AB1" "a_b1" "ab1"
"SHA512Managed" "sh_a512_managed" "sha512_managed"
"abc123DEF456" "abc123_de_f456" "abc123_def456"
"KEBAB-CASE" "keba_b-_case" "kebab-case"

Atributos System.Runtime.Serialization

System.Runtime.Serialization atributos como DataContractAttribute, DataMemberAttributee IgnoreDataMemberAttribute permitem que você defina um contrato de dados. Um contrato de dados é um contrato formal entre um serviço e um cliente que descreve abstratamente os dados a serem trocados. O contrato de dados define precisamente quais propriedades são serializadas para troca.

System.Text.Json não tem suporte interno para esses atributos. No entanto, a partir do .NET 7, você pode usar um resolvedor de tipo personalizado para adicionar suporte. Para obter um exemplo, confira ZCS. DataContractResolver.

Números octais

Newtonsoft.Json trata números com um zero à esquerda como números octais. System.Text.Json não permite zeros à esquerda porque não é aceita pela especificação RFC 8259.

Manipular membros ausentes

Se o JSON que está sendo desserializado incluir propriedades ausentes no tipo de destino, Newtonsoft.Json poderá ser configurado para gerar exceções. Por padrão, System.Text.Json ignora as propriedades extras no JSON, exceto quando você usa o atributo [JsonExtensionData].

No .NET 8 e em versões posteriores, você pode definir sua preferência para ignorar ou não permitir propriedades JSON desmapeadas utilizando um dos seguintes meios:

JsonObjectAttribute

Newtonsoft.Json tem um atributo, JsonObjectAttribute, que pode ser aplicado no nível do tipo para controlar quais membros são serializados, como os valores null são tratados e se todos os membros são necessários. System.Text.Json não tem nenhum atributo equivalente que possa ser aplicado a um tipo. Para alguns comportamentos, como manipulação de valor do null, você pode configurar o mesmo comportamento no JsonSerializerOptions global ou individualmente em cada propriedade.

Considere o seguinte exemplo que usa Newtonsoft.Json.JsonObjectAttribute para especificar que todas as propriedades null devem ser ignoradas:

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

No System.Text.Json, você pode definir o comportamento para todos os tipos e propriedades:

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

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

Ou você pode definir o comportamento em cada propriedade separadamente:

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

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

Em seguida, considere o seguinte exemplo que usa Newtonsoft.Json.JsonObjectAttribute para especificar que todas as propriedades de membro devem estar presentes no JSON:

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

Você pode obter o mesmo comportamento em System.Text.Json adicionando o modificador C# required ou o JsonRequiredAttributea cada propriedade. Para obter mais informações, consulte Propriedades necessárias.

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

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

TraceWriter

Newtonsoft.Json permite que você depurar usando um TraceWriter para exibir logs gerados por serialização ou desserialização. System.Text.Json não faz registro em log.

JsonDocument e JsonElement comparados ao JToken (como JObject, JArray)

System.Text.Json.JsonDocument oferece a capacidade de analisar e criar um DOM (Modelo de Objeto do Documento) somente leitura a partir de conteúdos JSON existentes. O DOM oferece acesso aleatório aos dados em um conteúdo JSON. Os elementos JSON que compõem o conteúdo podem ser acessados pelo tipo JsonElement. O tipo JsonElement fornece APIs para converter texto JSON em tipos comuns do .NET. JsonDocument mostra uma propriedade RootElement.

A partir do .NET 6, você pode analisar e criar um DOM mutável com conteúdos JSON existentes usando o tipo JsonNode e outros tipos no namespace System.Text.Json.Nodes. Para obter mais informações, consulte Usar JsonNode.

JsonDocument é IDisposable

JsonDocument cria uma exibição na memória dos dados em um buffer em pool. Portanto, diferente de JObject ou JArray de Newtonsoft.Json, o tipo JsonDocument implementa IDisposable e precisa ser usado dentro de um bloco de uso. Para obter mais informações, consulte JsonDocument é IDisposable.

JsonDocument é somente leitura

O DOM de System.Text.Json não pode adicionar, remover ou modificar elementos JSON. Ele foi projetado dessa forma para melhorar o desempenho e reduzir as alocações para analisar tamanhos comuns de conteúdo JSON (ou seja, < 1 MB).

JsonElement é um struct de união

JsonDocument expõe a RootElement como uma propriedade do tipo JsonElement, que é um tipo de struct de união que inclui qualquer elemento JSON. Newtonsoft.Json usa tipos hierárquicos dedicados, como JObject, JArray, JToken e assim por diante. JsonElement é o que você pode pesquisar e enumerar e pode usar JsonElement para materializar elementos JSON em tipos .NET.

A partir do .NET 6, você pode usar tipo JsonNode e tipos no namespace System.Text.Json.Nodes que correspondem a JObject, JArraye JToken. Para obter mais informações, consulte Usar JsonNode.

Como pesquisar um JsonDocument e JsonElement como subelementos

Pesquisa tokens JSON usando JObject ou JArray de Newtonsoft.Json costumam ser relativamente rápidos porque são pesquisas em algum dicionário. Em comparação, as pesquisas no JsonElement exigem uma pesquisa sequencial das propriedades e, portanto, são relativamente lentas (por exemplo, ao usar TryGetProperty). System.Text.Json foi criado para minimizar o tempo inicial de análise em vez do tempo de pesquisa. Para obter mais informações, consulte Como pesquisar um JsonDocument e JsonElement em busca de subelementos.

Utf8JsonReader vs. JsonTextReader

System.Text.Json.Utf8JsonReader é um leitor de alto desempenho, baixa alocação e somente para encaminhamento para texto JSON codificado em UTF-8, lido a partir de um ReadOnlySpan<byte> ou ReadOnlySequence<byte>. O Utf8JsonReader é um tipo de baixo nível que pode ser usado para criar analisadores e desserializadores personalizados.

Utf8JsonReader é um ref struct

O JsonTextReader no Newtonsoft.Json é uma classe. O tipo Utf8JsonReader difere porque é um ref struct. Para obter mais informações, confira limitações de struct ref para Utf8JsonReader.

Ler valores nulos em tipos de valor anulável

Newtonsoft.Json fornece APIs que retornam Nullable<T>, como ReadAsBoolean, que identifica um NullTokenType para você retornando um bool?. As APIs internas de System.Text.Json retornam apenas tipos de valor não anuláveis. Para obter mais informações, consulte Ler valores nulos em tipos de valor anulável.

Vários destinos para leitura de JSON

Se você precisa continuar usando o Newtonsoft.Json para determinadas estruturas de destino, é possível ter vários destinos e duas implementações. No entanto, não é comum e exigiria algumas #ifdefs e duplicação de origem. Uma forma de compartilhar o máximo de código possível é criar um wrapper ref struct próximo a Utf8JsonReader e Newtonsoft.Json.JsonTextReader. Esse wrapper unificaria a área de superfície pública ao isolar as diferenças comportamentais. Assim, é possível isolar as alterações principalmente na construção do tipo, junto com a aprovação do novo tipo por referência. Esse é o padrão que a biblioteca Microsoft.Extensions.DependencyModel segue:

Utf8JsonWriter vs. JsonTextWriter

System.Text.Json.Utf8JsonWriter é uma forma de alto desempenho para escrita de texto JSON codificado em UTF-8 com base em tipos .NET comuns como String, Int32 e DateTime. O gravador é um tipo de baixo nível que pode ser usado para criar serializadores personalizados.

Gravar valores brutos

Newtonsoft.Json tem um método WriteRawValue que grava JSON bruto onde um valor é esperado. System.Text.Json tem um equivalente direto: Utf8JsonWriter.WriteRawValue. Para obter mais informações, consulte Gravar JSON bruto.

Personalizar o formato JSON

JsonTextWriter inclui as seguintes configurações, para as quais Utf8JsonWriter não tem equivalente:

  • QuoteChar – especifica o caractere a ser usado para envolver os valores de cadeia de caracteres. Utf8JsonWriter sempre usa aspas duplas.
  • QuoteName – especifica se deve ou não envolver nomes de propriedades com aspas. Utf8JsonWriter sempre os cerca com aspas.

A partir do .NET 9, você pode personalizar o caractere e o tamanho de recuo Utf8JsonWriter usando as opções expostas pelo struct JsonWriterOptions:

JsonTextWriter inclui as seguintes configurações, para as quais Utf8JsonWriter não tem equivalente:

  • Recuo – especifica quantos caracteres devem ser recuados. Utf8JsonWriter sempre recuou por 2 caracteres.
  • IndentChar – especifica o caractere a ser usado para recuo. Utf8JsonWriter sempre usa espaço em branco.
  • QuoteChar – especifica o caractere a ser usado para envolver os valores de cadeia de caracteres. Utf8JsonWriter sempre usa aspas duplas.
  • QuoteName – especifica se deve ou não envolver nomes de propriedades com aspas. Utf8JsonWriter sempre os cerca com aspas.

Não há soluções alternativas que permitem personalizar o JSON produzido pelo Utf8JsonWriter dessa forma.

Gravar valores de Timespan, Uri ou char

JsonTextWriter fornece métodos WriteValue para valores de TimeSpan, Uri e char. Utf8JsonWriter não tem métodos equivalentes. Em vez disso, formate esses valores como cadeias de caracteres (com o nome ToString(), por exemplo) e chame WriteStringValue.

Vários destinos para gravação JSON

Se você precisa continuar usando o Newtonsoft.Json para determinadas estruturas de destino, é possível ter vários destinos e duas implementações. No entanto, não é comum e exigiria algumas #ifdefs e duplicação de origem. Uma forma de compartilhar o máximo de código possível é criar um wrapper circundando Utf8JsonWriter e Newtonsoft.Json.JsonTextWriter. Esse wrapper unificaria a área de superfície pública ao isolar as diferenças comportamentais. Isso permite isolar as alterações principalmente na construção do tipo. A biblioteca Microsoft.Extensions.DependencyModel é:

TypeNameHandling.All não tem suporte

A decisão de excluir a funcionalidade equivalente TypeNameHandling.All de System.Text.Json foi intencional. Permitir que um conteúdo JSON especifique suas próprias informações de tipo é uma fonte comum de vulnerabilidades em aplicativos Web. Em particular, a configuração de Newtonsoft.Json com TypeNameHandling.All permite que o cliente remoto insira um aplicativo executável inteiro dentro do próprio conteúdo JSON, de modo que, durante a desserialização, o aplicativo Web extraia e executa o código inserido. Para obter mais informações, consulte Friday the 13th JSON attacks PowerPoint e Friday the 13th JSON attacks details.

Consultas de caminho JSON sem suporte

O DOM de JsonDocument não dá suporte à consulta usando o Caminho JSON.

Em um DOM de JsonNode, cada instância JsonNode tem um método GetPath que retorna um caminho para esse nó. Mas não há nenhuma API interna para lidar com consultas com base em cadeias de consulta de caminho JSON.

Para obter mais informações, consulte o problema do GitHub dotnet/runtime #31068.

Alguns limites não configuráveis

System.Text.Json define limites que não podem ser alterados para alguns valores, como o tamanho máximo do token em caracteres (166 MB) e na base 64 (125 MB). Para obter mais informações, consulte JsonConstants no código-fonte e o problema do GitHub dotnet/runtime #39953.

NaN, Infinity, -Infinity

Newtonsoft analisa tokens de string JSON NaN, Infinity e -Infinity. Com System.Text.Json, use JsonNumberHandling.AllowNamedFloatingPointLiterals. Para obter informações sobre como usar essa configuração, consulte Permitir ou gravar números entre aspas.

Usar o GitHub Copilot para migrar

Você pode obter ajuda de codificação do GitHub Copilot para converter seu código no IDE.

Se você estiver usando o Visual Studio 2022 versão 17.8 ou posterior, poderá experimentar o GitHub Copilot no Visual Studio orientado por IA para ajudar a migrar seu código Newtonsoft.Json para usar o System.Text.Json. Envie sua pergunta e o código a ser convertido como um prompt na janela de bate-papo do Copilot, como no exemplo a seguir.

Observação

O GitHub Copilot é alimentado por IA, portanto é passível de surpresas e erros. Verifique qualquer código gerado ou sugestões. Para obter mais informações sobre o uso geral do GitHub Copilot, o impacto do produto, a supervisão humana e a privacidade, consulte Perguntas frequentes sobre o GitHub Copilot.

Exemplo de solicitação para o Chat do Copilot

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

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

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

Exemplo de resposta do Chat do Copilot

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

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

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

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

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

Observe que seus resultados podem ser diferentes do que é mostrado nessas respostas de exemplo. Os modelos de IA não são determinísticos, ou seja, eles podem retornar respostas diferentes quando fazem a mesma pergunta. Isso pode ser devido a aprendizado e adaptação adicionais ao longo do tempo, variação de idioma, mudanças no contexto, como seu histórico de bate-papo e muito mais.

Captura de tela animada que mostra o uso do Chat do GitHub Copilot no Visual Studio para migrar do newtonsoft

Você pode usar recursos de bate-papo, como comandos de barra, referências e threads, para definir a intenção e obter melhores respostas com o contexto com escopo. Por exemplo, se o arquivo de código filename estiver aberto no IDE, você poderá fazer referência ao arquivo no prompt do Chat do Copilot com "converter #filename para usar System.Text.Json". Ou você pode fazer referência à solução com "converter @workspace para usar System.Text.Json" na janela de chat ou no chat embutido.

Recursos adicionais