Modos de geração de fonte em System.Text.Json

A geração de origem pode ser usada em dois modos: otimização baseada em metadados e serialização. Este artigo descreve os diferentes modos.

Para obter informações sobre como usar os modos de geração de origem, consulte Como usar a geração de origem em System.Text.Json.

Modo baseado em metadados

Você pode usar a geração de origem para mover o processo de coleta de metadados do runtime para o tempo de compilação. Durante a compilação, os metadados são coletados e os arquivos de código-fonte são gerados. Os arquivos de código-fonte gerados são compilados automaticamente como parte integrante do aplicativo. Essa técnica elimina a coleção de metadados de runtime, o que melhora o desempenho da serialização e desserialização.

As melhorias de desempenho fornecidas pela geração de fonte podem ser substanciais. Por exemplo, os resultados do teste mostraram uma redução de até 40% ou mais no tempo de inicialização, redução da memória privada, aumento da taxa de transferência (no modo de otimização de serialização) e redução do tamanho do aplicativo.

Construtores e membros não públicos

Por padrão, tanto o modo de reflexão quanto o modo de geração de código-fonte incluem no contrato de serialização apenas propriedades e campos public.

A partir do .NET 11, a geração de origem dá suporte a membros que você marca explicitamente com o atributo [JsonInclude]. O membro pode ser private, internalou protected. Ele também oferece suporte a acessores private, internal e protected em propriedades que você marca com [JsonInclude]. A geração de origem também dá suporte a construtores inacessíveis marcados com [JsonConstructor].

No .NET 11, os acessadores gerados usam UnsafeAccessorAttribute.

Um setter gerado a partir do código-fonte para uma propriedade que só pode ser definida como init é executado somente quando o payload JSON contém essa propriedade. Uma propriedade que só pode ser definida como init e que o payload omite mantém o valor do seu inicializador de propriedade.

No .NET 10 e versões anteriores, a geração de origem tem as seguintes limitações:

  • A geração a partir do código-fonte não suporta membros ou acessadores de private ou protected. Se você marcar esse tipo de membro com [JsonInclude], o serializador lançará uma NotSupportedException em tempo de execução.
  • A geração a partir do código-fonte suporta membros e acessadores de internal somente quando eles são acessíveis ao JsonSerializerContext gerado no mesmo assembly.
  • A geração de origem não dá suporte a construtores inacessíveis ao contexto gerado, mesmo quando você os marca com [JsonConstructor].

Problemas conhecidos

Para obter informações sobre outros problemas conhecidos com geração de origem, consulte os problemas do GitHub rotulados como "gerador de origem" no repositório dotnet/runtime.

Modo serialização-otimização (caminho rápido)

JsonSerializer possui muitos recursos que personalizam a saída da serialização, como políticas de nomenclatura e preservação de referências. O suporte para todos esses recursos causa alguma sobrecarga de desempenho. A geração de origem pode melhorar o desempenho de serialização gerando código otimizado que usa Utf8JsonWriter diretamente.

O modo de otimização de serialização emite métodos de serialização de caminho rápido, mas não metadados de serialização. A serialização de caminho rápido é restrita no que pode fazer; ele não dá suporte à serialização assíncrona ou a qualquer modo de desserialização.

Além disso, o código otimizado não oferece suporte a todos os recursos de serialização que o JsonSerializer suporta. O serializador detecta se o código otimizado pode ser usado e retorna ao código de serialização padrão se as opções sem suporte forem especificadas. Por exemplo, JsonNumberHandling.AllowReadingFromString não é aplicável à escrita de código, portanto, especificar essa opção não aciona um retorno ao código padrão.

A tabela a seguir mostra para quais opções em JsonSerializerOptions há suporte para serialização de caminho rápido:

Opção de serialização Com suporte para caminho rápido
AllowTrailingCommas ✔️
Converters ❌
DefaultBufferSize ✔️
DefaultIgnoreCondition ✔️
DictionaryKeyPolicy ❌
Encoder ❌
IgnoreNullValues ❌
IgnoreReadOnlyFields ✔️
IgnoreReadOnlyProperties ✔️
IncludeFields ✔️
MaxDepth ✔️
NumberHandling ❌
PropertyNamingPolicy ✔️
ReferenceHandler ❌
TypeInfoResolver ✔️
WriteIndented ✔️

(Não há suporte para as seguintes opções porque elas se aplicam apenas à desserialização: PropertyNameCaseInsensitive, ReadCommentHandling e UnknownTypeHandling.)

A tabela a seguir mostra quais atributos são compatíveis com a serialização de caminho rápido:

Atributo Com suporte para caminho rápido
JsonConstructorAttribute ❌
JsonConverterAttribute ❌
JsonDerivedTypeAttribute ✔️
JsonExtensionDataAttribute ❌
JsonIgnoreAttribute ✔️
JsonIncludeAttribute ✔️
JsonNumberHandlingAttribute ❌
JsonPolymorphicAttribute ✔️
JsonPropertyNameAttribute ✔️
JsonPropertyOrderAttribute ✔️
JsonRequiredAttribute ✔️

Se uma opção ou atributo sem suporte for especificado para um tipo, o serializador retornará ao modo de metadados, supondo que o gerador de origem tenha sido configurado para gerar metadados. Nesse caso, o código otimizado não é usado ao serializar esse tipo, mas pode ser usado para outros tipos. Portanto, é importante fazer testes de desempenho com suas opções e cargas de trabalho para determinar quanto benefício você pode realmente obter do modo de otimização de serialização. Além disso, a capacidade de voltar ao JsonSerializer código requer o modo de metadados. Se você selecionar apenas o modo de otimização de serialização, a serialização poderá falhar para tipos ou opções que precisam retornar ao código JsonSerializer.

Confira também