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 plataformas-alvo, instale o pacote NuGet System.Text.Json. 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 do Newtonsoft.Json.

System.Text.Json Concentra-se principalmente no desempenho, segurança e conformidade com as normas. Tem algumas diferenças importantes no comportamento padrão e não visa ter paridade de funcionalidades com Newtonsoft.Json. 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 abaixo lista Newtonsoft.Json funcionalidades e System.Text.Json equivalências. Os equivalentes enquadram-se nas seguintes categorias:

  • ✔️ Suportado pela funcionalidade integrada. Obter um comportamento semelhante de System.Text.Json pode exigir o uso de 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 uma correspondência total com Newtonsoft.Json 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 insensível a maiúsculas e minúsculas por padrão ✔️ Configuração global PropertyNameCaseInsensitive
Nomes de propriedades em estilo 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 configuração 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 configuração 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 Records
Suporte para campos ✔️ Configuração global IncludeFields, atributo [JsonInclude]
DefaultValueHandling configuração 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 configuração global ✔️ Configuração global ReferenceHandling
Chamadas de retorno ✔️ Chamadas de retorno
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 valor de enumeração de uma cadeia de caracteres ✔️ Desserializar valores de cadeia de caracteres de enum
MissingMemberHandling configuração global ✔️ Lidar com membros ausentes
Atribuir propriedades sem setters ✔️ Preencher propriedades sem setters
ObjectCreationHandling configuração 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 configuração 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 indiferente ao uso de maiúsculas e minúsculas

Durante a desserialização, Newtonsoft.Json faz a correspondência de nomes de propriedades por padrão sem distinção entre maiúsculas e minúsculas. 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 estiver a usar o System.Text.Json indiretamente através do ASP.NET Core, não precisará fazer nada para obter um comportamento semelhante a Newtonsoft.Json. ASP.NET Core especifica as configurações para nomes de propriedades em camel case e correspondência sem distinção de maiú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 finais

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 finais porque a especificação RFC 8259 não as permite. Para obter informações sobre como fazer System.Text.Json aceitá-los, consulte Permitir comentários e vírgulas finais. Não há como permitir várias vírgulas finais.

Precedência de registo do converter

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

Essa ordem significa que um conversor personalizado da coleção Converters é substituído por um conversor que é registado aplicando um atributo ao 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 desta ordem de precedência é fazer com que as alterações em tempo de execução substituam as opções de tempo de projeto. 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 não-string 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 um 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 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 estiver a usar o System.Text.Json indiretamente através do ASP.NET Core, não precisará fazer nada para obter um comportamento semelhante a Newtonsoft.Json. ASP.NET Core especifica padrões da web quando utiliza System.Text.Json, 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 atributo Newtonsoft.Json[JsonConstructor] permite-lhe 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.
  • NullValueHandling e DefaultValueHandling definições 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.

Campos 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 configuração em PreserveReferencesHandling que permite serializar por referência:

  • Um metadado de identificador é adicionado ao JSON criado para o primeiro objeto Person.
  • 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 classe System.Text.Json.Serialization.ReferenceResolver 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 incluídos no JSON e, em seguida, usados durante a desserialização para decidir se devem ser desserializados no tipo base ou em 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 enum baseados em cadeia de caracteres, mas Newtonsoft.Json oferece suporte. 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:

  • Deduz o tipo de valores primitivos no conteúdo JSON (diferente de null) e retorna o armazenado string, long, double, boolean, ou DateTime como um objeto encapsulado. 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. Valores complexos são coleções de pares chave-valor JSON dentro de chavetas ({}) 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 um objeto em caixa de valores primitivos e complexos sempre que faz a desserialização para JsonElement, por exemplo:

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

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

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 adquire o valor de 1/1/0001 (default(DateTimeOffset)), ou seja, o valor definido no construtor é substituído. A desserialização, dado o mesmo POCO e JSON, deixaria 1/1/2001 na propriedade Newtonsoft.Json.

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 de tipos DateTime e DateTimeOffset 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 utiliza 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 : System.Text.Json.Serialization.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, NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
                || !int.TryParse(match.Groups[3].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
                || !int.TryParse(match.Groups[4].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
        {
            throw new System.Text.Json.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 = value.ToUnixTimeMilliseconds();

        TimeSpan utcOffset = value.Offset;

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

        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : System.Text.Json.Serialization.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, NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
        {
            throw new System.Text.Json.JsonException();
        }

        return s_epoch.AddMilliseconds(unixTime);
    }

    public override void Write(
        Utf8JsonWriter writer,
        DateTime value,
        JsonSerializerOptions options)
    {
        long unixTime = (value - s_epoch).Ticks / TimeSpan.TicksPerMillisecond;

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

Para obter mais informações, consulte suporte para 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 definidores e acessadores de propriedades privadas e internas por meio do atributo [JsonInclude]. Para obter um código de exemplo, consulte Acessores de propriedades não públicas.

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 cria sempre uma nova instância do tipo de destino usando o construtor padrão público sem parâmetros. Os conversores personalizados podem desserializar para uma instância existente.

Reutilizar ao invés 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.

Atribuir 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á a ser desserializado incluir propriedades que estão em falta 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.Json tem um atributo, JsonObjectAttribute, que pode ser aplicado ao nível de tipo para controlar quais membros são serializados, de que forma os valores de null são manipulados e se todos os membros são obrigató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 { ... }

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 fazer a depuração usando um TraceWriter para exibir logs que são gerados por serialização ou desserialização. System.Text.Json não faz registo.

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

System.Text.Json.JsonDocument fornece a capacidade de analisar e criar um Document Object Model (DOM) de apenas leitura a partir de cargas úteis JSON existentes. O DOM fornece acesso direto aos dados num carga JSON. 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 a propriedade RootElement.

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 Constrói uma visualização na memória dos dados para um buffer partilhado. Portanto, ao contrário de JObject ou JArray de Newtonsoft.Json, o tipo JsonDocument implementa IDisposable e precisa ser usado dentro de um bloco using. 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 propriedade RootElement do tipo JsonElement, que é uma struct de união que abarca 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 em JsonElement exigem 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 um JsonDocument e JsonElement por subelementos.

Utf8JsonReader vs. JsonTextReader

System.Text.Json.Utf8JsonReader é um leitor de alto desempenho e baixa alocação, apenas leitura para frente 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 é uma ref struct

O JsonTextReader in Newtonsoft.Json é uma classe. O Utf8JsonReader tipo difere porque é 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 processa um NullTokenType ao retornar um bool?. 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.

Suporte multi-alvo para a leitura de 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 algum #ifdefs e duplicação de código-fonte. 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. Isto permite isolar as alterações, principalmente na construção do tipo, além de permitir a passagem do 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. "Escritor é 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, pode-se personalizar o caractere de recuo e o tamanho de Utf8JsonWriter usando as opções expostas pela estrutura JsonWriterOptions.

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

  • Recuo - Especifica quantos caracteres recuar. Utf8JsonWriter sempre indenta em 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.

Escreva valores de Timespan, 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.

Suporte a múltiplos destinos 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 algum #ifdefs e duplicação de código-fonte. 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 excluir a funcionalidade equivalente de TypeNameHandling.All do System.Text.Json foi intencional. 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.

Num 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, Infinity, e -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