Migrar de Newtonsoft.Json para System.Text.Json
Este artigo mostra como migrar de Newtonsoft.Json para System.Text.Json.
O namespace System.Text.Json
apresenta funcionalidade para serialização e desserialização do JSON (JavaScript Object Notation). A biblioteca System.Text.Json
está incluída no runtime do .NET Core 3.1 e versões posteriores. Para outras estruturas de destino, instale o pacote NuGet System.Text.Json. O pacote é compatível com:
- .NET Standard 2.0 e versões posteriores
- .NET Framework 4.6.2 e versões posteriores
- .NET Core 2.0, 2.1 e 2.2
Dica
Você pode usar a assistência de IA para migrar do Newtonsoft.Json
com o GitHub Copilot.
System.Text.Json
se concentra principalmente na conformidade de padrões, segurança e desempenho. Ele tem algumas diferenças importantes no comportamento padrão e não tem como objetivo ter paridade de recursos com Newtonsoft.Json
. Para alguns cenários, atualmente System.Text.Json
não tem nenhuma funcionalidade interna, mas há soluções alternativas recomendadas. Para outros cenários, soluções alternativas são impraticáveis.
A equipe System.Text.Json
está investindo na adição dos recursos que são mais solicitados. Se seu aplicativo depende de um recurso em falta, considere arquivar um problema no repositório GitHub do dotnet/runtime para saber se o suporte de seu cenário pode ser adicionado.
Este artigo trata principalmente de como usar a API JsonSerializer, mas também inclui diretrizes de como usar o JsonDocument (que representa o Modelo de Objeto do Documento ou DOM) tipos Utf8JsonReadere Utf8JsonWriter.
No Visual Basic, você não pode usar Utf8JsonReader, o que também significa que não pode gravar conversores personalizados. A maioria das soluções alternativas apresentadas aqui exige que você grave conversores personalizados. Você pode gravar um conversor personalizado em C# e registrá-lo em um projeto do Visual Basic. Para saber mais, confira Suporte para Visual Basic.
Tabela de diferenças
A tabela a seguir lista os recursos Newtonsoft.Json
e equivalentes System.Text.Json
. Os equivalentes correspondem às seguintes categorias:
- ✔️ Compatível com a funcionalidade integrada. Para obter um comportamento de
System.Text.Json
parecido, pode ser necessário o uso de um atributo ou de uma opção global. - ⚠️ Não é compatível. É possível uma solução alternativa. As soluções alternativas são conversores personalizados, que podem não dar paridade completa com a funcionalidade
Newtonsoft.Json
. Para alguns, o código de exemplo é fornecido como exemplos. Se você depender desses recursosNewtonsoft.Json
, a migração exige modificações em seus modelos de objeto .NET ou outras alterações de código. - ❌ Não é compatível. A solução alternativa não é prática ou possível. Se você contar com esses recursos
Newtonsoft.Json
, a migração não pode ocorrer sem alterações significativas.
Recurso do Newtonsoft.Json | Equivalente System.Text.Json |
---|---|
Desserialização que não diferencia maiúsculas de minúsculas por padrão | ✔️ Configuração global PropertyNameCaseInsensitive |
Nomes de propriedade em minúsculas concatenadas | ✔️ Configuração global PropertyNamingPolicy |
Nomes de propriedade em snake-case | ✔️ Política de nomenclatura Snake case |
Escape mínimo de caracteres | ✔️ Escape de caractere estrito e configurável |
configuração globalNullValueHandling.Ignore |
✔️ Opção global DefaultIgnoreCondition |
Permitir comentários | ✔️ Configuração global ReadCommentHandling |
Permitir vírgulas à direita | ✔️ Configuração global AllowTrailingCommas |
Registro de conversor personalizado | ✔️ Diferença na ordem de precedência |
Profundidade máxima padrão 64, configurável | ✔️ Profundidade máxima padrão 64 configurável |
configuração global PreserveReferencesHandling |
✔️ Configuração global ReferenceHandling |
Serializar ou desserializar números entre aspas | ✔️ Configuração global NumberHandling, atributo [JsonNumberHandling] |
Desserializar para classes e structs imutáveis | ✔️ Registros JsonConstructor, C# 9 |
Suporte para campos | ✔️ Configuração global IncludeFields, atributo [JsonInclude] |
Configuração global DefaultValueHandling |
✔️ Configuração global DefaultIgnoreCondition |
Configuração NullValueHandling em [JsonProperty] |
✔️ Atributo JsonIgnore |
Configuração DefaultValueHandling em [JsonProperty] |
✔️ Atributo JsonIgnore |
Desserializar Dictionary com chave sem cadeia de caracteres |
✔️ Com suporte |
Suporte para setters e getters de propriedades não públicas | ✔️ Atributo JsonInclude |
Atributo [JsonConstructor] |
✔️ Atributo [JsonConstructor] |
Configuração global ReferenceLoopHandling |
✔️ Configuração global ReferenceHandling |
Retornos de chamada | ✔️ Retornos de chamada |
NaN, Infinity, -Infinity | ✔️ Com suporte |
Configuração Required no atributo [JsonProperty] |
✔️ Atributo [JsonRequired] e modificador necessário do C# |
DefaultContractResolver para ignorar propriedades |
✔️ DefaultJsonTypeInfoResolver class |
Serialização polimórfica | ✔️ [JsonDerivedType] attribute |
Desserialização polimórfica | ✔️ Discriminador de tipo no atributo [JsonDerivedType] |
Desserializar o valor de enumeração da cadeia de caracteres | ✔️ Desserializar os valores de enumeração da cadeia de caracteres |
Configuração global MissingMemberHandling |
✔️ Lidar com os membros ausentes |
Preencher propriedades sem setters | ✔️ Preencher propriedades sem setters |
Configuração global ObjectCreationHandling |
✔️ Reutilizar em vez de substituir propriedades |
Suporte para uma ampla gama de tipos | ⚠️ Alguns tipos exigem conversores personalizados |
Desserializar o tipo inferido para as propriedades object |
⚠️ Sem suporte, solução alternativa, amostra |
Desserializar o literal JSON null para tipos de valor não anuláveis |
⚠️ Sem suporte, solução alternativa, amostra |
ConfiguraçõesDateTimeZoneHandling , DateFormatString |
⚠️ Sem suporte, solução alternativa, amostra |
Método JsonConvert.PopulateObject |
⚠️ Sem suporte, solução alternativa |
Suporte para atributos System.Runtime.Serialization |
⚠️ Sem suporte, solução alternativa, amostra |
JsonObjectAttribute |
⚠️ Sem suporte, solução alternativa |
Permitir nomes de propriedade sem aspas | ❌Não há suporte por design |
Permitir aspas simples em torno de valores de cadeia de caracteres | ❌Não há suporte por design |
Permitir valores JSON não string para propriedades string | ❌Não há suporte por design |
Configuração global TypeNameHandling.All |
❌Não há suporte por design |
Suporte para consultas JsonPath |
❌Sem suporte |
Limites configuráveis | ❌Sem suporte |
Recurso do Newtonsoft.Json | Equivalente System.Text.Json |
---|---|
Desserialização que não diferencia maiúsculas de minúsculas por padrão | ✔️ Configuração global PropertyNameCaseInsensitive |
Nomes de propriedade em minúsculas concatenadas | ✔️ Configuração global PropertyNamingPolicy |
Escape mínimo de caracteres | ✔️ Escape de caractere estrito e configurável |
configuração globalNullValueHandling.Ignore |
✔️ Opção global DefaultIgnoreCondition |
Permitir comentários | ✔️ Configuração global ReadCommentHandling |
Permitir vírgulas à direita | ✔️ Configuração global AllowTrailingCommas |
Registro de conversor personalizado | ✔️ Diferença na ordem de precedência |
Profundidade máxima padrão 64, configurável | ✔️ Profundidade máxima padrão 64 configurável |
configuração global PreserveReferencesHandling |
✔️ Configuração global ReferenceHandling |
Serializar ou desserializar números entre aspas | ✔️ Configuração global NumberHandling, atributo [JsonNumberHandling] |
Desserializar para classes e structs imutáveis | ✔️ Registros JsonConstructor, C# 9 |
Suporte para campos | ✔️ Configuração global IncludeFields, atributo [JsonInclude] |
Configuração global DefaultValueHandling |
✔️ Configuração global DefaultIgnoreCondition |
Configuração NullValueHandling em [JsonProperty] |
✔️ Atributo JsonIgnore |
Configuração DefaultValueHandling em [JsonProperty] |
✔️ Atributo JsonIgnore |
Desserializar Dictionary com chave sem cadeia de caracteres |
✔️ Com suporte |
Suporte para setters e getters de propriedades não públicas | ✔️ Atributo JsonInclude |
Atributo [JsonConstructor] |
✔️ Atributo [JsonConstructor] |
Configuração global ReferenceLoopHandling |
✔️ Configuração global ReferenceHandling |
Retornos de chamada | ✔️ Retornos de chamada |
NaN, Infinity, -Infinity | ✔️ Com suporte |
Configuração Required no atributo [JsonProperty] |
✔️ Atributo [JsonRequired] e modificador necessário do C# |
DefaultContractResolver para ignorar propriedades |
✔️ DefaultJsonTypeInfoResolver class |
Serialização polimórfica | ✔️ [JsonDerivedType] attribute |
Desserialização polimórfica | ✔️ Discriminador de tipo no atributo [JsonDerivedType] |
Desserializar o valor de enumeração da cadeia de caracteres | ✔️ Desserializar os valores de enumeração da cadeia de caracteres |
Suporte para uma ampla gama de tipos | ⚠️ Alguns tipos exigem conversores personalizados |
Desserializar o tipo inferido para as propriedades object |
⚠️ Sem suporte, solução alternativa, amostra |
Desserializar o literal JSON null para tipos de valor não anuláveis |
⚠️ Sem suporte, solução alternativa, amostra |
ConfiguraçõesDateTimeZoneHandling , DateFormatString |
⚠️ Sem suporte, solução alternativa, amostra |
Método JsonConvert.PopulateObject |
⚠️ Sem suporte, solução alternativa |
Configuração global ObjectCreationHandling |
⚠️ Sem suporte, solução alternativa |
Adicionar a coleções sem setters | ⚠️ Sem suporte, solução alternativa |
Nomes de propriedade em snake-case | ⚠️ Sem suporte, solução alternativa |
Suporte para atributos System.Runtime.Serialization |
⚠️ Sem suporte, solução alternativa, amostra |
Configuração global MissingMemberHandling |
⚠️ Sem suporte, solução alternativa, amostra |
JsonObjectAttribute |
⚠️ Sem suporte, solução alternativa |
Permitir nomes de propriedade sem aspas | ❌Não há suporte por design |
Permitir aspas simples em torno de valores de cadeia de caracteres | ❌Não há suporte por design |
Permitir valores JSON não string para propriedades string | ❌Não há suporte por design |
Configuração global TypeNameHandling.All |
❌Não há suporte por design |
Suporte para consultas JsonPath |
❌Sem suporte |
Limites configuráveis | ❌Sem suporte |
Recurso do Newtonsoft.Json | Equivalente System.Text.Json |
---|---|
Desserialização que não diferencia maiúsculas de minúsculas por padrão | ✔️ Configuração global PropertyNameCaseInsensitive |
Nomes de propriedade em minúsculas concatenadas | ✔️ Configuração global PropertyNamingPolicy |
Escape mínimo de caracteres | ✔️ Escape de caractere estrito e configurável |
configuração globalNullValueHandling.Ignore |
✔️ Opção global DefaultIgnoreCondition |
Permitir comentários | ✔️ Configuração global ReadCommentHandling |
Permitir vírgulas à direita | ✔️ Configuração global AllowTrailingCommas |
Registro de conversor personalizado | ✔️ Diferença na ordem de precedência |
Profundidade máxima padrão 64, configurável | ✔️ Profundidade máxima padrão 64 configurável |
configuração global PreserveReferencesHandling |
✔️ Configuração global ReferenceHandling |
Serializar ou desserializar números entre aspas | ✔️ Configuração global NumberHandling, atributo [JsonNumberHandling] |
Desserializar para classes e structs imutáveis | ✔️ Registros JsonConstructor, C# 9 |
Suporte para campos | ✔️ Configuração global IncludeFields, atributo [JsonInclude] |
Configuração global DefaultValueHandling |
✔️ Configuração global DefaultIgnoreCondition |
Configuração NullValueHandling em [JsonProperty] |
✔️ Atributo JsonIgnore |
Configuração DefaultValueHandling em [JsonProperty] |
✔️ Atributo JsonIgnore |
Desserializar Dictionary com chave sem cadeia de caracteres |
✔️ Com suporte |
Suporte para setters e getters de propriedades não públicas | ✔️ Atributo JsonInclude |
Atributo [JsonConstructor] |
✔️ Atributo [JsonConstructor] |
Configuração global ReferenceLoopHandling |
✔️ Configuração global ReferenceHandling |
Retornos de chamada | ✔️ Retornos de chamada |
NaN, Infinity, -Infinity | ✔️ Com suporte |
Desserializar o valor de enumeração da cadeia de caracteres | ✔️ Desserializar os valores de enumeração da cadeia de caracteres |
Suporte para uma ampla gama de tipos | ⚠️ Alguns tipos exigem conversores personalizados |
Serialização polimórfica | ⚠️ Sem suporte, solução alternativa, amostra |
Desserialização polimórfica | ⚠️ Sem suporte, solução alternativa, amostra |
Desserializar o tipo inferido para as propriedades object |
⚠️ Sem suporte, solução alternativa, amostra |
Desserializar o literal JSON null para tipos de valor não anuláveis |
⚠️ Sem suporte, solução alternativa, amostra |
Configuração Required no atributo [JsonProperty] |
⚠️ Sem suporte, solução alternativa, amostra |
DefaultContractResolver para ignorar propriedades |
⚠️ Sem suporte, solução alternativa, amostra |
ConfiguraçõesDateTimeZoneHandling , DateFormatString |
⚠️ Sem suporte, solução alternativa, amostra |
Método JsonConvert.PopulateObject |
⚠️ Sem suporte, solução alternativa |
Configuração global ObjectCreationHandling |
⚠️ Sem suporte, solução alternativa |
Adicionar a coleções sem setters | ⚠️ Sem suporte, solução alternativa |
Nomes de propriedade em snake-case | ⚠️ Sem suporte, solução alternativa |
JsonObjectAttribute |
⚠️ Sem suporte, solução alternativa |
Suporte para atributos System.Runtime.Serialization |
❌Sem suporte |
Configuração global MissingMemberHandling |
❌Sem suporte |
Permitir nomes de propriedade sem aspas | ❌Não há suporte por design |
Permitir aspas simples em torno de valores de cadeia de caracteres | ❌Não há suporte por design |
Permitir valores JSON não string para propriedades string | ❌Não há suporte por design |
Configuração global TypeNameHandling.All |
❌Não há suporte por design |
Suporte para consultas JsonPath |
❌Sem suporte |
Limites configuráveis | ❌Sem suporte |
Esta não é uma lista exaustiva de recursos Newtonsoft.Json
. A lista inclui muitos dos cenários que foram solicitados em problemas do GitHub ou postagens do StackOverflow . Se implementar uma solução alternativa para um dos cenários listados aqui que atualmente não tem código de exemplo e quiser compartilhar sua solução, selecione Esta página na seção Comentários na parte inferior desta página. Isso cria um problema no repositório GitHub desta documentação e também lista na seção Comentários desta página.
Diferenças no comportamento padrão
System.Text.Json é estrito por padrão e evita qualquer detecção ou interpretação em nome do chamador, enfatizando o comportamento determinístico. A biblioteca foi projetada intencionalmente dessa forma por conta do desempenho e segurança. Newtonsoft.Json
é flexível por padrão. Essa diferença fundamental no design mostra muitas das seguintes diferenças específicas no comportamento padrão.
Desserialização que não diferencia maiúsculas de minúsculas
Durante a desserialização, Newtonsoft.Json
gera um nome da propriedade que não diferencia maiúsculas de minúsculas corresponde por padrão. O System.Text.Json por padrão diferencia maiúsculas de minúsculas, o que oferece melhor desempenho, pois tem uma correspondência exata. Para obter informações sobre como fazer correspondência sem diferenciar maiúsculas de minúsculas, consulte correspondência de propriedades que não diferencia maiúsculas de minúsculas.
Se você estiver usando System.Text.Json
indiretamente pelo ASP.NET Core, não precisa fazer nada para obter um comportamento como Newtonsoft.Json
. O ASP.NET Core especifica as configurações para nomes de propriedade em minúsculas concatenadas e correspondências que não diferenciam maiúsculas de minúsculas ao usar System.Text.Json
.
Por padrão, o ASP.NET Core também permite desserializar os números entre aspas.
Escape mínimo de caracteres
Durante a serialização, Newtonsoft.Json
é relativamente permitido deixar os caracteres passarem sem escape. Ou seja, não os substitui por \uxxxx
onde xxxx
é o ponto de código do caractere. Quando há escape, ele emite um \
antes que o caractere (por exemplo, "
se torne \"
). System.Text.Json escapa mais caracteres por padrão para fornecer proteções de defesa em profundidade contra XSS (cross-site scripting) ou ataques de divulgação de informações e, para isso, usa a sequência de seis caracteres. Por padrão, System.Text.Json
escapa todos os caracteres não ASCII e você não precisa executar nada se estiver usando StringEscapeHandling.EscapeNonAscii
no Newtonsoft.Json
. Por padrão, System.Text.Json
também escapa caracteres sensíveis a HTML. Para obter informações sobre como substituir o comportamento padrão System.Text.Json
, consulte Personalizar codificação de caracteres.
Comentários
Durante a desserialização, Newtonsoft.Json
ignora os comentários no JSON por padrão. O System.Text.Json padrão é lançar exceções para comentários porque a especificação RFC 8259 não inclui exceções. Para obter informações sobre como permitir comentários, consulte Permitir comentários e vírgulas à direita.
Vírgulas à direita
Durante a desserialização, Newtonsoft.Json
ignora vírgulas à direita por padrão. O código também ignora várias vírgulas à direita (por exemplo, [{"Color":"Red"},{"Color":"Green"},,]
). O System.Text.Json padrão gera exceções para vírgulas à direita porque a especificação RFC 8259 não permite exceções. Para obter informações sobre como System.Text.Json
aceita as exceções, consulte Permitir comentários e vírgulas à direita. Não há como permitir várias vírgulas à direita.
Precedência de registro do conversor
A precedência de registro de Newtonsoft.Json
para conversores personalizados é:
- Atributo na propriedade
- Atributo no tipo
- Coleção Conversores
Essa ordem significa que um conversor personalizado na coleção Converters
é substituído por um conversor registrado aplicando um atributo no nível do tipo. Ambos os registros são substituídos por um atributo no nível da propriedade.
A precedência de registro de System.Text.Json para conversores personalizados é diferente:
- Atributo na propriedade
- Converters collection
- Atributo no tipo
A diferença aqui é que um conversor personalizado na coleção Converters
substitui um atributo no nível do tipo. A intenção dessa ordem de precedência é fazer com que as alterações no tempo de execução substituam as opções no tempo de design. Não há como alterar a precedência.
Para obter mais informações sobre o registro de conversor personalizado, consulte Registrar um conversor personalizado.
Profundidade máxima
Por padrão, a versão mais recente de Newtonsoft.Json
tem um limite máximo de profundidade de 64. System.Text.Json também tem um limite padrão de 64 e pode ser configurado pela configuração JsonSerializerOptions.MaxDepth.
Se você estiver usando System.Text.Json
indiretamente pelo ASP.NET Core, o limite máximo de profundidade padrão será 32. O valor padrão é igual para model binding e é definido na classe JsonOptions.
Cadeias de caracteres JSON (nomes de propriedade e valores de cadeia de caracteres)
Durante a desserialização, Newtonsoft.Json
aceita nomes de propriedade com aspas duplas, aspas simples ou sem aspas. O código aceita valores de cadeia de caracteres com aspas duplas ou aspas simples. Por exemplo, Newtonsoft.Json
aceita o seguinte JSON:
{
"name1": "value",
'name2': "value",
name3: 'value'
}
System.Text.Json
aceita apenas nomes de propriedade e valores de cadeia de caracteres em aspas duplas porque esse formato é exigido pela especificação RFC 8259 e é o único formato considerado JSON válido.
Um valor entre aspas simples resulta em um JsonException com a seguinte mensagem:
''' is an invalid start of a value.
Valores não cadeia de caracteres para propriedades de cadeia de caracteres
Newtonsoft.Json
aceita valores não cadeia de caracteres, como um número ou literais true
e false
, para desserialização para propriedades do tipo string. Veja aqui um exemplo de JSON que Newtonsoft.Json
desserializa com êxito para a seguinte classe:
{
"String1": 1,
"String2": true,
"String3": false
}
public class ExampleClass
{
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
}
System.Text.Json
não desserializa valores não cadeia de caracteres em propriedades de cadeia de caracteres. Um valor não cadeia de caracteres recebido para um campo de cadeia de caracteres resulta em um JsonException com a seguinte mensagem:
The JSON value could not be converted to System.String.
Cenários usando JsonSerializer
Alguns dos cenários a seguir não são suportados pela funcionalidade integrada, mas são possíveis soluções alternativas. As soluções alternativas são conversores personalizados, que podem não oferecer paridade completa com a funcionalidade Newtonsoft.Json
. Para alguns, o código de exemplo é fornecido como exemplos. Se você depender desses recursos Newtonsoft.Json
, a migração exige modificações em seus modelos de objeto .NET ou outras alterações de código.
Para alguns dos cenários a seguir, soluções alternativas não são práticas ou possíveis. Se você contar com esses recursos Newtonsoft.Json
, a migração não pode ocorrer sem alterações significativas.
Permitir ou gravar números entre aspas
Newtonsoft.Json
pode serializar ou desserializar números representados por cadeias de caracteres JSON (com aspas). Por exemplo, ele pode aceitar: {"DegreesCelsius":"23"}
em vez de {"DegreesCelsius":23}
. Para habilitar esse comportamento em System.Text.Json, defina JsonSerializerOptions.NumberHandling para WriteAsString ou AllowReadingFromString, ou use o atributo [JsonNumberHandling].
Se você estiver usando System.Text.Json
indiretamente pelo ASP.NET Core, não precisa fazer nada para obter um comportamento como Newtonsoft.Json
. O ASP.NET Core especifica os padrões da Web quando usa System.Text.Json
, e os padrões da Web permitem números entre aspas.
Para obter mais informações, consulte Permitir ou gravar números entre aspas.
Especificar construtor a ser usado na desserialização
O atributo Newtonsoft.Json
[JsonConstructor]
permite especificar qual construtor chamar para desserializar para um POCO.
System.Text.Json
também tem um atributo [JsonConstructor]. Para obter mais informações, consulte Tipos imutáveis e Registros.
Ignorar condicionalmente uma propriedade
Newtonsoft.Json
tem várias formas de ignorar condicionalmente uma propriedade em serialização ou desserialização:
DefaultContractResolver
permite selecionar propriedades para incluir ou ignorar com base em critérios arbitrários.- As configurações
NullValueHandling
eDefaultValueHandling
emJsonSerializerSettings
permitem especificar que todas as propriedades de valor nulo ou valor padrão devem ser ignoradas. - As configurações
NullValueHandling
eDefaultValueHandling
no atributo[JsonProperty]
permitem especificar propriedades individuais que devem ser ignoradas quando definidas como valor nulo ou valor padrão.
System.Text.Json apresenta as seguintes formas de ignorar propriedades ou campos durante a serialização:
- O atributo [JsonIgnore] em uma propriedade permite que a propriedade seja omitida do JSON durante a serialização.
- A opção global IgnoreReadOnlyProperties permite ignorar todas as propriedades somente leitura.
- Se você estiver incluindo campos, a opção global JsonSerializerOptions.IgnoreReadOnlyFields permitirá que você ignore todos os campos somente leitura.
- A opção global
DefaultIgnoreCondition
permite ignorar todas as propriedades de tipo de valor que têm valores padrão ou ignorar todas as propriedades de tipo de referência que têm valores nulos.
Além disso, no .NET 7 e em versões posteriores, você pode personalizar o contrato JSON para ignorar propriedades com base em critérios arbitrários. Para obter mais informações, consulte Contratos personalizados.
Essas opções não ignoram as propriedades selecionadas com base em critérios arbitrários avaliados no tempo de execução.
Campos públicos e não públicos
Newtonsoft.Json
pode serializar e desserializar campos, além das propriedades.
No System.Text.Json, use a configuração global JsonSerializerOptions.IncludeFields ou o atributo [JsonInclude] para incluir campos públicos na serialização ou desserialização. Para obter um exemplo, consulte Incluir campos.
Preservar referências de objeto e loops de identificador
Por padrão, Newtonsoft.Json
serializa por valor. Por exemplo, se um objeto contiver duas propriedades com uma referência ao mesmo objeto Person
, os valores das propriedades do objeto Person
serão duplicados no JSON.
Newtonsoft.Json
tem uma configuração PreserveReferencesHandling
no JsonSerializerSettings
que permite serializar por referência:
- Um metadados de identificador é adicionado ao JSON criado para o primeiro objeto
Person
. - O JSON foi criado para o segundo objeto
Person
que contém uma referência a esse identificador em vez de valores da propriedade.
Newtonsoft.Json
também tem uma configuração ReferenceLoopHandling
que permite ignorar referências circulares em vez de abrir uma exceção.
Para preservar referências e lidar com referências circulares em System.Text.Json, defina JsonSerializerOptions.ReferenceHandler como Preserve. A configuração ReferenceHandler.Preserve
é equivalente a PreserveReferencesHandling
= PreserveReferencesHandling.All
em Newtonsoft.Json
.
A opção ReferenceHandler.IgnoreCycles
tem um comportamento semelhante a Newtonsoft.JsonReferenceLoopHandling.Ignore
. Uma diferença é que a implementação System.Text.Json substitui loops de referência pelo token JSON null
em vez de ignorar a referência de objeto. Para obter mais informações, consulte Ignorar referências circulares.
Assim como o Newtonsoft.JsonReferenceResolver, a classe System.Text.Json.Serialization.ReferenceResolver define o comportamento de preservar referências sobre serialização e desserialização. Crie uma classe derivada para especificar o comportamento personalizado. Para um exemplo, consulte GuidReferenceResolver.
Não há suporte para alguns recursos Newtonsoft.Json
relacionados:
Para obter mais informações, consulte Preservar referências e lidar com referências circulares.
Dicionário sem chave de cadeia de caracteres
Ambos Newtonsoft.Json
e System.Text.Json
suportam coleções de tipo Dictionary<TKey, TValue>
. No entanto, em System.Text.Json
, TKey
deve ser um tipo primitivo, não um tipo personalizado. Para obter mais informações, consulte Tipos de chaves com suporte.
Cuidado
Desserializar para um Dictionary<TKey, TValue>
local em que TKey
é digitado como qualquer outra coisa que string
não possa introduzir uma vulnerabilidade de segurança no aplicativo de consumo. Para obter mais informações, confira dotnet/runtime#4761.
Tipos sem suporte interno
System.Text.Json não oferece suporte interno para os seguintes tipos:
- DataTable e tipos relacionados (para obter mais informações, consulte Tipos de coleção com suporte)
- ExpandoObject
- TimeZoneInfo
- BigInteger
- DBNull
- Type
- ValueTuple e seus tipos genéricos associados
Conversores personalizados podem ser implementados para tipos que não têm suporte interno.
Serialização polimórfica
Newtonsoft.Json
faz a serialização polimórfica automaticamente. A partir do .NET 7, System.Text.Json oferece suporte à serialização polimórfica por meio do atributo JsonDerivedTypeAttribute. Para obter mais informações, consulte Serializar propriedades de classes derivadas.
Desserialização polimórfica
Newtonsoft.Json
tem uma configuração TypeNameHandling
que adiciona metadados de nome de tipo ao JSON durante a serialização. Ele usa os metadados durante a desserialização para fazer a desserialização polimórfica. A partir do .NET 7, System.Text.Json depende de informações discriminadas de tipo para executar a desserialização polimórfica. Esses metadados são emitidos no JSON e, em seguida, usados durante a desserialização para determinar se deseja desserializar para o tipo base ou um tipo derivado. Para obter mais informações, consulte Serializar propriedades de classes derivadas.
Para oferecer suporte à desserialização polimórfica nas versões .NET anteriores, crie um conversor como o exemplo Como gravar conversores personalizados.
Desserializar os valores de enumeração da cadeia de caracteres
Por padrão, System.Text.Json não dá suporte para a desserialização de valores de enumeração de cadeia de caracteres, enquanto Newtonsoft.Json
dá. Por exemplo, o código a seguir gera um JsonException:
string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";
var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.
class MyObj
{
public string Text { get; set; } = "";
public MyEnum Enum { get; set; }
}
enum MyEnum
{
One,
Two,
Three
}
Entretanto, você pode habilitar a desserialização de valores de enumeração de cadeia de caracteres utilizando o conversor JsonStringEnumConverter. Para obter mais informações, consulte Enumerações como cadeia de caracteres.
Desserialização das propriedades do objeto
Quando Newtonsoft.Json
desserializa para Object:
- Infere o tipo de valores primitivos no conteúdo JSON (diferente de
null
) e retorna ostring
,long
,double
,boolean
ouDateTime
armazenado como um objeto em caixa. Valores primitivos são valores JSON únicos, como um número JSON, cadeia de caracteres,true
,false
ounull
. - Retorna um
JObject
ouJArray
para valores complexos no conteúdo JSON. Valores complexos são coleções de pares chave-valor JSON dentro de chaves ({}
) ou listas de valores entre colchetes ([]
). As propriedades e os valores dentro das chaves ou colchetes podem ter propriedades ou valores adicionais. - Retorna uma referência nula quando o conteúdo tem o literal JSON
null
.
System.Text.Json armazena um box para valores primitivos e complexos JsonElement
sempre que desserializar para Object, por exemplo:
- Uma propriedade
object
. - Um valor de dicionário
object
. - Um valor de matriz
object
. - Um
object
raiz.
No entanto, System.Text.Json
trata null
o mesmo que Newtonsoft.Json
e retorna uma referência nula quando o conteúdo tem o literal JSON null
.
Para implementar a inferência de tipo para propriedades object
, crie um conversor como o exemplo em Como gravar conversores personalizados.
Desserializar o tipo nulo para não anulável
Newtonsoft.Json
não gera uma exceção no seguinte cenário:
NullValueHandling
é definido comoIgnore
, e- Durante a desserialização, o JSON contém um valor nulo para um tipo de valor não anulável.
No mesmo cenário, System.Text.Json gera uma exceção. (A configuração correspondente de tratamento nulo em System.Text.Json
é JsonSerializerOptions.IgnoreNullValues = true
.)
Se você possui o tipo de destino, a melhor solução alternativa é tornar essa propriedade anulável (por exemplo, alterar int
para int?
).
Outra solução alternativa é criar um conversor para o tipo, como o exemplo a seguir que lida com valores nulos para tipos DateTimeOffset
:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
reader.TokenType == JsonTokenType.Null
? default
: reader.GetDateTimeOffset();
public override void Write(
Utf8JsonWriter writer,
DateTimeOffset dateTimeValue,
JsonSerializerOptions options) =>
writer.WriteStringValue(dateTimeValue);
}
}
Registre esse conversor personalizado usando um atributo na propriedade ou adicionando o conversor à coleção Converters.
Nota: o conversor anterior manipula valores nulos de forma diferente do que Newtonsoft.Json
para POCOs que especificam valores padrão. Por exemplo, suponha que o código a seguir represente seu objeto de destino:
public class WeatherForecastWithDefault
{
public WeatherForecastWithDefault()
{
Date = DateTimeOffset.Parse("2001-01-01");
Summary = "No summary";
}
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
}
E suponha que o JSON a seguir é desserializado usando o conversor anterior:
{
"Date": null,
"TemperatureCelsius": 25,
"Summary": null
}
Após a desserialização, a propriedade Date
tem 1/1/0001 (default(DateTimeOffset)
) ou seja, o valor definido no construtor é substituído. Considerando o mesmo POCO e JSON, a desserialização Newtonsoft.Json
deixaria 1/1/2001 na propriedade Date
.
Desserializar para classes e structs imutáveis
Newtonsoft.Json
pode desserializar para classes e structs imutáveis porque pode usar construtores que têm parâmetros.
No System.Text.Json, use o atributo [JsonConstructor] para especificar o uso de um construtor parametrizado. Os registros no C# 9 também são imutáveis e têm suporte como destinos de desserialização. Para obter mais informações, consulte Tipos imutáveis e Registros.
Propriedades obrigatórias
No Newtonsoft.Json
, você especifica que uma propriedade é necessária pela configuração Required
no atributo [JsonProperty]
. Newtonsoft.Json
gera uma exceção se nenhum valor for recebido no JSON para uma propriedade marcada como necessária.
A partir do .NET 7, você pode usar o modificador C# required
ou o atributo JsonRequiredAttribute em uma propriedade necessária. System.Text.Json gerará uma exceção se o conteúdo JSON não contiver um valor para a propriedade marcada. Para obter mais informações, consulte Propriedades necessárias.
System.Text.Json não gera uma exceção se nenhum valor for recebido para uma das propriedades do tipo de destino. Por exemplo, se você tiver uma classe WeatherForecast
:
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
O JSON a seguir é desserializado sem erro:
{
"TemperatureCelsius": 25,
"Summary": "Hot"
}
Para que a desserialização não ocorra, se nenhuma propriedade Date
estiver no JSON, escolha uma das seguintes opções:
- Use o .NET 7 ou a versão posterior do System.Text.Json pacote e adicione o modificador
required
(disponível a partir do C# 11) ou o atributo JsonRequiredAttribute à propriedade . - Implemente um conversor personalizado.
- Implemente um
OnDeserialized
retorno de chamada (.NET 6 e posterior).
O exemplo de código de conversor a seguir gera uma exceção se a propriedade Date
não for definida após a conclusão da desserialização:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class WeatherForecastRequiredPropertyConverter : JsonConverter<WeatherForecast>
{
public override WeatherForecast Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
// Don't pass in options when recursively calling Deserialize.
WeatherForecast forecast = JsonSerializer.Deserialize<WeatherForecast>(ref reader)!;
// Check for required fields set by values in JSON
return forecast!.Date == default
? throw new JsonException("Required property not received in the JSON")
: forecast;
}
public override void Write(
Utf8JsonWriter writer,
WeatherForecast forecast, JsonSerializerOptions options)
{
// Don't pass in options when recursively calling Serialize.
JsonSerializer.Serialize(writer, forecast);
}
}
}
Registre esse conversor personalizado adicionando o conversor à coleção JsonSerializerOptions.Converters.
Esse padrão de chamada recursiva do conversor requer que o registro do conversor usando JsonSerializerOptions e não um atributo. Se você registrar o conversor usando um atributo, o conversor personalizado chama recursivamente para si mesmo. O resultado é um loop infinito que termina em uma exceção de excedente de pilha.
Ao registrar o conversor usando o objeto de opções, evite um loop infinito que não passa o objeto de opções na chamada recursiva Serialize ou Deserialize. O objeto de opções contém a coleção Converters. Se você passar para Serialize
ou Deserialize
, o conversor personalizado chama para si mesmo, fazendo um loop infinito que resulta em uma exceção excedente de pilha. Se as opções padrão não forem viáveis, crie uma nova instância das opções com as configurações necessárias. Essa abordagem será lenta, pois cada nova instância armazena em cache de forma independente.
Há um padrão alternativo que pode usar o registro JsonConverterAttribute
na classe a ser convertida. Nessa abordagem, o código do conversor chama Serialize
ou Deserialize
em uma classe que deriva da classe a ser convertida. A classe derivada não tem um JsonConverterAttribute
aplicado. No exemplo a seguir desta alternativa:
WeatherForecastWithRequiredPropertyConverterAttribute
é a classe a ser desserializada e temJsonConverterAttribute
aplicado.WeatherForecastWithoutRequiredPropertyConverterAttribute
é a classe derivada que não tem o atributo conversor.- O código no conversor chama
Serialize
eDeserialize
emWeatherForecastWithoutRequiredPropertyConverterAttribute
para evitar um loop infinito. Há um custo de desempenho para essa abordagem na serialização devido a uma instanciação de objeto extra e à cópia de valores de propriedade.
Veja aqui os tipos WeatherForecast*
:
[JsonConverter(typeof(WeatherForecastRequiredPropertyConverterForAttributeRegistration))]
public class WeatherForecastWithRequiredPropertyConverterAttribute
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
public class WeatherForecastWithoutRequiredPropertyConverterAttribute :
WeatherForecastWithRequiredPropertyConverterAttribute
{
}
E aqui está o conversor:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class WeatherForecastRequiredPropertyConverterForAttributeRegistration :
JsonConverter<WeatherForecastWithRequiredPropertyConverterAttribute>
{
public override WeatherForecastWithRequiredPropertyConverterAttribute Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
// OK to pass in options when recursively calling Deserialize.
WeatherForecastWithRequiredPropertyConverterAttribute forecast =
JsonSerializer.Deserialize<WeatherForecastWithoutRequiredPropertyConverterAttribute>(
ref reader,
options)!;
// Check for required fields set by values in JSON.
return forecast!.Date == default
? throw new JsonException("Required property not received in the JSON")
: forecast;
}
public override void Write(
Utf8JsonWriter writer,
WeatherForecastWithRequiredPropertyConverterAttribute forecast,
JsonSerializerOptions options)
{
var weatherForecastWithoutConverterAttributeOnClass =
new WeatherForecastWithoutRequiredPropertyConverterAttribute
{
Date = forecast.Date,
TemperatureCelsius = forecast.TemperatureCelsius,
Summary = forecast.Summary
};
// OK to pass in options when recursively calling Serialize.
JsonSerializer.Serialize(
writer,
weatherForecastWithoutConverterAttributeOnClass,
options);
}
}
}
O conversor de propriedades exigiria lógica adicional se você precisasse lidar com atributos como [JsonIgnore] ou opções diferentes, como codificadores personalizados. Além disso, o código de exemplo não lida com as propriedades para as quais um valor padrão é definido no construtor. E essa abordagem não diferencia os seguintes cenários:
- Uma propriedade está ausente do JSON.
- Uma propriedade para um tipo não anulável está presente no JSON, mas o valor é o padrão para o tipo, como zero para um
int
. - Uma propriedade para um tipo de valor anulável está presente no JSON, mas o valor é nulo.
Observação
Se você estiver usando System.Text.Json de um controlador de ASP.NET Core, pode usar um atributo [Required]
nas propriedades da classe de modelo em vez de implementar um conversor System.Text.Json.
Especificar formato de data
Newtonsoft.Json
oferece várias formas de controlar como as propriedades de tipos DateTime
e DateTimeOffset
são serializados e desserializados:
- A configuração
DateTimeZoneHandling
pode ser usada para serializar todos os valoresDateTime
como datas UTC. - A configuração
DateFormatString
e os conversoresDateTime
podem ser usados para personalizar o formato das cadeias de caracteres de data.
System.Text.Json dá suporte à ISO 8601-1:2019, incluindo o perfil RFC 3339. Esse formato é amplamente adotado, inequívoco e faz viagens de ida e volta com precisão. Para usar qualquer outro formato, crie um conversor personalizado. Por exemplo, os conversores a seguir serializam e desserializam o JSON que usa o formato Unix epoch com ou sem um deslocamento de fuso horário (valores como /Date(1590863400000-0700)/
ou /Date(1590863400000)/
):
sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
static readonly DateTimeOffset s_epoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);
if (
!match.Success
|| !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
|| !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
|| !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
{
throw new JsonException();
}
int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
TimeSpan utcOffset = new(hours * sign, minutes * sign, 0);
return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
}
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
TimeSpan utcOffset = value.Offset;
string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");
writer.WriteStringValue(formatted);
}
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);
if (
!match.Success
|| !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
{
throw new JsonException();
}
return s_epoch.AddMilliseconds(unixTime);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime})/");
writer.WriteStringValue(formatted);
}
}
Para obter mais informações, confira Suporte para DateTime e DateTimeOffset em System.Text.Json.
Retornos de chamada
Newtonsoft.Json
permite executar código personalizado em vários pontos no processo de serialização ou desserialização:
- OnDeserializing (ao começar a desserializar um objeto)
- OnDeserializing (ao terminar de desserializar um objeto)
- OnSerializing (ao começar a serializar um objeto)
- OnSerializing (ao terminar de serializar um objeto)
System.Text.Json expõe as mesmas notificações durante a serialização e desserialização. Para usá-las, implemente uma ou mais das seguintes interfaces do namespace System.Text.Json.Serialization:
Aqui está um exemplo que verifica uma propriedade nula e grava mensagens no início e no final da serialização e desserialização:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Callbacks
{
public class WeatherForecast :
IJsonOnDeserializing, IJsonOnDeserialized,
IJsonOnSerializing, IJsonOnSerialized
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
void IJsonOnDeserializing.OnDeserializing() => Console.WriteLine("\nBegin deserializing");
void IJsonOnDeserialized.OnDeserialized()
{
Validate();
Console.WriteLine("Finished deserializing");
}
void IJsonOnSerializing.OnSerializing()
{
Console.WriteLine("Begin serializing");
Validate();
}
void IJsonOnSerialized.OnSerialized() => Console.WriteLine("Finished serializing");
private void Validate()
{
if (Summary is null)
{
Console.WriteLine("The 'Summary' property is 'null'.");
}
}
}
public class Program
{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
};
string jsonString = JsonSerializer.Serialize(weatherForecast);
Console.WriteLine(jsonString);
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
Console.WriteLine($"Date={weatherForecast?.Date}");
Console.WriteLine($"TemperatureCelsius={weatherForecast?.TemperatureCelsius}");
Console.WriteLine($"Summary={weatherForecast?.Summary}");
}
}
}
// output:
//Begin serializing
//The 'Summary' property is 'null'.
//Finished serializing
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":null}
//Begin deserializing
//The 'Summary' property is 'null'.
//Finished deserializing
//Date=8/1/2019 12:00:00 AM
//TemperatureCelsius = 25
//Summary=
O código OnDeserializing
não tem acesso à nova instância POCO. Para lidar com a nova instância POCO no início da desserialização, coloque esse código no construtor POCO.
Suporte para setters e getters de propriedades não públicas
Newtonsoft.Json
pode usar setters e getters de propriedade privada e interna por meio do atributo JsonProperty
.
System.Text.Json suporta setters e getters de propriedade privada e interna por meio do atributo [JsonInclude]. Para obter o código de exemplo, consulte Acessadores de propriedade não pública.
Preencher objetos existentes
O método JsonConvert.PopulateObject
no Newtonsoft.Json
desserializa um documento JSON para uma instância existente de uma classe, em vez de criar uma nova instância. System.Text.Json sempre cria uma nova instância do tipo de destino usando o construtor público sem parâmetros padrão. Conversores personalizados podem desserializar para uma instância existente.
Reutilizar em vez de substituir propriedades
A partir do .NET 8, System.Text.Json dá suporte à reutilização de propriedades inicializadas em vez de substituí-las. Há algumas diferenças de comportamento, sobre as quais você pode ler na proposta de API.
Para obter mais informações, consulte Popular propriedades inicializadas.
A configuração ObjectCreationHandling
em Newtonsoft.Json
permite especificar que os objetos nas propriedades devem ser reutilizados em vez de substituídos durante a desserialização. System.Text.Json sempre substitui objetos nas propriedades. Conversores personalizados podem fornecer essa funcionalidade ou você pode atualizar para o .NET 8, que fornece funcionalidade de preenchimento.
Preencher propriedades sem setters
A partir do .NET 8, System.Text.Json dá suporte à população de propriedades, incluindo aquelas que não têm um setter. Para obter mais informações, consulte Popular propriedades inicializadas.
Durante a desserialização, Newtonsoft.Json
adiciona objetos a uma coleção mesmo que a propriedade não tenha nenhum setter. System.Text.Json ignora propriedades que não têm setters. Conversores personalizados podem fornecer essa funcionalidade ou você pode atualizar para o .NET 8, que pode preencher propriedades somente leitura.
Política de nomenclatura Snake case
System.Text.Json inclui uma política de nomenclatura interna para snake case. No entanto, há algumas diferenças de comportamento com Newtonsoft.Json
para algumas entradas. A tabela a seguir mostra algumas dessas diferenças ao converter a entrada usando a política JsonNamingPolicy.SnakeCaseLower.
Entrada | resultado Newtonsoft.Json | resultado System.Text.Json |
---|---|---|
"AB1" | "a_b1" | "ab1" |
"SHA512Managed" | "sh_a512_managed" | "sha512_managed" |
"abc123DEF456" | "abc123_de_f456" | "abc123_def456" |
"KEBAB-CASE" | "keba_b-_case" | "kebab-case" |
A única política de nomenclatura de propriedade interna no System.Text.Json é camel case. Newtonsoft.Json
pode converter nomes de propriedade em snake case (underline case). Uma política de nomenclatura personalizada pode fornecer essa funcionalidade ou atualizar para o .NET 8 ou posterior, que inclui políticas internas de nomenclatura snake case.
Atributos System.Runtime.Serialization
System.Runtime.Serialization atributos como DataContractAttribute, DataMemberAttributee IgnoreDataMemberAttribute permitem que você defina um contrato de dados. Um contrato de dados é um contrato formal entre um serviço e um cliente que descreve abstratamente os dados a serem trocados. O contrato de dados define precisamente quais propriedades são serializadas para troca.
System.Text.Json não tem suporte interno para esses atributos. No entanto, a partir do .NET 7, você pode usar um resolvedor de tipo personalizado para adicionar suporte. Para obter um exemplo, confira ZCS. DataContractResolver.
Números octais
Newtonsoft.Json
trata números com um zero à esquerda como números octais. System.Text.Json não permite zeros à esquerda porque não é aceita pela especificação RFC 8259.
Manipular membros ausentes
Se o JSON que está sendo desserializado incluir propriedades ausentes no tipo de destino, Newtonsoft.Json
poderá ser configurado para gerar exceções. Por padrão, System.Text.Json ignora as propriedades extras no JSON, exceto quando você usa o atributo [JsonExtensionData].
No .NET 8 e em versões posteriores, você pode definir sua preferência para ignorar ou não permitir propriedades JSON desmapeadas utilizando um dos seguintes meios:
- Aplique o atributo JsonUnmappedMemberHandlingAttribute ao tipo para o qual você está desserializando.
- Para definir sua preferência globalmente, defina a propriedade JsonSerializerOptions.UnmappedMemberHandling. Ou, para a geração de origens, defina a propriedade JsonSourceGenerationOptionsAttribute.UnmappedMemberHandling e aplique o atributo à sua classe JsonSerializerContext.
- Personalizar a propriedade JsonTypeInfo.UnmappedMemberHandling.
JsonObjectAttribute
Newtonsoft.Json
tem um atributo, JsonObjectAttribute
, que pode ser aplicado no nível do tipo para controlar quais membros são serializados, como os valores null
são tratados e se todos os membros são necessários. System.Text.Json não tem nenhum atributo equivalente que possa ser aplicado a um tipo. Para alguns comportamentos, como manipulação de valor do null
, você pode configurar o mesmo comportamento no JsonSerializerOptions global ou individualmente em cada propriedade.
Considere o seguinte exemplo que usa Newtonsoft.Json.JsonObjectAttribute
para especificar que todas as propriedades null
devem ser ignoradas:
[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Person { ... }
No System.Text.Json, você pode definir o comportamento para todos os tipos e propriedades:
JsonSerializerOptions options = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
string json = JsonSerializer.Serialize<Person>(person, options);
Ou você pode definir o comportamento em cada propriedade separadamente:
public class Person
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Name { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public int? Age { get; set; }
}
Em seguida, considere o seguinte exemplo que usa Newtonsoft.Json.JsonObjectAttribute
para especificar que todas as propriedades de membro devem estar presentes no JSON:
[JsonObject(ItemRequired = Required.Always)]
public class Person { ... }
Você pode obter o mesmo comportamento em System.Text.Json adicionando o modificador C# required
ou o JsonRequiredAttribute a cada propriedade. Para obter mais informações, consulte Propriedades necessárias.
public class Person
{
[JsonRequired]
public string? Name { get; set; }
public required int? Age { get; set; }
}
TraceWriter
Newtonsoft.Json
permite que você depurar usando um TraceWriter
para exibir logs gerados por serialização ou desserialização. System.Text.Json não faz registro em log.
JsonDocument e JsonElement comparados ao JToken (como JObject, JArray)
System.Text.Json.JsonDocument oferece a capacidade de analisar e criar um DOM (Modelo de Objeto do Documento) somente leitura a partir de conteúdos JSON existentes. O DOM oferece acesso aleatório aos dados em um conteúdo JSON. Os elementos JSON que compõem o conteúdo podem ser acessados pelo tipo JsonElement. O tipo JsonElement
fornece APIs para converter texto JSON em tipos comuns do .NET. JsonDocument
mostra uma propriedade RootElement.
A partir do .NET 6, você pode analisar e criar um DOM mutável com conteúdos JSON existentes usando o tipo JsonNode e outros tipos no namespace System.Text.Json.Nodes. Para obter mais informações, consulte Usar JsonNode
.
JsonDocument é IDisposable
JsonDocument
cria uma exibição na memória dos dados em um buffer em pool. Portanto, diferente de JObject
ou JArray
de Newtonsoft.Json
, o tipo JsonDocument
implementa IDisposable
e precisa ser usado dentro de um bloco de uso. Para obter mais informações, consulte JsonDocument é IDisposable.
JsonDocument é somente leitura
O DOM de System.Text.Json não pode adicionar, remover ou modificar elementos JSON. Ele foi projetado dessa forma para melhorar o desempenho e reduzir as alocações para analisar tamanhos comuns de conteúdo JSON (ou seja, < 1 MB).
JsonElement é um struct de união
JsonDocument
expõe a RootElement
como uma propriedade do tipo JsonElement, que é um tipo de struct de união que inclui qualquer elemento JSON. Newtonsoft.Json
usa tipos hierárquicos dedicados, como JObject
, JArray
, JToken
e assim por diante. JsonElement
é o que você pode pesquisar e enumerar e pode usar JsonElement
para materializar elementos JSON em tipos .NET.
A partir do .NET 6, você pode usar tipo JsonNode e tipos no namespace System.Text.Json.Nodes que correspondem a JObject
, JArray
e JToken
. Para obter mais informações, consulte Usar JsonNode
.
Como pesquisar um JsonDocument e JsonElement como subelementos
Pesquisa tokens JSON usando JObject
ou JArray
de Newtonsoft.Json
costumam ser relativamente rápidos porque são pesquisas em algum dicionário. Em comparação, as pesquisas no JsonElement
exigem uma pesquisa sequencial das propriedades e, portanto, são relativamente lentas (por exemplo, ao usar TryGetProperty
). System.Text.Json foi criado para minimizar o tempo inicial de análise em vez do tempo de pesquisa. Para obter mais informações, consulte Como pesquisar um JsonDocument e JsonElement em busca de subelementos.
Utf8JsonReader vs. JsonTextReader
System.Text.Json.Utf8JsonReader é um leitor de alto desempenho, baixa alocação e somente para encaminhamento para texto JSON codificado em UTF-8, lido a partir de um ReadOnlySpan<byte> ou ReadOnlySequence<byte>. O Utf8JsonReader
é um tipo de baixo nível que pode ser usado para criar analisadores e desserializadores personalizados.
Utf8JsonReader é um ref struct
O JsonTextReader
no Newtonsoft.Json
é uma classe. O tipo Utf8JsonReader
difere porque é um ref struct. Para obter mais informações, confira limitações de struct ref para Utf8JsonReader.
Ler valores nulos em tipos de valor anulável
Newtonsoft.Json
fornece APIs que retornam Nullable<T>, como ReadAsBoolean
, que identifica um Null
TokenType
para você retornando um bool?
. As APIs internas de System.Text.Json
retornam apenas tipos de valor não anuláveis. Para obter mais informações, consulte Ler valores nulos em tipos de valor anulável.
Vários destinos para leitura de JSON
Se você precisa continuar usando o Newtonsoft.Json
para determinadas estruturas de destino, é possível ter vários destinos e duas implementações. No entanto, não é comum e exigiria algumas #ifdefs
e duplicação de origem. Uma forma de compartilhar o máximo de código possível é criar um wrapper ref struct
próximo a Utf8JsonReader e Newtonsoft.Json.JsonTextReader
. Esse wrapper unificaria a área de superfície pública ao isolar as diferenças comportamentais. Assim, é possível isolar as alterações principalmente na construção do tipo, junto com a aprovação do novo tipo por referência. Esse é o padrão que a biblioteca Microsoft.Extensions.DependencyModel segue:
Utf8JsonWriter vs. JsonTextWriter
System.Text.Json.Utf8JsonWriter é uma forma de alto desempenho para escrita de texto JSON codificado em UTF-8 com base em tipos .NET comuns como String
, Int32
e DateTime
. O gravador é um tipo de baixo nível que pode ser usado para criar serializadores personalizados.
Gravar valores brutos
Newtonsoft.Json
tem um método WriteRawValue
que grava JSON bruto onde um valor é esperado. System.Text.Json tem um equivalente direto: Utf8JsonWriter.WriteRawValue. Para obter mais informações, consulte Gravar JSON bruto.
Personalizar o formato JSON
JsonTextWriter
inclui as seguintes configurações, para as quais Utf8JsonWriter não tem equivalente:
- QuoteChar – especifica o caractere a ser usado para envolver os valores de cadeia de caracteres.
Utf8JsonWriter
sempre usa aspas duplas. - QuoteName – especifica se deve ou não envolver nomes de propriedades com aspas.
Utf8JsonWriter
sempre os cerca com aspas.
A partir do .NET 9, você pode personalizar o caractere e o tamanho de recuo Utf8JsonWriter usando as opções expostas pelo struct JsonWriterOptions:
JsonTextWriter
inclui as seguintes configurações, para as quais Utf8JsonWriter
não tem equivalente:
- Recuo – especifica quantos caracteres devem ser recuados.
Utf8JsonWriter
sempre recuou por 2 caracteres. - IndentChar – especifica o caractere a ser usado para recuo.
Utf8JsonWriter
sempre usa espaço em branco. - QuoteChar – especifica o caractere a ser usado para envolver os valores de cadeia de caracteres.
Utf8JsonWriter
sempre usa aspas duplas. - QuoteName – especifica se deve ou não envolver nomes de propriedades com aspas.
Utf8JsonWriter
sempre os cerca com aspas.
Não há soluções alternativas que permitem personalizar o JSON produzido pelo Utf8JsonWriter
dessa forma.
Gravar valores de Timespan, Uri ou char
JsonTextWriter
fornece métodos WriteValue
para valores de TimeSpan, Uri e char. Utf8JsonWriter
não tem métodos equivalentes. Em vez disso, formate esses valores como cadeias de caracteres (com o nome ToString()
, por exemplo) e chame WriteStringValue.
Vários destinos para gravação JSON
Se você precisa continuar usando o Newtonsoft.Json
para determinadas estruturas de destino, é possível ter vários destinos e duas implementações. No entanto, não é comum e exigiria algumas #ifdefs
e duplicação de origem. Uma forma de compartilhar o máximo de código possível é criar um wrapper circundando Utf8JsonWriter e Newtonsoft.Json.JsonTextWriter
. Esse wrapper unificaria a área de superfície pública ao isolar as diferenças comportamentais. Isso permite isolar as alterações principalmente na construção do tipo. A biblioteca Microsoft.Extensions.DependencyModel é:
TypeNameHandling.All não tem suporte
A decisão de excluir a funcionalidade equivalente TypeNameHandling.All
de System.Text.Json
foi intencional. Permitir que um conteúdo JSON especifique suas próprias informações de tipo é uma fonte comum de vulnerabilidades em aplicativos Web. Em particular, a configuração de Newtonsoft.Json
com TypeNameHandling.All
permite que o cliente remoto insira um aplicativo executável inteiro dentro do próprio conteúdo JSON, de modo que, durante a desserialização, o aplicativo Web extraia e executa o código inserido. Para obter mais informações, consulte Friday the 13th JSON attacks PowerPoint e Friday the 13th JSON attacks details.
Consultas de caminho JSON sem suporte
O DOM de JsonDocument
não dá suporte à consulta usando o Caminho JSON.
Em um DOM de JsonNode, cada instância JsonNode
tem um método GetPath
que retorna um caminho para esse nó. Mas não há nenhuma API interna para lidar com consultas com base em cadeias de consulta de caminho JSON.
Para obter mais informações, consulte o problema do GitHub dotnet/runtime #31068.
Alguns limites não configuráveis
System.Text.Json define limites que não podem ser alterados para alguns valores, como o tamanho máximo do token em caracteres (166 MB) e na base 64 (125 MB). Para obter mais informações, consulte JsonConstants
no código-fonte e o problema do GitHub dotnet/runtime #39953.
NaN, Infinity, -Infinity
Newtonsoft analisa tokens de string JSON NaN
, Infinity
e -Infinity
. Com System.Text.Json, use JsonNumberHandling.AllowNamedFloatingPointLiterals. Para obter informações sobre como usar essa configuração, consulte Permitir ou gravar números entre aspas.
Usar o GitHub Copilot para migrar
Você pode obter ajuda de codificação do GitHub Copilot para converter seu código no IDE.
Se você estiver usando o Visual Studio 2022 versão 17.8 ou posterior, poderá experimentar o GitHub Copilot no Visual Studio orientado por IA para ajudar a migrar seu código Newtonsoft.Json
para usar o System.Text.Json
. Envie sua pergunta e o código a ser convertido como um prompt na janela de bate-papo do Copilot, como no exemplo a seguir.
Observação
O GitHub Copilot é alimentado por IA, portanto é passível de surpresas e erros. Verifique qualquer código gerado ou sugestões. Para obter mais informações sobre o uso geral do GitHub Copilot, o impacto do produto, a supervisão humana e a privacidade, consulte Perguntas frequentes sobre o GitHub Copilot.
Exemplo de solicitação para o Chat do Copilot
convert the following code to use System.Text.Json
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonConvert.SerializeObject(product);
Console.WriteLine(output);
Exemplo de resposta do Chat do Copilot
To convert the code to use System.Text.Json, you can use the JsonSerializer class. Here's the updated code:
using System;
using System.Text.Json;
public class Product
{
public string Name { get; set; }
public DateTime ExpiryDate { get; set; }
public decimal Price { get; set; }
public string[] Sizes { get; set; }
}
public class Program
{
public static void Main()
{
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonSerializer.Serialize(product);
Console.WriteLine(output);
}
}
This code uses System.Text.Json.JsonSerializer.Serialize to convert the Product object to a JSON string.
Observe que seus resultados podem ser diferentes do que é mostrado nessas respostas de exemplo. Os modelos de IA não são determinísticos, ou seja, eles podem retornar respostas diferentes quando fazem a mesma pergunta. Isso pode ser devido a aprendizado e adaptação adicionais ao longo do tempo, variação de idioma, mudanças no contexto, como seu histórico de bate-papo e muito mais.
Você pode usar recursos de bate-papo, como comandos de barra, referências e threads, para definir a intenção e obter melhores respostas com o contexto com escopo.
Por exemplo, se o arquivo de código filename
estiver aberto no IDE, você poderá fazer referência ao arquivo no prompt do Chat do Copilot com "converter #filename
para usar System.Text.Json
". Ou você pode fazer referência à solução com "converter @workspace
para usar System.Text.Json
" na janela de chat ou no chat embutido.