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本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
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
$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. 下の青いボタンを押して
serialization.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
serializationフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
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
- System.Text.Json Source Generation: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation
- Protocol Buffers: https://protobuf.dev/
- MessagePack-CSharp: https://github.com/MessagePack-CSharp/MessagePack-CSharp
- Akka.NET Serialization: https://getakka.net/articles/networking/serialization.html
- Wire Compatibility: https://getakka.net/community/contributing/wire-compatibility.html