Partilhar via


Migrar de Newtonsoft.Json para System.Text.Json

Este artigo mostra como migrar do Newtonsoft.Json .System.Text.Json

O System.Text.Json namespace fornece funcionalidade para serializar e desserializar a partir de JavaScript Object Notation (JSON). A System.Text.Json biblioteca está incluída no tempo de execução do .NET Core 3.1 e versões posteriores. Para outras estruturas de destino, instale o System.Text.Json pacote NuGet. O pacote suporta:

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

Gorjeta

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

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

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

A maior parte deste artigo é sobre como usar a JsonSerializer API, mas também inclui orientações sobre como usar o JsonDocument (que representa o Document Object Model ou DOM) Utf8JsonReadere Utf8JsonWriter tipos.

No Visual Basic, você não pode usar Utf8JsonReader, o que também significa que você não pode escrever conversores personalizados. A maioria das soluções alternativas apresentadas aqui requer que você escreva conversores personalizados. Você pode escrever um conversor personalizado em C# e registrá-lo em um projeto Visual Basic. Para obter mais informações, consulte Suporte do Visual Basic.

Tabela de diferenças

A tabela a seguir lista Newtonsoft.Json recursos e System.Text.Json equivalentes. Os equivalentes enquadram-se nas seguintes categorias:

  • ✔️ Suportado pela funcionalidade integrada. Obter um comportamento semelhante pode exigir o uso de System.Text.Json um atributo ou opção global.
  • ⚠️ Não suportado, mas a solução alternativa é possível. As soluções alternativas são conversores personalizados, que podem não fornecer paridade completa com Newtonsoft.Json a funcionalidade. Para alguns deles, o código de exemplo é fornecido como exemplos. Se você confiar nesses Newtonsoft.Json recursos, a migração exigirá modificações em seus modelos de objeto .NET ou outras alterações de código.
  • ❌ Não suportado e a solução alternativa não é prática ou possível. Se você confiar nesses Newtonsoft.Json recursos, a migração não será possível sem alterações significativas.
Funcionalidade do Newtonsoft.Json System.Text.Json equivalente
Desserialização sem diferenciação de maiúsculas e minúsculas por padrão ✔️ Configuração global PropertyNameCaseInsensitive
Nomes de propriedades camel-case ✔️ Configuração global de PropertyNamingPolicy
Nomes de propriedade de caso de cobra ✔️ Política de nomenclatura de casos de cobra
Fuga mínima de caracteres ✔️ Fuga estrita de caracteres, configurável
NullValueHandling.Ignore cenário global ✔️ Opção global DefaultIgnoreCondition
Permitir comentários ✔️ ReadCommentManipulando a configuração global
Permitir vírgulas à direita ✔️ Configuração global AllowTrailingCommas
Registo personalizado do conversor ✔️ A ordem de precedência difere
Profundidade máxima padrão 64, configurável ✔️ Profundidade máxima padrão 64, configurável
PreserveReferencesHandling cenário global ✔️ Configuração global ReferenceHandling
Serializar ou desserializar números entre aspas ✔️ Configuração global NumberHanding, atributo [JsonNumberHandling]
Desserializar para classes e estruturas imutáveis ✔️ JsonConstructor, C# 9 Registros
Suporte para campos ✔️ Configuração global IncludeFields, atributo [JsonInclude]
DefaultValueHandling cenário global ✔️ Configuração global DefaultIgnoreCondition
NullValueHandling configuração em [JsonProperty] ✔️ Atributo JsonIgnore
DefaultValueHandling configuração em [JsonProperty] ✔️ Atributo JsonIgnore
Desserializar Dictionary com chave não-string ✔️ Suportado
Apoio a criadores e arrendatários de propriedades não públicas ✔️ Atributo JsonInclude
Atributo [JsonConstructor] ✔️ Atributo [JsonConstructor]
ReferenceLoopHandling cenário global ✔️ Configuração global ReferenceHandling
Chamadas de retorno ✔️ Retornos de chamada
NaN, Infinito, -Infinito ✔️ Suportado
Required configuração no [JsonProperty] atributo ✔️ Atributo [JsonRequired] e modificador necessário em C#
DefaultContractResolver Para ignorar propriedades ✔️ DefaultJsonTypeInfoResolver classe
Serialização polimórfica ✔️ Atributo [JsonDerivedType]
Desserialização polimórfica ✔️ Discriminador de tipo no atributo [JsonDerivedType]
Desserializar o valor do enum da cadeia de caracteres ✔️ Desserializar valores de enum de cadeia de caracteres
MissingMemberHandling cenário global ✔️ Lidar com membros ausentes
Preencher propriedades sem setters ✔️ Preencher propriedades sem setters
ObjectCreationHandling cenário global ✔️ Reutilizar em vez de substituir propriedades
Suporte para uma ampla gama de tipos ️ Alguns tipos requerem conversores personalizados
Desserializar o tipo inferido para object propriedades ️ Não suportado, solução alternativa, amostra
Desserializar JSON null literal para tipos de valor não anuláveis ️ Não suportado, solução alternativa, amostra
DateTimeZoneHandling, DateFormatString configurações ️ Não suportado, solução alternativa, amostra
JsonConvert.PopulateObject método ️ Não suportado, solução alternativa
Suporte para System.Runtime.Serialization atributos ️ Não suportado, solução alternativa, amostra
JsonObjectAttribute ️ Não suportado, solução alternativa
Permitir nomes de propriedades sem aspas Não suportado pelo design
Permitir aspas simples em torno de valores de cadeia de caracteres Não suportado pelo design
Permitir valores JSON sem cadeia de caracteres para propriedades de cadeia de caracteres Não suportado pelo design
TypeNameHandling.All cenário global Não suportado pelo design
Suporte para JsonPath consultas Não suportado
Limites configuráveis Não suportado

Esta não é uma lista exaustiva de Newtonsoft.Json características. A lista inclui muitos dos cenários que foram solicitados em problemas do GitHub ou postagens do StackOverflow. Se você implementar uma solução alternativa para um dos cenários listados aqui que atualmente não tem código de exemplo e se 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 o lista na seção Comentários desta página também.

Diferenças no comportamento padrão

System.Text.Json é rigoroso por padrão e evita qualquer adivinhação ou interpretação em nome do chamador, enfatizando o comportamento determinista. A biblioteca é intencionalmente projetada desta forma para desempenho e segurança. Newtonsoft.Json é flexível por padrão. Essa diferença fundamental no design está por trás de muitas das seguintes diferenças específicas no comportamento padrão.

Desserialização sem diferenciação de maiúsculas e minúsculas

Durante a desserialização, Newtonsoft.Json faz a correspondência de nome de propriedade sem diferenciação de maiúsculas e minúsculas por padrão. O System.Text.Json padrão diferencia maiúsculas de minúsculas, o que proporciona um melhor desempenho, uma vez que está fazendo uma correspondência exata. Para obter informações sobre como fazer a correspondência que não diferencia maiúsculas de minúsculas, consulte Correspondência de propriedade que não diferencia maiúsculas de minúsculas.

Se você estiver usando System.Text.Json indiretamente usando o ASP.NET Core, não precisará fazer nada para obter um comportamento como Newtonsoft.Jsono . ASP.NET Core especifica as configurações para nomes de propriedades de caixa camel e correspondência sem diferenciação de maiúsculas e minúsculas quando usa System.Text.Json.

ASP.NET Core também permite a desserialização de números cotados por padrão.

Fuga mínima de caracteres

Durante a serialização, Newtonsoft.Json é relativamente permissivo em deixar os personagens passarem sem escapar deles. Ou seja, não os substitui por \uxxxx onde xxxx está o ponto de código do personagem. Onde ele escapa deles, ele o faz emitindo um \ antes do personagem (por exemplo, " torna-se \"). System.Text.Json escapa de mais caracteres por padrão para fornecer proteções de defesa profunda contra scripts entre sites (XSS) ou ataques de divulgação de informações e faz isso usando a sequência de seis caracteres. System.Text.Json escapa de todos os caracteres não-ASCII por padrão, portanto, você não precisa fazer nada se estiver usando StringEscapeHandling.EscapeNonAscii o Newtonsoft.Json. System.Text.Json também escapa de caracteres sensíveis a HTML, por padrão. 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 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 os inclui. 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. Ele também ignora várias vírgulas à direita (por exemplo, [{"Color":"Red"},{"Color":"Green"},,]). O System.Text.Json padrão é lançar exceções para vírgulas à direita porque a especificação RFC 8259 não as permite. Para obter informações sobre como fazer System.Text.Json com que sejam aceites, 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 Newtonsoft.Json precedência de registro para conversores personalizados é a seguinte:

Essa ordem significa que um conversor personalizado na Converters coleção é substituído por um conversor que é 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 System.Text.Json precedência de registro para conversores personalizados é diferente:

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

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

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

Profundidade máxima

A versão mais recente do Newtonsoft.Json tem um limite máximo de profundidade de 64 por padrão. System.Text.Json também tem um limite padrão de 64 e é configurável pela configuração JsonSerializerOptions.MaxDepth.

Se você estiver usando System.Text.Json indiretamente usando o ASP.NET Core, o limite máximo de profundidade padrão é 32. O valor padrão é o mesmo que para a vinculação de modelo e é definido na classe JsonOptions.

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

Durante a desserialização, Newtonsoft.Json aceita nomes de propriedades entre aspas duplas, aspas simples ou sem aspas. Ele aceita valores de cadeia de caracteres cercados por aspas duplas ou aspas simples. Por exemplo, Newtonsoft.Json aceita o seguinte JSON:

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

System.Text.Json só aceita nomes de propriedade e valores de cadeia de caracteres entre 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 sem cadeia de caracteres para propriedades de cadeia de caracteres

Newtonsoft.Json Aceita valores que não sejam de cadeia de caracteres, como um número ou os literais true e false, para desserialização para propriedades do tipo string. Aqui está 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 que não sejam de cadeia de caracteres em propriedades de cadeia de caracteres. Um valor não-string 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 interna, mas soluções alternativas são possíveis. As soluções alternativas são conversores personalizados, que podem não fornecer paridade completa com Newtonsoft.Json a funcionalidade. Para alguns deles, o código de exemplo é fornecido como exemplos. Se você confiar nesses Newtonsoft.Json recursos, a migração exigirá modificações em seus modelos de objeto .NET ou outras alterações de código.

Para alguns dos cenários a seguir, as soluções alternativas não são práticas ou possíveis. Se você confiar nesses Newtonsoft.Json recursos, a migração não será possível sem alterações significativas.

Permitir ou escrever números entre aspas

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

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

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

Especifique o construtor a ser usado ao desserializar

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

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

Ignorar condicionalmente uma propriedade

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

  • DefaultContractResolver Permite selecionar propriedades a serem incluídas ou ignoradas, com base em critérios arbitrários.
  • As NullValueHandling configurações e DefaultValueHandling em JsonSerializerSettings permitem especificar que todas as propriedades de valor nulo ou valor padrão devem ser ignoradas.
  • As NullValueHandling configurações e DefaultValueHandling no [JsonProperty] atributo permitem especificar propriedades individuais que devem ser ignoradas quando definidas como null ou o valor padrão.

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

Além disso, no .NET 7 e 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.

Domínios públicos e não públicos

Newtonsoft.Json pode serializar e desserializar campos, bem como propriedades.

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

Preservar referências de objeto e manipular loops

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

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

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

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

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

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

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

Alguns recursos relacionados Newtonsoft.Json não são suportados:

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

Dicionário com chave não-string

Ambos Newtonsoft.Json e System.Text.Json apoiar coleções do tipo Dictionary<TKey, TValue>. Para obter informações sobre os tipos de chave suportados, consulte Tipos de chave suportados.

Atenção

Desserializar para um Dictionary<TKey, TValue> onde TKey é digitado como qualquer coisa diferente de string poderia introduzir uma vulnerabilidade de segurança no aplicativo consumidor. Para obter mais informações, consulte dotnet/runtime#4761.

Tipos sem suporte integrado

System.Text.Json não fornece 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 Automaticamente faz serialização polimórfica. A partir do .NET 7, System.Text.Json oferece suporte à serialização polimórfica por meio do JsonDerivedTypeAttribute atributo. Para obter mais informações, consulte Serializar propriedades de classes derivadas.

Desserialização polimórfica

Newtonsoft.Json tem uma TypeNameHandling configuração 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 das informações do discriminador 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 a desserialização deve ser desserializada 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 em versões mais antigas do .NET, crie um conversor como o exemplo em Como escrever conversores personalizados.

Desserializar valores de enum de cadeia de caracteres

Por padrão, System.Text.Json não oferece suporte à desserialização de valores de enum de cadeia de caracteres, enquanto Newtonsoft.Json oferece. Por exemplo, o código a seguir lança 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
}

No entanto, você pode habilitar a desserialização de valores de enum de cadeia de caracteres usando o JsonStringEnumConverter conversor. Para obter mais informações, consulte Enums as strings.

Desserialização de propriedades de objeto

Quando Newtonsoft.Json desserializa para Object, ele:

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

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

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

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

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

Desserializar nulo para tipo não anulável

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

  • NullValueHandling está 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 lança uma exceção. (A configuração de manipulação nula correspondente em System.Text.Json é JsonSerializerOptions.IgnoreNullValues = true.)

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

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

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 à Converters coleção.

Nota: O conversor anterior manipula valores nulos de forma diferente do Newtonsoft.Json que faz para POCOs que especificam valores padrão. Por exemplo, suponha que o código a seguir representa 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 seguinte JSON é desserializado usando o conversor anterior:

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

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

Desserializar para classes e estruturas imutáveis

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

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

Propriedades obrigatórias

No Newtonsoft.Json, você especifica que uma propriedade é necessária definindo Required no [JsonProperty] atributo. Newtonsoft.Json lança 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 JsonRequiredAttribute atributo em uma propriedade necessária. System.Text.Json lança uma exceção se a carga 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 Fornece várias maneiras de controlar como as propriedades e DateTimeDateTimeOffset os tipos são serializados e desserializados:

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

System.Text.Json suporta ISO 8601-1:2019, incluindo o perfil RFC 3339. Este 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 seguintes conversores serializam e desserializam JSON que usa o formato de época Unix 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, consulte Suporte a DateTime e DateTimeOffset em System.Text.Json.

Chamadas de retorno

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

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

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

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 OnDeserializing código não tem acesso à nova instância POCO. Para manipular a nova instância POCO no início da desserialização, coloque esse código no construtor POCO.

Criadores e arrendatários de propriedades não públicas

Newtonsoft.Json pode usar setters e getters de propriedades particulares e internas por meio do JsonProperty atributo.

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

Preencher objetos existentes

O JsonConvert.PopulateObject método em 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 Parameterless público padrão. Os conversores personalizados podem desserializar para uma instância existente.

Reutilizar em vez de substituir propriedades

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

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

Preencher propriedades sem setters

A partir do .NET 8, System.Text.Json oferece suporte ao preenchimento de propriedades, incluindo aquelas que não têm um setter. Para obter mais informações, consulte Preencher propriedades inicializadas.

Política de nomenclatura de casos de cobra

System.Text.Json Inclui uma política de nomenclatura interna para caso de cobra. No entanto, existem algumas diferenças de comportamento com Newtonsoft.Json para algumas entradas. A tabela a seguir mostra algumas dessas diferenças ao converter entradas usando a JsonNamingPolicy.SnakeCaseLower política.

Entrada Newtonsoft.Json Resultado System.Text.Json Resultado
"AB1" "a_b1" "AB1"
"SHA512Gerenciado" "sh_a512_managed" "sha512_managed"
"abc123DEF456" "abc123_de_f456" "abc123_def456"
"KEBAB-CASE" "keba_b-_case" "Caso Kebab"

Atributos System.Runtime.Serialization

System.Runtime.Serializationatributos como DataContractAttribute, DataMemberAttributee IgnoreDataMemberAttribute permitem definir um contrato de dados. Um contrato de dados é um acordo formal entre um serviço e um cliente que descreve abstratamente os dados a serem trocados. O contrato de dados define com precisão 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, consulte 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 a especificação RFC 8259 não os permite.

Lidar com membros ausentes

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

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

JsonObjectAttribute

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

Considere o exemplo a seguir que usa Newtonsoft.Json.JsonObjectAttribute para especificar que todas as null propriedades 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 exemplo a seguir 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 adicionando System.Text.Json 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 depurar usando um TraceWriter para exibir logs gerados por serialização ou desserialização. System.Text.Json não faz registro.

JsonDocument e JsonElement em comparação com JToken (como JObject, JArray)

System.Text.Json.JsonDocument fornece a capacidade de analisar e criar um DOM (Document Object Model) somente leitura a partir de cargas úteis JSON existentes. O DOM fornece acesso aleatório aos dados em uma carga JSON útil. Os elementos JSON que compõem a carga útil podem ser acessados através do JsonElement tipo. O JsonElement tipo fornece APIs para converter texto JSON em tipos .NET comuns. JsonDocument expõe uma RootElement propriedade.

A partir do .NET 6, você pode analisar e criar um DOM mutável a partir de cargas JSON existentes usando o JsonNode tipo e outros tipos no System.Text.Json.Nodes namespace. 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, ao contrário JObject ou JArray de , o tipo implementa Newtonsoft.JsonJsonDocument e precisa ser usado dentro de um bloco de IDisposableuso. Para obter mais informações, consulte JsonDocument is IDisposable.

JsonDocument é somente leitura

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

JsonElement é uma estrutura de união

JsonDocument expõe a RootElement propriedade as a do tipo JsonElement, que é um tipo struct de união que engloba 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 você pode usar JsonElement para materializar elementos JSON em tipos .NET.

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

Como pesquisar subelementos JsonDocument e JsonElement

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

Utf8JsonReader vs. JsonTextReader

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

Utf8JsonReader é uma ref struct

O JsonTextReader in Newtonsoft.Json é uma classe. O Utf8JsonReader tipo difere na medida em que é um ref struct. Para obter mais informações, consulte ref struct limitations for Utf8JsonReader.

Ler valores nulos em tipos de valor anuláveis

Newtonsoft.Json fornece APIs que retornam Nullable<T>, como ReadAsBoolean, que lida com um NullTokenType para você retornando um bool?arquivo . As APIs internas 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áveis.

Multi-target para leitura JSON

Se você precisar continuar a usar Newtonsoft.Json para determinadas estruturas de destino, poderá ter vários destinos e duas implementações. No entanto, isso não é trivial e exigiria alguma #ifdefs duplicação de fontes. Uma maneira de compartilhar o máximo de código possível é criar um ref struct wrapper em torno Utf8JsonReader e Newtonsoft.Json.JsonTextReader. Este invólucro unificaria a área de superfície pública, isolando as diferenças comportamentais. Isso permite isolar as alterações principalmente na construção do tipo, além de passar o novo tipo por referência. Este é o padrão que a biblioteca Microsoft.Extensions.DependencyModel segue:

Utf8JsonWriter vs. JsonTextWriter

System.Text.Json.Utf8JsonWriter é uma maneira de alto desempenho para escrever texto JSON codificado em UTF-8 a partir de tipos .NET comuns como String, Int32e DateTime. O gravador é um tipo de baixo nível que pode ser usado para criar serializadores personalizados.

Escrever valores brutos

Newtonsoft.Json tem um WriteRawValue método que grava JSON bruto onde um valor é esperado. System.Text.Json tem um equivalente direto: Utf8JsonWriter.WriteRawValue. Para obter mais informações, consulte Escrever 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 cercar valores de cadeia de caracteres. Utf8JsonWriter usa sempre aspas duplas.
  • QuoteName - Especifica se os nomes de propriedade devem ou não ser cercados por aspas. Utf8JsonWriter rodeia-os sempre de citações.

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

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

  • Recuo - Especifica quantos caracteres recuar. Utf8JsonWriter sempre recua 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 cercar valores de cadeia de caracteres. Utf8JsonWriter usa sempre aspas duplas.
  • QuoteName - Especifica se os nomes de propriedade devem ou não ser cercados por aspas. Utf8JsonWriter rodeia-os sempre de citações.

Não há soluções alternativas que permitam personalizar o JSON produzido por Utf8JsonWriter dessas maneiras.

Escrever valores de Timepan, Uri ou char

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

Multi-target para escrever JSON

Se você precisar continuar a usar Newtonsoft.Json para determinadas estruturas de destino, poderá ter vários destinos e duas implementações. No entanto, isso não é trivial e exigiria alguma #ifdefs duplicação de fontes. Uma maneira de compartilhar o máximo de código possível é criar um wrapper em torno Utf8JsonWriter e Newtonsoft.Json.JsonTextWriter. Este invólucro unificaria a área de superfície pública, isolando as diferenças comportamentais. Isso permite isolar as alterações principalmente na construção do tipo. A biblioteca Microsoft.Extensions.DependencyModel segue:

TypeNameHandling.All não suportado

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

Consultas de caminho JSON não suportadas

O JsonDocument DOM não oferece suporte à consulta usando o caminho JSON.

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

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

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 o código-fonte e a questão do GitHub dotnet/runtime #39953.

NaN, Infinito, -Infinito

Newtonsoft analisa NaN, Infinitye -Infinity JSON string tokens. 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.

Use o Copilot do GitHub para migrar

Você pode obter ajuda de codificação do GitHub Copilot para migrar seu código do Newtonsoft.Json para System.Text.Json dentro do seu IDE. Você pode personalizar o prompt de acordo com suas necessidades.

Exemplo de prompt para o Copilot Chat

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

O GitHub Copilot é alimentado por IA, então surpresas e erros são possíveis. Para obter mais informações, consulte Copilot FAQs.

Saiba mais sobre Copiloto GitHub no Visual Studio e Copiloto GitHub no VS Code.

Recursos adicionais