Protobuf → C# コンバーター
.proto スキーマを貼り付けるだけ。auto-property 付きの C# クラスが手に入ります。プロジェクトにそのまま投入でき、protoc プラグインも不要です。
入力 (.proto スキーマ)
出力 (C#)
このツールでできること
Protocol Buffers のスキーマがあって、対応する DTO が必要な C# のサービスやクライアントを書いている、という場面。公式ルートは protoc と C# プラグインを入れる(または .csproj に Grpc.Tools を組み込む)か、MSBuild にビルド時に partial クラスを生成させるやり方です。これはこれで動きますが、スキーマをただ読みたい、型をざっと見たい、Razor ページや単発の連携にメッセージを貼りたい、というだけなら大げさです。このコンバーターは同じ種類の作業を — 貼り付け、コピー、Types.cs に投入 — の流れでやってくれます。
型マッピングは退屈でもきっちりやるタイプ。string はそのまま string(nullable reference 有効プロジェクトに怒られないよう = "" 初期化子付き)。bool、int32、int64、uint32、uint64、float、double はそれぞれ bool、int、long、uint、ulong、float、double に対応。bytes は byte[]。repeated T は List<T>、map<K, V> は Dictionary<K, V>、そして google.protobuf.Timestamp のような well-known ラッパーは string として出力されます(proto3 JSON のタイムスタンプ表現は RFC 3339、つまり wire レベルではただの文字列 — 詳細は proto3 JSON 仕様 を参照)。
フィールド名は標準的な PascalCase 化 — order_id → OrderId、shipping_address → ShippingAddress — C# protobuf 公式リファレンス が生成するものと同じです。enum 値は、スキーマが各値に enum 名を SCREAMING_SNAKE で付ける慣習に従っているとき、その接頭辞が剥がれます(つまり enum OrderStatus の ORDER_STATUS_PENDING は OrderStatus.Pending になる)。出力クラスはフラットで、ネストされたメッセージはすべてトップレベルに引き上げられます。スコープを解きほぐさずに分割や並べ替えができます。変換はすべてブラウザ内で完結し、スキーマはどこにもアップロードされません。
使い方
3 ステップ。出力はそのままコンパイル可能 — プロジェクトの <code>Types.cs</code> ファイルに貼ってください。
.proto スキーマを貼り付ける
左側のエディターにスキーマを投入します。先頭の syntax = "proto3"; はあってもなくても構いません。パーサーはネストされた message ブロック、enum 宣言、oneof、map<K, V>、フィールドオプション、おなじみの package / import / option ディレクティブを処理します。import は認識はしますがスキップするので、依存する型がある場合はその型もインラインで貼り付けてください。
フィールド名の変換は自動です。.proto の customer_name は C# では CustomerName になります。クラス名と enum 名はそのまま(慣習的にすでに PascalCase)。
出力を読む
右側にはメッセージごとに { get; set; } auto-property を持つ public class 宣言、enum ごとに public enum 宣言が並びます。スキーマが repeated や map フィールドを使うときは using System.Collections.Generic; 行が追加されます(List と Dictionary を解決するため)。ファイルをプロジェクトに投入し、自分の名前空間で囲めば終わりです。
実際に組み込む
純粋な DTO 用途なら、System.Text.Json や Newtonsoft.Json でそのままシリアライズできます。本格的な gRPC C# の作業 — サービス実装、ストリーミング、デッドライン — では引き続き Grpc.Tools を使って wire-format 型を生成し、このコンバーターは生成コードと併用する手書きラッパー、マッピング層、テストフィクスチャに使ってください。
実際に時間が浮く場面
新しい gRPC サービス用に DTO を下書きする
ASP.NET Core gRPC サービスを立ち上げようとしていて、Grpc.Tools を導入する前に C# でメッセージがどう見えるか確認したい。.proto を貼り、クラスをざっと見て、フィールド命名がチームのスタイルと合うか判断し、それからコード生成を本格的に組み込みましょう。
手書きのマッピング層
生成された gRPC 型は専用の名前空間にいて、ドメイン層には素直な DTO がほしい。ここの出力なら、生成コード特有のメタデータの煩雑さがないクリーンなクラスが手に入り、AutoMapper や手書きコンバーターでの双方向マッピングが楽になります。
Protobuf スキーマ変更のレビュー
チームメイトが PR でメッセージにフィールドを追加した。ブランチを checkout してビルドせず、C# 側のシェイプへの影響を見たい。新しいスキーマを貼り、現状の C# 型と差分を取り、要点を押さえたレビューコメントを残しましょう。
テストフィクスチャやちょっとしたスクリプト
使い捨ての LinqPad スクリプトや、gRPC-gateway に POST するコンソールアプリを書いている。50 行のテストコードのために Protobuf のフルツールチェーンを組むのは大げさです。ここでクラスを取得して JSON にシリアライズし、リクエストを投げて、次に進みましょう。
よくある質問
スキーマはどこかに送信されますか?
いいえ。パーサーと C# エミッターはすべてブラウザ内で JavaScript として動作します。貼り付けながら DevTools の Network タブを見てみてください — リクエストはゼロです。スキーマに社内の型名やパッケージパス、サードパーティに送りたくない情報が含まれているときに有用です。
これらのクラスは Grpc.Tools 生成コードと一緒に使えますか?
同等のシェイプを作りますが、バイト単位で同一ではありません。Grpc.Tools は partial クラス、parser 登録、descriptor 結線、Google.Protobuf.IMessage 由来の祖先型を生成しますが、ここではそれらは扱いません。本物の gRPC ワイヤープロトコルの作業には Grpc.Tools を使ってください。DTO のみのコード(JSON-over-HTTP ゲートウェイ、マッピング層、テストデータなど)には、この出力で十分です。
なぜ int64 と uint64 は string ではなく long と ulong として型付けされるのですか?
C# ではちゃんと収まるからです。JavaScript の Number と違って(2^53 を超えると精度を失います)、C# の long は int64 の全レンジをネイティブに扱えるため、string にフォールバックする理由がありません。proto3 JSON で 64 ビット整数が文字列として届く場合(JSON マッピング仕様 による)、System.Text.Json と JsonNumberHandling.AllowReadingFromString の組み合わせが変換してくれます。
SCREAMING_SNAKE の enum 値命名規則はどう扱いますか?
Protobuf スタイルガイドは、enum 値ごとに enum 名を SCREAMING_SNAKE で接頭辞として付けることを推奨しています。コンバーターはこれを検出して剥がします。enum OrderStatus の ORDER_STATUS_UNSPECIFIED は OrderStatus.Unspecified になります。enum がその慣習に従っていない場合(一部だけに接頭辞が付くなど)は接頭辞剥がしをスキップし、値はそのまま PascalCase 化されます。
oneof は扱えますか?
oneof のフィールドはそれぞれ通常の auto-property として出力されます。出力は oneof が暗黙に課す「いずれか一つだけ」制約までは強制しません — C# 言語にはまだネイティブの discriminated union がないためです。より厳密にモデル化したい場合は NuGet の OneOf のようなライブラリを見るか、出力を手で編集して基底クラス + サブクラスにしてください。
google.protobuf.Timestamp や well-known 型はどう扱いますか?
Timestamp は string として出力されます(proto3 JSON はタイムスタンプを RFC 3339 文字列としてエンコードします)。Empty と Any は object プレースホルダーになります — プロジェクトが WKT パッケージを取り込んでいるなら Google.Protobuf.WellKnownTypes.Any やより強い型に手で調整してください。出力は意図的にフレームワークに依存しないので、NuGet 依存を強制せずどんな C# プロジェクトにも投入できます。
関連ツール
Protobuf、JSON、C# のあいだを行き来しているなら、相性のいいツールはこちら: