Start Debugging

.NET 11 Preview 3 の System.Text.Json が PascalCase とメンバー単位のネーミングポリシーを追加

.NET 11 Preview 3 は System.Text.Json のネーミングポリシーの話を完成させます: JsonNamingPolicy.PascalCase、メンバーレベルの [JsonNamingPolicy] 属性、そしてクリーンな DTO のための型レベルの [JsonIgnore] デフォルト。

.NET 8 は System.Text.Json のための組み込みネーミングポリシーの最初のバッチ (camel、snake、kebab の両ケーシング) を導入しました。.NET 11 の Preview 3 は最後の明白なギャップを埋め、ほとんどの DTO 形状に対して手書きの JsonConverter を不要にする 2 つのノブをさらに追加します。作業は dotnet/runtime #124644#124645#124646 を通じて出荷されました。

PascalCase が組み込みポリシーに加わる

JsonNamingPolicy.PascalCase は Preview 3 で新しく、既存の CamelCaseSnakeCaseLowerSnakeCaseUpperKebabCaseLowerKebabCaseUpper の隣に座ります。.NET 側が既に PascalCase プロパティを使用し、JSON コントラクトも PascalCase であるときに欲しいポリシーで、Azure 管理 API、古い SOAP から REST へのゲートウェイ、一部の Microsoft Graph の形状でよくあります:

using System.Text.Json;

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.PascalCase
};

var json = JsonSerializer.Serialize(
    new { firstName = "Ada", age = 37 },
    options);
// {"FirstName":"Ada","Age":37}

Preview 3 以前は、デフォルト (ポリシーなし) のままにするか、1 行のカスタム JsonNamingPolicy サブクラスを書くかのどちらかでした。今は他のプリセットと肩を並べ、既存の JsonKnownNamingPolicy enum とクリーンにラウンドトリップします。

単一メンバーでネーミングを上書きする

より興味深い変更は、[JsonNamingPolicy] がメンバーレベルの属性になったことです。以前はポリシーは JsonSerializerOptions 上に住みグラフ全体に適用されていたので、その他は camelCase のコントラクトに 1 つだけ PascalCase の例外があると、厄介なプロパティすべてに [JsonPropertyName] のオーバーライドを書くか、完全にカスタムなポリシーを用意するしかありませんでした。.NET 11 Preview 3 では、同じ型の中でポリシーを混在させられます:

using System.Text.Json.Serialization;

public sealed class Webhook
{
    public string Url { get; set; } = "";

    [JsonNamingPolicy(JsonKnownNamingPolicy.KebabCaseLower)]
    public string RetryStrategy { get; set; } = "exponential";

    [JsonNamingPolicy(JsonKnownNamingPolicy.SnakeCaseLower)]
    public int MaxAttempts { get; set; } = 5;
}

PropertyNamingPolicy = JsonNamingPolicy.CamelCase で、Urlurl に、RetryStrategyretry-strategy に、MaxAttemptsmax_attempts にシリアライズされます。これは、外部システムが一貫していないときのプロパティごとの [JsonPropertyName] のノイズをたくさん取り除きます。

型レベルの [JsonIgnore] デフォルト

伴う変更は、[JsonIgnore(Condition = ...)] がプロパティだけでなく、型そのものにも合法になったことです (dotnet/runtime #124646)。クラスに付けると、その条件が型内のすべてのプロパティのデフォルトになります:

using System.Text.Json.Serialization;

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public sealed class PatchRequest
{
    public string? Name { get; set; }
    public string? Email { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
    public bool? IsActive { get; set; }
}

PatchRequest のすべての nullable プロパティは、null のとき payload から落ちます - これは JSON Merge Patch リクエスト形状が望むところそのものです。IsActive のオーバーライドは、明示的な false がそこで意味を持つので、再び入ります。同じパターンは以前、個々のプロパティに JsonIgnoreCondition.WhenWritingNull をつけるか、シリアライザーオプションに DefaultIgnoreCondition を付ける必要があり、それが他のすべての DTO を同じルールに従わせていました。

小さな面が重要な理由

属性レベルの制御は、チームがカスタムコンバーターをストックの System.Text.Json に置き換えることを可能にするものです。PascalCase は「自分でポリシーを書け」の最後の理由を取り除き、メンバー単位のネーミングは [JsonPropertyName] のボイラープレートの 1 クラスを削除し、型レベルの [JsonIgnore] は PATCH やイベント DTO が 1 箇所でデフォルトを設定することを可能にします。3 つの変更はすべて source generator とも動くので、Native AOT アプリは追加の設定なしにこれらを得ます。Preview 3 のライブラリノート が今月出荷される System.Text.Json の残りの更新を追跡しています。

< 戻る