Skip to content
Development
Skill

/serialization

Choose the right serialization format for .NET applications. Prefer schema-based formats (Protobuf, MessagePack) over reflection-based (Newtonsoft.Json). Use System.Text.Json with AOT source generators for JSON scenarios.

From plugin
dotnet-skills
1.1k36 skills6 agents
Install
$ npx -y skills add aaronontheweb/dotnet-skills --skill serialization --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/serialization

Context preview

The summary Claude sees to decide when to auto-load this skill.

Choose the right serialization format for .NET applications. Prefer schema-based formats (Protobuf, MessagePack) over reflection-based (Newtonsoft.Json). Use System.Text.Json with AOT source generators for JSON scenarios.

SKILL.md

serialization.SKILL.md
name: serialization
description: Choose the right serialization format for .NET applications. Prefer schema-based formats (Protobuf, MessagePack) over reflection-based (Newtonsoft.Json). Use System.Text.Json with AOT source generators for JSON scenarios.
invocable: false

Serialization in .NET

When to Use This Skill

Use this skill when:

  • Choosing a serialization format for APIs, messaging, or persistence
  • Migrating from Newtonsoft.Json to System.Text.Json
  • Implementing AOT-compatible serialization
  • Designing wire formats for distributed systems
  • Optimizing serialization performance

---

Schema-Based vs Reflection-Based

| Aspect | Schema-Based | Reflection-Based | |--------|--------------|------------------| | **Examples** | Protobuf, MessagePack, System.Text.Json (source gen) | Newtonsoft.Json, BinaryFormatter | | **Type info in payload** | No (external schema) | Yes (type names embedded) | | **Versioning** | Explicit field numbers/names | Implicit (type structure) | | **Performance** | Fast (no reflection) | Slower (runtime reflection) | | **AOT compatible** | Yes | No | | **Wire compatibility** | Excellent | Poor |

**Recommendation**: Use schema-based serialization for anything that crosses process boundaries.

---

Format Recommendations

| Use Case | Recommended Format | Why | |----------|-------------------|-----| | **REST APIs** | System.Text.Json (source gen) | Standard, AOT-compatible | | **gRPC** | Protocol Buffers | Native format, excellent versioning | | **Actor messaging** | MessagePack or Protobuf | Compact, fast, version-safe | | **Event sourcing** | Protobuf or MessagePack | Must handle old events forever | | **Caching** | MessagePack | Compact, fast | | **Configuration** | JSON (System.Text.Json) | Human-readable | | **Logging** | JSON (System.Text.Json) | Structured, parseable |

Formats to Avoid

| Format | Problem | |--------|---------| | **BinaryFormatter** | Security vulnerabilities, deprecated, never use | | **Newtonsoft.Json default** | Type names in payload break on rename | | **DataContractSerializer** | Complex, poor versioning | | **XML** | Verbose, slow, complex |

---

System.Text.Json with Source Generators

For JSON serialization, use System.Text.Json with source generators for AOT compatibility and performance.

Setup

// Define a JsonSerializerContext with all your types
[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 { }

Usage

// Serialize with context
var json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);

// Deserialize with context
var order = JsonSerializer.Deserialize(json, AppJsonContext.Default.Order);

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

Benefits

  • **No reflection at runtime** - All type info generated at compile time
  • **AOT compatible** - Works with Native AOT publishing
  • **Faster** - No runtime type analysis
  • **Trim-safe** - Linker knows exactly what's needed

---

Protocol Buffers (Protobuf)

Best for: Actor systems, gRPC, event sourcing, any long-lived wire format.

Setup

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

Define Schema

// orders.proto
syntax = "proto3";

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

    // Adding new fields is always safe
    string notes = 5;  // Added in v2 - old readers ignore it
}

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

Versioning Rules

// SAFE: Add new fields with new numbers
message Order {
    string id = 1;
    string customer_id = 2;
    string shipping_address = 5;  // NEW - safe
}

// SAFE: Remove fields (old readers ignore unknown, new readers use default)
// Just stop using the field, keep the number reserved
message Order {
    string id = 1;
    // customer_id removed, but field 2 is reserved
    reserved 2;
}

// UNSAFE: Change field types
message Order {
    int32 id = 1;  // Was: string - BREAKS!
}

// UNSAFE: Reuse field numbers
message Order {
    reserved 2;
    string new_field = 2;  // Reusing 2 - BREAKS!
}

---

MessagePack

Best for: High-performance scenarios, compact payloads, actor messaging.

Setup

dotnet add package MessagePack
dotnet add package MessagePack.Annotations

Usage with Contracts

[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; }

    // New field - old readers skip unknown keys
    [Key(4)]
    public string? Notes { get; init; }
}

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

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

AOT-Compatible Setup

// Use source generator for AOT
[MessagePackObject]
public partial class Order { }  // partial enables source gen

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

---

Migrating from Newtonsoft.Json

Common Issues

| Newtonsoft | System.Text.Json | Fix | |------------|------------------|-----| | `$type` in JSON | Not supp

Read more
Ships withdotnet-skills

A comprehensive AI coding plugin with 30 skills and 5 specialized agents for professional .NET development. Battle-tested patterns from production systems covering C#, Akka.NET, Aspire, EF Core, testing, and performance optimization.

Get the whole plugin