jpskill.com
🛠️ 開発・MCP コミュニティ

serialization

.NETアプリケーションのデータ形式を選ぶ際、ProtobufやMessagePackのような形式を優先し、JSONを使う場合はSystem.Text.JsonとAOTソースジェネレーターを使い、効率的で安全なデータ処理をする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.

🇯🇵 日本人クリエイター向け解説

一言でいうと

.NETアプリケーションのデータ形式を選ぶ際、ProtobufやMessagePackのような形式を優先し、JSONを使う場合はSystem.Text.JsonとAOTソースジェネレーターを使い、効率的で安全なデータ処理をするSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o serialization.zip https://jpskill.com/download/8721.zip && unzip -o serialization.zip && rm serialization.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8721.zip -OutFile "$d\serialization.zip"; Expand-Archive "$d\serialization.zip" -DestinationPath $d -Force; ri "$d\serialization.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して serialization.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → serialization フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

.NET におけるシリアライゼーション

このスキルを使うべき時

このスキルは、以下のような場合に利用します。

  • API、メッセージング、または永続化のためのシリアライゼーション形式を選択する
  • Newtonsoft.Json から System.Text.Json への移行
  • AOT互換のシリアライゼーションの実装
  • 分散システムのためのワイヤーフォーマットの設計
  • シリアライゼーションのパフォーマンスの最適化

スキーマベース vs リフレクションベース

側面 スキーマベース リフレクションベース
Protobuf, MessagePack, System.Text.Json (ソースジェネレーター) Newtonsoft.Json, BinaryFormatter
ペイロード内の型情報 いいえ (外部スキーマ) はい (型名が埋め込まれている)
バージョニング 明示的なフィールド番号/名前 暗黙的 (型構造)
パフォーマンス 高速 (リフレクションなし) 低速 (実行時リフレクション)
AOT互換性 はい いいえ
ワイヤー互換性 非常に良い 悪い

推奨事項: プロセス境界を越えるものには、スキーマベースのシリアライゼーションを使用してください。


フォーマットの推奨事項

ユースケース 推奨フォーマット 理由
REST API System.Text.Json (ソースジェネレーター) 標準、AOT互換
gRPC Protocol Buffers ネイティブフォーマット、優れたバージョニング
Actor メッセージング MessagePack または Protobuf コンパクト、高速、バージョンセーフ
イベントソーシング Protobuf または MessagePack 古いイベントを永久に処理する必要がある
キャッシュ MessagePack コンパクト、高速
構成 JSON (System.Text.Json) 人間が読める
ログ JSON (System.Text.Json) 構造化、解析可能

避けるべきフォーマット

フォーマット 問題点
BinaryFormatter セキュリティ脆弱性、非推奨、絶対に使用しない
Newtonsoft.Json デフォルト ペイロード内の型名がリネーム時に壊れる
DataContractSerializer 複雑、バージョニングが貧弱
XML 冗長、低速、複雑

System.Text.Json とソースジェネレーター

JSON シリアライゼーションには、AOT 互換性とパフォーマンスのために、ソースジェネレーター付きの System.Text.Json を使用します。

セットアップ

// すべての型を持つ JsonSerializerContext を定義する
[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 { }

使い方

// コンテキストを使用してシリアライズする
var json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);

// コンテキストを使用してデシリアライズする
var order = JsonSerializer.Deserialize(json, AppJsonContext.Default.Order);

// ASP.NET Core で構成する
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});

利点

  • 実行時のリフレクションなし - すべての型情報はコンパイル時に生成される
  • AOT 互換 - Native AOT パブリッシングで動作する
  • より高速 - 実行時の型分析がない
  • トリムセーフ - リンカーは必要なものを正確に把握している

Protocol Buffers (Protobuf)

最適な用途: Actor システム、gRPC、イベントソーシング、あらゆる長期的なワイヤーフォーマット。

セットアップ

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

スキーマの定義

// orders.proto
syntax = "proto3";

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

    // 新しいフィールドの追加は常に安全
    string notes = 5;  // v2 で追加 - 古いリーダーは無視する
}

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

バージョニングルール

// 安全: 新しい番号で新しいフィールドを追加する
message Order {
    string id = 1;
    string customer_id = 2;
    string shipping_address = 5;  // 新規 - 安全
}

// 安全: フィールドを削除する (古いリーダーは不明なものを無視し、新しいリーダーはデフォルトを使用する)
// フィールドの使用を停止し、番号を予約したままにする
message Order {
    string id = 1;
    // customer_id を削除したが、フィールド 2 は予約されている
    reserved 2;
}

// 危険: フィールドの型を変更する
message Order {
    int32 id = 1;  // 以前: string - 壊れる!
}

// 危険: フィールド番号を再利用する
message Order {
    reserved 2;
    string new_field = 2;  // 2 の再利用 - 壊れる!
}

MessagePack

最適な用途: 高パフォーマンスのシナリオ、コンパクトなペイロード、Actor メッセージング。

セットアップ

dotnet add package MessagePack
dotnet add package MessagePack.Annotations

コントラクトを使った使い方

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

    // 新しいフィールド - 古いリーダーは不明なキーをスキップする
    [Key(4)]
    public string? Notes { get; init; }
}

// シリアライズ
var bytes = MessagePackSerializer.Serialize(order);

// デシリアライズ
var order = MessagePackSerializer.Deserialize<Order>(bytes);

AOT 互換のセットアップ

// AOT 用にソースジェネレーターを使用する
[MessagePackObject]
public partial class Order { }  // partial はソースジェネレーターを有効にする

// リゾルバーを構成する
var options = MessagePackSerializerOptions.Standard
    .WithResolver(CompositeResolver.Create(
        GeneratedResolver.Instance,  // 生成済み
        StandardResolver.Instance));

Newtonsoft.Json からの移行

よくある問題

Newtonsoft System.Text.Json 修正方法
JSON 内の $type デフォルトではサポートされていない 識別子またはカスタムコンバーターを使用する
JsonProperty JsonPropertyName 異なる属性
DefaultValueHandling DefaultIgnoreCondition 異なる API
NullValueHandling DefaultIgnoreCondition 異なる API
プライベートセッター R
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

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 supported by default Use discriminators or custom converters
JsonProperty JsonPropertyName Different attribute
DefaultValueHandling DefaultIgnoreCondition Different API
NullValueHandling DefaultIgnoreCondition Different API
Private setters Requires [JsonInclude] Explicit opt-in
Polymorphism [JsonDerivedType] (.NET 7+) Explicit discriminators

Migration Pattern

// Newtonsoft (reflection-based)
public class Order
{
    [JsonProperty("order_id")]
    public string Id { get; set; }

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

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

    string? Notes  // Null handling via JsonSerializerOptions
);

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

Polymorphism with Discriminators

// .NET 7+ polymorphism
[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);

// Serializes as:
// { "$type": "credit_card", "amount": 100, "last4": "1234" }

Wire Compatibility Patterns

Tolerant Reader

Old code must safely ignore unknown fields:

// Protobuf/MessagePack: Automatic - unknown fields skipped
// System.Text.Json: Configure to allow
var options = new JsonSerializerOptions
{
    UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip
};

Introduce Read Before Write

Deploy deserializers before serializers for new formats:

// Phase 1: Add deserializer (deployed everywhere)
public Order Deserialize(byte[] data, string manifest) => manifest switch
{
    "Order.V1" => DeserializeV1(data),
    "Order.V2" => DeserializeV2(data),  // NEW - can read V2
    _ => throw new NotSupportedException()
};

// Phase 2: Enable serializer (next release, after V1 deployed everywhere)
public (byte[] data, string manifest) Serialize(Order order) =>
    _useV2Format
        ? (SerializeV2(order), "Order.V2")
        : (SerializeV1(order), "Order.V1");

Never Embed Type Names

// BAD: Type name in payload - renaming class breaks wire format
{
    "$type": "MyApp.Order, MyApp.Core",
    "id": "123"
}

// GOOD: Explicit discriminator - refactoring safe
{
    "type": "order",
    "id": "123"
}

Performance Comparison

Approximate throughput (higher is better):

Format Serialize Deserialize Size
MessagePack ★★★★★ ★★★★★ ★★★★★
Protobuf ★★★★★ ★★★★★ ★★★★★
System.Text.Json (source gen) ★★★★☆ ★★★★☆ ★★★☆☆
System.Text.Json (reflection) ★★★☆☆ ★★★☆☆ ★★★☆☆
Newtonsoft.Json ★★☆☆☆ ★★☆☆☆ ★★★☆☆

For hot paths, prefer MessagePack or Protobuf.


Akka.NET Serialization

For Akka.NET actor systems, use schema-based serialization:

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

See Akka.NET Serialization Docs.


Best Practices

DO

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

// Use explicit field numbers/keys
[MessagePackObject]
public class Order
{
    [Key(0)] public string Id { get; init; }
}

// Use records for immutable message types
public sealed record OrderCreated(OrderId Id, CustomerId CustomerId);

DON'T

// Don't use BinaryFormatter (ever)
var formatter = new BinaryFormatter();  // Security risk!

// Don't embed type names in wire format
settings.TypeNameHandling = TypeNameHandling.All;  // Breaks on rename!

// Don't use reflection serialization for hot paths
JsonConvert.SerializeObject(order);  // Slow, not AOT-compatible

Resources