system-text-json-net11 について
.NET 11 の System.Text.Json API を使い、命名ポリシーと型メタデータを扱うコーディングエージェント向けスキルです。net11.0 でのコード検証も案内します。
主な機能
- 01JSON プロパティ名の PascalCase 設定
- 02辞書キーへの PascalCase 適用
- 03型付き JsonTypeInfo メタデータの取得
- 04TryGetTypeInfo によるメタデータ確認
- 05net11.0 サンプルの実行と検証
活用シーン
- 01組み込み API で独自命名ポリシーを置き換え
- 02辞書キーを PascalCase でシリアライズ
- 03キャストなしで型メタデータを利用
- 04例外を使わずメタデータの有無を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill system-text-json-net11インストール後、Claude Code で/system-text-json-net11を入力して読み込まれたか確認します。
詳細
System.Text.Json — .NET 11
.NET 11 では、System.Text.Json に 3 つの API が追加されました。このスキルでは、それぞれをいつ使うか、何を書くか、何を書かないか、そして結果が実行できることをどう確認するかを正確に説明します。これらの API についてユーザーに説明するのではなく、適用してからコードを実行し、出力を提示してください。
| API | .NET 11 より前に必要だった回避策 |
|---|---|
JsonNamingPolicy.PascalCase(静的プロパティ) | カスタムの JsonNamingPolicy サブクラスを作成する、またはすべてのメンバーに [JsonPropertyName] を手動で付与する |
JsonSerializerOptions.GetTypeInfo<T>() | ジェネリックではない GetTypeInfo(typeof(T)) を呼び出して JsonTypeInfo<T> にキャストする |
JsonSerializerOptions.TryGetTypeInfo<T>(out JsonTypeInfo<T>? info) | 利用可否を確認するために GetTypeInfo を try/catch で囲む |
ステップ 0 — net11.0 の検証を実行できるようにする
これらの API は .NET 11 の基本クラスライブラリにのみ存在します。コードを書く前に、次の手順を行ってください。
dotnet --list-sdksを実行し、net11.0をターゲットにできる SDK(11.xSDK、またはnet11.0ターゲティングパックを含む後続の SDK)が存在することを確認します。- ユーザーがサンプルの実行を明示的に求めていて、適切な SDK がインストールされていない場合は、公式の
dotnet-installスクリプトを使って、現在の .NET 11 SDK を一時ディレクトリまたはプロジェクト内のディレクトリにインストールします。GA チャネルのビルドを優先してください。GA がまだ利用できない場合、またはユーザーがプレビューを明示的に求めた場合にのみ、プレビューを使います。管理者権限を要求したり、マシン全体のPATHを変更したり、インストール済みの SDK を置き換えたりしないでください。 - そのローカルの
dotnet実行ファイルを使ってサンプルを実行します。ダウンロードまたは実行がブロックされた場合も、完全なnet11.0プログラムを提示し、実行していないことを報告してください。net10.0、カスタムの命名ポリシー、または別の API に置き換えて、それを .NET 11 の機能の検証結果として提示してはいけません。
推測したバージョンではなく、チャネルを指定してください。まず GA チャネルを試します。
$installScript = Join-Path $env:TEMP "dotnet-install-$([guid]::NewGuid()).ps1"
try {
Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript
& $installScript -Channel 11.0 -InstallDir .\.dotnet
& .\.dotnet\dotnet.exe run --project <PATH_TO_NET11_PROJECT>
}
finally {
Remove-Item -LiteralPath $installScript -Force -ErrorAction SilentlyContinue
}
install_script="$(mktemp "${TMPDIR:-/tmp}/dotnet-install.XXXXXX")"
trap 'rm -f "$install_script"' EXIT
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$install_script"
bash "$install_script" --channel 11.0 --install-dir ./.dotnet
./.dotnet/dotnet run --project <PATH_TO_NET11_PROJECT>
.NET 11 GA のリリース前は、インストールを再試行する際に、PowerShell では -Quality preview、シェルでは --quality preview を指定してください。
判断表 — 症状 → 対応 → 絶対にしないこと
ユーザーの依頼に該当する行を選び、「対応」列の内容をそのまま適用し、作業を終える前に「確認」列を確認してください。
| ユーザーの依頼 | 対応(net11.0 を使用) | 絶対にしないこと | 確認 |
|---|---|---|---|
| PascalCase の JSON プロパティ名 | options.PropertyNamingPolicy = JsonNamingPolicy.PascalCase; | class …: JsonNamingPolicy を定義する、メンバーごとに [JsonPropertyName] を追加する、名前の文字列の大文字・小文字を自分で変換する | JSON の出力キーが PascalCase になっている。例:"Name"、"Age" |
| PascalCase のディクショナリキー | options.DictionaryKeyPolicy = JsonNamingPolicy.PascalCase; | PropertyNamingPolicy だけを設定する、ディクショナリを事前に変換する、カスタムポリシーを定義する | pendingOrders などのディクショナリキーが "PendingOrders" になる |
厳密に型指定されたメタデータ JsonTypeInfo<T> | TypeInfoResolver = new DefaultJsonTypeInfoResolver() を設定し、その後 JsonTypeInfo<T> ti = options.GetTypeInfo<T>(); を実行する | (JsonTypeInfo<T>)options.GetTypeInfo(typeof(T)) | 変数の型が JsonTypeInfo<T> であり、キャストしていない |
| メタデータが解決されるかどうかを確認する | if (options.TryGetTypeInfo<T>(out var ti)) { … } else { … } | try { options.GetTypeInfo<T>(); } catch (…) { … } | try/catch を使わず、両方の分岐を処理している |
ルール 1 — PascalCase のプロパティ名
ユーザーが JSON 出力のプロパティ名を PascalCase(Name、Age)にしたいと希望し、組み込みまたはフレームワーク提供の方法を求めている場合:
JsonSerializerOptionsを作成または再利用し、PropertyNamingPolicy = JsonNamingPolicy.PascalCaseを設定します。- そのオプションを使ってシリアル化します。
大文字・小文字を強制するために JsonNamingPolicy のサブクラスを作成したり、[JsonPropertyName("…")] 属性を追加したり、各名前の先頭文字を手作業で大文字にしたりしないでください。.NET 11 では、JsonNamingPolicy.PascalCase が唯一の正しい方法です。
// Console project (reflection enabled by default). To run this as a file-based app
// (dotnet run app.cs), also set TypeInfoResolver = new DefaultJsonTypeInfoResolver()
// — see "Producing runnable output" below.
using System.Text.Json;
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.PascalCase
};
string json = JsonSerializer.Serialize(new { name = "Jane", age = 30 }, options);
Console.WriteLine(json);
// {"Name":"Jane","Age":30}
辞書キーは別の設定です
PropertyNamingPolicy は Dictionary<string, TValue> のキーを変換しません。
その場合は DictionaryKeyPolicy を設定します。
var options = new JsonSerializerOptions
{
DictionaryKeyPolicy = JsonNamingPolicy.PascalCase
};
var values = new Dictionary<string, int>
{
["pendingOrders"] = 2,
["activeUsers"] = 5
};
Console.WriteLine(JsonSerializer.Serialize(values, options));
// {"PendingOrders":2,"ActiveUsers":5}
ルール 2 — 強く型付けされた JsonTypeInfo<T>
ユーザーが 型メタデータを、キャストが必要な非ジェネリックの
JsonTypeInfo ではなく、JsonTypeInfo<T> として取得したい場合:
options.GetTypeInfo<T>()を呼び出します。これにより、JsonTypeInfo<T>が直接返されます。- これを
JsonTypeInfo<T>型の変数に代入して使用します(例:JsonSerializer.Serialize/Deserializeに渡します)。
非ジェネリックの GetTypeInfo(Type) オーバーロードを呼び出して、結果をキャストしてはいけません。
リゾルバーが必要です。
optionsにTypeInfoResolverが設定されていない場合、GetTypeInfo<T>()はNotSupportedException(NoMetadataForType)をスローします。リフレクションベースのアプリではTypeInfoResolver = new DefaultJsonTypeInfoResolver()を設定するか、 トリミング/AOT 対応アプリではソース生成されたJsonSerializerContextを使用してください。
// File-based app (run: dotnet run app.cs). In a .csproj project, remove this line and
// set <TargetFramework>net11.0</TargetFramework> in the project file instead.
#:property TargetFramework=net11.0
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
JsonTypeInfo<Person> typeInfo = options.GetTypeInfo<Person>();
Console.WriteLine(typeInfo.Type.Name); // Person
record Person(string Name, int Age);
ルール 3 — 例外をスローせずにメタデータを確認する
ユーザーが T のメタデータが利用可能かどうかを確認して処理を分岐したい場合、
メタデータが利用できないときに例外をスローさせずに行うには:
options.TryGetTypeInfo<T>(out var info)を呼び出します。trueの分岐(メタデータが解決されており、infoを使用)とfalseの分岐 (解決されていない)を明示的に処理します。
不足するケースを検出するために GetTypeInfo<T>() を try/catch で囲んではいけません。
これは、まさにこの API がなくすアンチパターンです。TryGetTypeInfo<T> は、
T のメタデータを生成できるリゾルバーがない場合に(例外をスローする代わりに)
false を返します。これは、処理を分岐したいケースそのものです。
// File-based app (run: dotnet run app.cs). In a .csproj project, remove this line and
// set <TargetFramework>net11.0</TargetFramework> in the project file instead.
#:property TargetFramework=net11.0
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
// Configured with a resolver → metadata is available.
var configured = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
if (configured.TryGetTypeInfo<Person>(out JsonTypeInfo<Person>? info) && info is not null)
{
Console.WriteLine($"Resolved: {info.Type.Name}"); // Resolved: Person
}
else
{
Console.WriteLine("Type info not available");
}
// No resolver → TryGetTypeInfo returns false instead of throwing.
var empty = new JsonSerializerOptions();
Console.WriteLine(empty.TryGetTypeInfo<Person>(out _)); // False
record Person(string Name, int Age);
net11.0 で実行可能な出力を生成する
プログラムが net11.0 で実行され、JSON を出力するまでは、作業は完了していません。
コンソール プロジェクトを推奨します。リフレクションベースのシリアル化が
そのまま動作するためです。ファイルベースのアプリも動作しますが、重要な注意点があります(以下を参照)。
インストール済みの SDK で net11.0 をターゲットにできず、実行が求められている場合は、
公式スクリプトを使って SDK をローカルにインストールし、フルパスで実行してください。
インストールスクリプトは管理者権限を必要とせず、PATH を永続的に変更することもありません。
オプション A — コンソールプロジェクト(推奨)
.csproj に <TargetFramework>net11.0</TargetFramework> を含むプロジェクトを作成し、
コードを Program.cs に記述してから、dotnet run を実行します。プロセスがコード 0 で終了し、
期待される JSON を出力することを確認してください。
using System.Text.Json;
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.PascalCase };
Console.WriteLine(JsonSerializer.Serialize(new { name = "Jane", age = 30 }, options));
// {"Name":"Jane","Age":30}
オプション B — ファイルベースのアプリ(最速ですが、注意点が 1 つあります)
app.cs として保存し、dotnet run app.cs を実行します。最初のディレクティブでフレームワークを指定します。
注意点 — ファイルベースのアプリでは System.Text.Json のリフレクションが無効になります。
dotnet run app.csで実行するファイルベースのアプリでは、JsonSerializer.IsReflectionEnabledByDefaultがfalseであるため、 通常のリフレクションによるシリアル化はNotSupportedException(NoMetadataForType)をスローします。オプションに明示的なTypeInfoResolver = new DefaultJsonTypeInfoResolver()を設定するか(以下の例を参照)、 ソース生成されたJsonSerializerContextを使用してください。通常のプロジェクトではこの設定は不要です。
// File-based app (run: dotnet run app.cs). In a .csproj project, remove this line and
// set <TargetFramework>net11.0</TargetFramework> in the project file instead.
#:property TargetFramework=net11.0
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.PascalCase,
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
Console.WriteLine(JsonSerializer.Serialize(new { name = "Jane", age = 30 }, options));
// {"Name":"Jane","Age":30}
実例 — 型付きメタデータと PascalCase を使ったシリアル化
以下のレコードでは、出力で PascalCase ポリシーによる変換が見て分かるように、 意図的にメンバー名を小文字にしています。
// File-based app (run: dotnet run app.cs). In a .csproj project, remove this line and
// set <TargetFramework>net11.0</TargetFramework> in the project file instead.
#:property TargetFramework=net11.0
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.PascalCase,
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
JsonTypeInfo<Person> typeInfo = options.GetTypeInfo<Person>();
string json = JsonSerializer.Serialize(new Person("Jane", 30), typeInfo);
Console.WriteLine(json);
// {"Name":"Jane","Age":30}
record Person(string name, int age);
検証チェックリスト
成功を報告する前に、該当する項目をすべて確認してください。
- プロジェクトまたはファイルベースのアプリが
net11.0をターゲットにしています(.csprojまたは#:property TargetFramework=net11.0ディレクティブで確認できます)。 - PascalCase の要求では
JsonNamingPolicy.PascalCaseを使用しています。カスタムのJsonNamingPolicyサブクラスや、大文字と小文字の変更だけを目的としたメンバーごとの[JsonPropertyName]属性は使用していません。 - ディクショナリキーの要求では、
PropertyNamingPolicyだけでなくDictionaryKeyPolicyを設定しています。 - 型付きメタデータの要求では、ジェネリックの
GetTypeInfo<T>()を使用しています。非ジェネリックのJsonTypeInfoをキャストせず、NoMetadataForTypeが発生しないよう、オプションにTypeInfoResolver(例:DefaultJsonTypeInfoResolver)を設定しています。 - メタデータの有無を確認する要求では、
GetTypeInfoをtry/catchで囲まず、TryGetTypeInfo<T>(out …)を使用しています。 - プログラムを実際に実行しており(
dotnet run …)、終了コードが 0 で、出力された JSON に 期待されるプロパティ名(例:"Name"、"Age")が表示されています。 - ファイルベースのアプリ(
dotnet run app.cs)を使用する場合、すべてのJsonSerializerOptionsにTypeInfoResolverを設定しています。ファイルベースのアプリではリフレクションが無効になっているため、 設定しないと通常のシリアル化でNoMetadataForTypeが発生します。
よくある落とし穴
| 落とし穴 | 対処方法 |
|---|---|
PascalCase のために class … : JsonNamingPolicy を独自に実装する | 削除して、PropertyNamingPolicy = JsonNamingPolicy.PascalCase を設定します。 |
大文字と小文字を強制するため、すべてのメンバーに [JsonPropertyName("Name")] を追加する | 属性を削除します。命名ポリシーがすべてのメンバーを一括で処理します。 |
(JsonTypeInfo<T>)options.GetTypeInfo(typeof(T)) にキャストする | ジェネリックの options.GetTypeInfo<T>() を呼び出します。キャストは不要です。 |
利用可能かどうかを確認するため、try { options.GetTypeInfo<T>(); } catch (…) { … } を使う | if (options.TryGetTypeInfo<T>(out var info)) { … } に置き換えます。 |
GetTypeInfo<T>() で NotSupportedException/NoMetadataForType が発生する | オプションにリゾルバーが設定されていません。TypeInfoResolver = new DefaultJsonTypeInfoResolver()(リフレクション)またはソース生成された JsonSerializerContext(トリミング/AOT)を設定します。 |
dotnet run app.cs のファイルベースのアプリで、通常の Serialize でも NoMetadataForType が発生する | ファイルベースのアプリでは STJ のリフレクションが無効になっています。TypeInfoResolver = new DefaultJsonTypeInfoResolver() を追加するか、通常のプロジェクトとして実行します。 |
| アプリを SDK の既定の TFM のままにする | .NET 11 API を解決でき、出力にもターゲットが表示されるよう、net11.0 を明示的に指定します。 |
| 実行せずに成功したと主張する | dotnet run を実行し、実際の JSON 出力を貼り付けます。目標は、実行して動作を確認したプログラムです。 |
詳細情報
- JsonNamingPolicy クラス —
PascalCaseを含む組み込みの命名ポリシー - JsonSerializerOptions.GetTypeInfo — 型付きおよび型なしのメタデータへのアクセス
- JsonTypeInfo<T> — 強い型付けを備えたシリアル化メタデータ
- DefaultJsonTypeInfoResolver —
GetTypeInfo/TryGetTypeInfoに必要なリフレクションベースのリゾルバー - ファイルベースのアプリ —
dotnet run app.csと#:propertyディレクティブ