serialização

Escolha o formato de serialização correto para aplicações .NET. Prefira formatos baseados em esquema (Protobuf, MessagePack) em vez de reflexão (Newtonsoft.Json). Use System.Text.Json com source generators AOT para cenários JSON.

O que é serialização?

Esta habilidade (skill) orienta desenvolvedores .NET na escolha e implementação do formato de serialização mais adequado para cada cenário, priorizando segurança, desempenho e compatibilidade com AOT. Ela cobre os principais formatos — System.Text.Json com source generators, Protocol Buffers (Protobuf) e MessagePack — explicando quando e como utilizá-los. A documentação inclui exemplos práticos de código C#, boas práticas para versionamento e evolução de esquemas, além de um guia de migração de Newtonsoft.Json para System.Text.Json. Também aborda serialização em sistemas de atores como Akka.NET. É ideal para plataformas como .NET Core, .NET 5+ e Native AOT, sendo relevante para ferramentas de desenvolvimento como IDEs e editores de código com suporte a AI.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/serialization

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Serialização no .NET

Quando Usar Esta Habilidade

Use esta habilidade quando:

  • Escolher um formato de serialização para APIs, mensageria ou persistência
  • Migrar de Newtonsoft.Json para System.Text.Json
  • Implementar serialização compatível com AOT
  • Projetar formatos de transmissão para sistemas distribuídos
  • Otimizar o desempenho da serialização

Baseado em Esquema vs Baseado em Reflexão

AspectoBaseado em EsquemaBaseado em Reflexão
ExemplosProtobuf, MessagePack, System.Text.Json (source gen)Newtonsoft.Json, BinaryFormatter
Informação de tipo no payloadNão (esquema externo)Sim (nomes de tipo embutidos)
VersionamentoNúmeros/nomes de campo explícitosImplícito (estrutura do tipo)
DesempenhoRápido (sem reflexão)Mais lento (reflexão em tempo real)
Compatível com AOTSimNão
Compatibilidade de transmissãoExcelenteRuim

Recomendação: Use serialização baseada em esquema para tudo que ultrapassa limites de processo.


Recomendações de Formato

Caso de UsoFormato RecomendadoMotivo
APIs RESTSystem.Text.Json (source gen)Padrão, compatível com AOT
gRPCProtocol BuffersFormato nativo, excelente versionamento
Mensageria de atoresMessagePack ou ProtobufCompacto, rápido, seguro para versões
Event sourcingProtobuf ou MessagePackDeve lidar com eventos antigos para sempre
CacheMessagePackCompacto, rápido
ConfiguraçãoJSON (System.Text.Json)Legível por humanos
Registro (logging)JSON (System.Text.Json)Estruturado, analisável

Formatos a Evitar

FormatoProblema
BinaryFormatterVulnerabilidades de segurança, obsoleto, nunca usar
Newtonsoft.Json padrãoNomes de tipo no payload quebram ao renomear
DataContractSerializerComplexo, versionamento pobre
XMLVerboso, lento, complexo

System.Text.Json com Source Generators

Para serialização JSON, use System.Text.Json com source generators para compatibilidade AOT e desempenho.

Configuração

// Defina um JsonSerializerContext com todos os seus tipos
[JsonSerializable(typeof(Order))]
[JsonSerializable(typeof(OrderItem))]
[JsonSerializable(typeof(Customer))]
[JsonSerializable(typeof(List<Order>))]
[JsonSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
public partial class AppJsonContext : JsonSerializerContext { }

Uso

// Serializar com contexto
var json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);

// Desserializar com contexto
var order = JsonSerializer.Deserialize(json, AppJsonContext.Default.Order);

// Configurar no ASP.NET Core
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});

Benefícios

  • Sem reflexão em tempo de execução - Toda informação de tipo gerada em tempo de compilação
  • Compatível com AOT - Funciona com publicação Native AOT
  • Mais rápido - Sem análise de tipo em tempo real
  • Trim-safe - O vinculador sabe exatamente o que é necessário

Protocol Buffers (Protobuf)

Melhor para: Sistemas de atores, gRPC, event sourcing, qualquer formato de transmissão de longa duração.

Configuração

dotnet add package Google.Protobuf
dotnet add package Grpc.Tools

Definir Esquema

// orders.proto
syntax = "proto3";

message Order {
    string id = 1;
    string customer_id = 2;
    repeated OrderItem items = 3;
    int64 created_at_ticks = 4;

    // Adicionar novos campos é sempre seguro
    string notes = 5;  // Adicionado na v2 - leitores antigos ignoram
}

message OrderItem {
    string product_id = 1;
    int32 quantity = 2;
    int64 price_cents = 3;
}

Regras de Versionamento

// SEGURO: Adicionar novos campos com novos números
message Order {
    string id = 1;
    string customer_id = 2;
    string shipping_address = 5;  // NOVO - seguro
}

// SEGURO: Remover campos (leitores antigos ignoram desconhecidos, novos usam padrão)
// Apenas pare de usar o campo, mantenha o número reservado
message Order {
    string id = 1;
    // customer_id removido, mas campo 2 está reservado
    reserved 2;
}

// INSEGURO: Mudar tipos de campo
message Order {
    int32 id = 1;  // Era: string - QUEBRA!
}

// INSEGURO: Reutilizar números de campo
message Order {
    reserved 2;
    string new_field = 2;  // Reutilizando 2 - QUEBRA!
}

MessagePack

Melhor para: Cenários de alto desempenho, payloads compactos, mensageria de atores.

Configuração

dotnet add package MessagePack
dotnet add package MessagePack.Annotations

Uso com Contratos

[MessagePackObject]
public sealed class Order
{
    [Key(0)]
    public required string Id { get; init; }

    [Key(1)]
    public required string CustomerId { get; init; }

    [Key(2)]
    public required IReadOnlyList<OrderItem> Items { get; init; }

    [Key(3)]
    public required DateTimeOffset CreatedAt { get; init; }

    // Novo campo - leitores antigos pulam chaves desconhecidas
    [Key(4)]
    public string? Notes { get; init; }
}

// Serializar
var bytes = MessagePackSerializer.Serialize(order);

// Desserializar
var order = MessagePackSerializer.Deserialize<Order>(bytes);

Configuração Compatível com AOT

// Use source generator para AOT
[MessagePackObject]
public partial class Order { }  // partial habilita source gen

// Configurar resolver
var options = MessagePackSerializerOptions.Standard
    .WithResolver(CompositeResolver.Create(
        GeneratedResolver.Instance,  // Gerado
        StandardResolver.Instance));

Migrando de Newtonsoft.Json

Problemas Comuns

NewtonsoftSystem.Text.JsonCorreção
$type no JSONNão suportado por padrãoUse discriminadores ou conversores personalizados
JsonPropertyJsonPropertyNameAtributo diferente
DefaultValueHandlingDefaultIgnoreConditionAPI diferente
NullValueHandlingDefaultIgnoreConditionAPI diferente
Setters privadosRequer [JsonInclude]Aceitação explícita
Polimorfismo[JsonDerivedType] (.NET 7+)Discriminadores explícitos

Padrão de Migração

// Newtonsoft (baseado em reflexão)
public class Order
{
    [JsonProperty("order_id")]
    public string Id { get; set; }

    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public string? Notes { get; set; }
}

// System.Text.Json (compatível com source-gen)
public sealed record Order(
    [property: JsonPropertyName("order_id")]
    string Id,

    string? Notes  // Tratamento de nulo via JsonSerializerOptions
);

[JsonSerializable(typeof(Order))]
[JsonSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
public partial class OrderJsonContext : JsonSerializerContext { }

Polimorfismo com Discriminadores

// .NET 7+ polimorfismo
[JsonDerivedType(typeof(CreditCardPayment), "credit_card")]
[JsonDerivedType(typeof(BankTransferPayment), "bank_transfer")]
public abstract record Payment(decimal Amount);

public sealed record CreditCardPayment(decimal Amount, string Last4) : Payment(Amount);
public sealed record BankTransferPayment(decimal Amount, string AccountNumber) : Payment(Amount);

// Serializa como:
// { "$type": "credit_card", "amount": 100, "last4": "1234" }

Padrões de Compatibilidade de Transmissão

Leitor Tolerante

Código antigo deve ignorar com segurança campos desconhecidos:

// Protobuf/MessagePack: Automático - campos desconhecidos ignorados
// System.Text.Json: Configure para permitir
var options = new JsonSerializerOptions
{
    UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip
};

Introduzir Leitura Antes da Escrita

Implante desserializadores antes de serializadores para novos formatos:

// Fase 1: Adicionar desserializador (implantado em todos os lugares)
public Order Deserialize(byte[] data, string manifest) => manifest switch
{
    "Order.V1" => DeserializeV1(data),
    "Order.V2" => DeserializeV2(data),  // NOVO - pode ler V2
    _ => throw new NotSupportedException()
};

// Fase 2: Habilitar serializador (próximo lançamento, após V1 implantado em todos os lugares)
public (byte[] data, string manifest) Serialize(Order order) =>
    _useV2Format
        ? (SerializeV2(order), "Order.V2")
        : (SerializeV1(order), "Order.V1");

Nunca Embutir Nomes de Tipo

// RUIM: Nome de tipo no payload - renomear classe quebra formato de transmissão
{
    "$type": "MyApp.Order, MyApp.Core",
    "id": "123"
}

// BOM: Discriminador explícito - refatoração segura
{
    "type": "order",
    "id": "123"
}

Comparação de Desempenho

Taxa de transferência aproximada (maior é melhor):

FormatoSerializarDesserializarTamanho
MessagePack★★★★★★★★★★★★★★★
Protobuf★★★★★★★★★★★★★★★
System.Text.Json (source gen)★★★★☆★★★★☆★★★☆☆
System.Text.Json (reflexão)★★★☆☆★★★☆☆★★★☆☆
Newtonsoft.Json★★☆☆☆★★☆☆☆★★★☆☆

Para caminhos críticos, prefira MessagePack ou Protobuf.


Serialização Akka.NET

Para sistemas de atores Akka.NET, use serialização baseada em esquema:

akka {
  actor {
    serializers {
      messagepack = "Akka.Serialization.MessagePackSerializer, Akka.Serialization.MessagePack"
    }
    serialization-bindings {
      "MyApp.Messages.IMessage, MyApp" = messagepack
    }
  }
}

Veja Documentação de Serialização Akka.NET.


Melhores Práticas

FAÇA

// Use source generators para System.Text.Json
[JsonSerializable(typeof(Order))]
public partial class AppJsonContext : JsonSerializerContext { }

// Use números/chaves de campo explícitos
[MessagePackObject]
public class Order
{
    [Key(0)] public string Id { get; init; }
}

// Use records para tipos de mensagem imutáveis
public sealed record OrderCreated(OrderId Id, CustomerId CustomerId);

NÃO FAÇA

// Não use BinaryFormatter (nunca)
var formatter = new BinaryFormatter();  // Risco de segurança!

// Não embuta nomes de tipo em formato de transmissão
settings.TypeNameHandling = TypeNameHandling.All;  // Quebra ao renomear!

// Não use serialização por reflexão para caminhos críticos
JsonConvert.SerializeObject(order);  // Lento, não compatível com AOT

Recursos

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

Habilidades Relacionadas