mcpField
S
system-text-json-net11

system-text-json-net11

github.com/dotnet开发5.8k3.3k 次安装1 天前更新
代码仓库

关于 system-text-json-net11

面向编码 Agent 的技能,指导使用 .NET 11 的 System.Text.Json API 设置命名策略和处理类型元数据,并要求在 net11.0 上验证代码。

主要功能

  1. 01设置 JSON 属性的 PascalCase
  2. 02将字典键转换为 PascalCase
  3. 03获取强类型 JsonTypeInfo 元数据
  4. 04使用 TryGetTypeInfo 探测元数据
  5. 05运行并验证 net11.0 示例

适用场景

  1. 01用内置 API 替代自定义命名策略
  2. 02将字典键序列化为 PascalCase
  3. 03无需强制转换即可使用类型元数据
  4. 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 新增了三个 API。本指南会准确说明何时使用每个 API、要编写什么、不要编写什么,以及如何验证结果确实可以运行。不要向用户介绍这些 API——直接应用它们,然后运行代码并展示输出。

API替代 .NET 11 之前的变通方法……
JsonNamingPolicy.PascalCase(静态属性)编写自定义 JsonNamingPolicy 子类,或为每个成员手动添加 [JsonPropertyName] 注解
JsonSerializerOptions.GetTypeInfo<T>()调用非泛型的 GetTypeInfo(typeof(T)) 并强制转换为 JsonTypeInfo<T>
JsonSerializerOptions.TryGetTypeInfo<T>(out JsonTypeInfo<T>? info)用 try/catch 包装 GetTypeInfo 以探测是否可用

步骤 0 — 确保可以执行所需的 net11.0 验证

这些 API 仅存在于 .NET 11 基类库中。编写代码之前:

  1. 运行 dotnet --list-sdks,确认已安装可面向 net11.0 的 SDK——11.x SDK,或带有 net11.0 目标包的更高版本 SDK。
  2. 如果用户明确要求运行示例,但没有安装合适的 SDK,请使用官方 dotnet-install 脚本,将当前 .NET 11 SDK 安装到临时目录或项目本地目录。优先使用 GA 通道版本。仅当 GA 尚不可用或用户明确要求预览版时,才使用预览版。无需管理员权限,不要更改整台机器的 PATH,也不要替换已安装的 SDK。
  3. 使用本地的 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 发布之前,请使用 -Quality preview(PowerShell)或 --quality preview(shell)重新尝试安装。

决策表 — 症状 → 执行此操作 → 绝不要这样做

根据用户的请求匹配一行,逐字应用“执行此操作”列,并在完成前确认“验证”列中的要求。

用户请求……执行此操作(在 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),并询问内置或框架提供的方法时:

  1. 创建或复用一个 JsonSerializerOptions,并设置 PropertyNamingPolicy = JsonNamingPolicy.PascalCase。
  2. 使用这些选项进行序列化。

不要编写 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<T> 类型的类型元数据(而不是需要强制转换的非泛型 JsonTypeInfo)时:

  1. 调用 options.GetTypeInfo<T>() — 它会直接返回 JsonTypeInfo<T>。
  2. 将其赋值给 JsonTypeInfo<T> 变量并使用它(例如,将其传递给 JsonSerializer.Serialize/Deserialize)。

不要调用非泛型的 GetTypeInfo(Type) 重载并强制转换结果。

需要解析器。 除非选项中设置了 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 的元数据并据此分支处理,且在元数据不存在时不抛出异常:

  1. 调用 options.TryGetTypeInfo<T>(out var info)。
  2. 显式处理 true 分支(元数据已解析,使用 info)和 false 分支 (未解析)。

不要用 try/catch 包装 GetTypeInfo<T>() 来检测缺失情况——这正是此 API 要消除的反模式。当没有解析器能为 T 生成元数据时,TryGetTypeInfo<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 — 基于文件的应用(最快,但有一个注意事项)

保存为 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] 属性。
  • 字典键请求设置 DictionaryKeyPolicy,而不只是 PropertyNamingPolicy。
  • 类型化元数据请求使用泛型 GetTypeInfo<T>()——不对非泛型 JsonTypeInfo 进行强制转换——且选项设置了 TypeInfoResolver(例如 DefaultJsonTypeInfoResolver),以免调用抛出 NoMetadataForType。
  • 探测请求使用 TryGetTypeInfo<T>(out …)——不在 GetTypeInfo 外层使用 try/catch。
  • 程序已实际运行(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显式指定 net11.0,以解析 .NET 11 API,并在输出中显示目标框架。
未运行程序就声称成功运行 dotnet run 并粘贴实际的 JSON 输出;目标是一个可运行且已执行的程序。

更多信息

评论

支持 Markdown