Agent Skills by ALSEL
OpenAIソフトウェア開発⭐ リポ 1品質スコア 68/100

serialization

.NETアプリケーションに適したシリアライゼーション形式を選択してください。リフレクション依存の形式(Newtonsoft.Json)よりも、スキーマベースの形式(Protobuf、MessagePack)を優先してください。JSONのシナリオではAOTソースジェネレータを備えたSystem.Text.Jsonを使用してください。

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.

SKILL.md 本文

.NETでのシリアライゼーション

このスキルを使用するタイミング

以下の場合にこのスキルを使用してください:

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

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

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

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


形式の推奨事項

ユースケース推奨形式理由
REST APISystem.Text.Json(ソース生成)標準、AOT互換
gRPCProtocol Buffersネイティブ形式、優秀なバージョニング
アクターメッセージング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ソース生成を使用してください。

セットアップ

// 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 { }

使用方法

// 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);
});

メリット

  • ランタイムでリフレクションなし - すべての型情報はコンパイル時に生成される
  • AOT互換 - Native AOTパブリッシングで動作
  • 高速 - ランタイム型解析なし
  • トリム安全 - リンカーが必要なものを正確に認識

Protocol Buffers(Protobuf)

最適な用途: アクターシステム、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;

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

バージョニングルール

// 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

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

セットアップ

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

    // 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互換セットアップ

// 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));

Newtonsoft.Jsonからの移行

よくある問題

NewtonsoftSystem.Text.Json修正
$type in JSONデフォルトではサポートされていないディスクリミネーターまたはカスタムコンバーターを使用
JsonPropertyJsonPropertyName異なる属性
DefaultValueHandlingDefaultIgnoreCondition異なるAPI
NullValueHandlingDefaultIgnoreCondition異なるAPI
プライベートセッター[JsonInclude] が必要明示的なオプトイン
ポリモーフィズム[JsonDerivedType] (.NET 7以降)明示的なディスクリミネーター

移行パターン

// Newtonsoft (リフレクションベース)
public class Order
{
    [JsonProperty("order_id")]
    public string Id { get; set; }

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

// System.Text.Json (ソース生成互換)
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 { }

ディスクリミネーターを使用したポリモーフィズム

// .NET 7+ ポリモーフィズム
[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" }

ワイヤー互換性パターン

寛容なリーダー

古いコードは未知のフィールドを安全に無視する必要があります:

// Protobuf/MessagePack: 自動 - 未知のフィールドはスキップ
// System.Text.Json: スキップを許可するように構成
var options = new JsonSerializerOptions
{
    UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip
};

読み取りを書き込みより前に導入

新しい形式のデシリアライザーを最初にデプロイします:

// フェーズ1: デシリアライザーを追加(すべての場所にデプロイ)
public Order Deserialize(byte[] data, string manifest) => manifest switch
{
    "Order.V1" => DeserializeV1(data),
    "Order.V2" => DeserializeV2(data),  // NEW - V2を読み取り可能
    _ => throw new NotSupportedException()
};

// フェーズ2: シリアライザーを有効化(次のリリース、V1がすべての場所にデプロイ後)
public (byte[] data, string manifest) Serialize(Order order) =>
    _useV2Format
        ? (SerializeV2(order), "Order.V2")
        : (SerializeV1(order), "Order.V1");

型名をペイロードに埋め込まない

// 悪い例: ペイロード内の型名 - クラスのリネーム時にワイヤー形式が破損
{
    "$type": "MyApp.Order, MyApp.Core",
    "id": "123"
}

// 良い例: 明示的なディスクリミネーター - リファクタリング安全
{
    "type": "order",
    "id": "123"
}

パフォーマンス比較

概算スループット(値が高いほど良い):

形式シリアライズデシリアライズサイズ
MessagePack★★★★★★★★★★★★★★★
Protobuf★★★★★★★★★★★★★★★
System.Text.Json(ソース生成)★★★★☆★★★★☆★★★☆☆
System.Text.Json(リフレクション)★★★☆☆★★★☆☆★★★☆☆
Newtonsoft.Json★★☆☆☆★★☆☆☆★★★☆☆

ホットパスについては、MessagePackまたはProtobufを優先してください。


Akka.NETシリアライゼーション

Akka.NETアクターシステムの場合、スキーマベースのシリアライゼーションを使用してください:

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

Akka.NETシリアライゼーションドキュメントを参照してください。


ベストプラクティス

実施すること

// System.Text.Jsonにソース生成を使用
[JsonSerializable(typeof(Order))]
public partial class AppJsonContext : JsonSerializerContext { }

// 明示的なフィールド番号/キーを使用
[MessagePackObject]
public class Order
{
    [Key(0)] public string Id { get; init; }
}

// 不変メッセージ型にはレコードを使用
public sealed record OrderCreated(OrderId Id, CustomerId CustomerId);

実施しないこと

// BinaryFormatterを使用しないでください(絶対に)
var formatter = new BinaryFormatter();  // セキュリティリスク!

// ワイヤー形式に型名を埋め込まないでください
settings.TypeNameHandling = TypeNameHandling.All;  // リネーム時に破損!

// ホットパスにリフレクションシリアライゼーションを使用しないでください
JsonConvert.SerializeObject(order);  // 低速、AOT互換ではない

リソース

ライセンス: Apache-2.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
woutervanranst
リポジトリ
woutervanranst/Arius7
ライセンス
Apache-2.0
最終更新
2026/5/12

Source: https://github.com/woutervanranst/Arius7 / ライセンス: Apache-2.0

本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: woutervanranst · woutervanranst/Arius7 · ライセンス: Apache-2.0