.NET 11 以降、JsonSerializer は C# 15 の union 型をサポートします。 共用体は、その宣言内でケース型の 1 つを保持しています。
JsonSerializer はアクティブなケース値を書き込み、読み戻すことができます。
共用体値のシリアル化と逆シリアル化
各ケースが異なる JSON トークン型を持つ共用体を宣言します。
public union Payload(int, string, Message);
public sealed record Message(string Text);
JsonSerializerを使用して、共用体をシリアル化および逆シリアル化します。
Payload payload = new Message("Ready");
string json = JsonSerializer.Serialize(payload);
Payload copy = JsonSerializer.Deserialize<Payload>(json);
シリアル化された JSON には、ラッパーや判別子ではなく、有効なケースの値が含まれます。
{"Text":"Ready"}
既定では、シリアライザーは受信 JSON をトークンの種類別に分類します。 上記の共用体では、JSON 番号が int選択され、JSON 文字列によって stringが選択され、JSON オブジェクトによって Messageが選択されます。
JsonSerializerOptions.Webでは、JSON 文字列からintを読み取ることもできます。
Payloadのintとstringの両方のケースで、文字列トークンの種類を要求します。 シリアライザーがいずれかのケースを解析する前に、"25%" も曖昧さのために JsonException をスローします。 Web の既定値を使用して文字列を読み取る場合は、ケースを選択する カスタム分類子を指定 します。
同じ JSON トークン型のケースを区別する
トークン分類では、両方とも JSON オブジェクトとしてシリアル化される 2 つのケースを区別できません。 JsonUnionAttribute適用し、JsonUnionTypeStructuralClassifierを選択して、プロパティ名でオブジェクト ケースを分類します。
[JsonUnion(TypeClassifier = typeof(JsonUnionTypeStructuralClassifier))]
public union Pet(Dog, Cat);
public sealed record Dog(string Name, string Breed);
public sealed record Cat(string Name, int Lives);
ここでは、 JsonUnionAttribute 既存の Pet 共用体の分類子を選択します。通常の型に適用しても、その型は共用体になりません。
分類子は、ペイロードにBreedが含まれている場合にDogを選択し、Livesが含まれるときにCatを選択します。
Pet pet = JsonSerializer.Deserialize<Pet>(
"""{"Name":"Rex","Breed":"Husky"}""");
JSON オブジェクトの場合、構造分類器は互換性のあるオブジェクトの候補から始め、認識されたルートレベルのプロパティ名を読み取るにつれて、その候補集合を絞り込みます。 必須プロパティは、それらが存在しないケースを除外します。 JsonUnmappedMemberHandling.Disallow は、ケースが宣言しないプロパティがペイロードに含まれている場合にケースを削除します。 分類は、1 つのケースが残っている場合にのみ成功します。
構造分類子は、プロパティ値、入れ子になったオブジェクト、文字列の内容、または配列要素を検査しません。 次の結果に留意してください。
- 0 個または複数の候補を残すペイロードは、 JsonExceptionをスローします。
- 分類子の作成時に、重なり合うオブジェクト コントラクトまたはシャドウオブジェクト コントラクトが拒否される場合があります。
- 同じ JSON トークン型を使用する複数の非オブジェクト ケースはサポートされていません。 たとえば、
Guidとstringの両方で JSON 文字列が使用されます。 - プレーン オブジェクトのケースは、ディクショナリ、
JsonObject、または他の POCO ではないオブジェクト形状のケースと混在させることはできません。 - 入れ子になった共用体のケースとポリモーフィックなケースはサポートされていません。
- ReferenceHandler.Preserveはサポートされていません。
- 大文字と小文字を区別できない構成の場合、シリアライザーが分類子を構築する際に NotSupportedException がスローされます。
共用体または閉じた階層を選択する
制御しない識別子のない JSON 形式を保持する必要がある場合、またはケースに個別の JSON 図形がある場合は、共用体を使用します。 たとえば、Payloadのintとstringのケースは、JSON トークンの種類によって区別できます。 ただし、 Pet(Dog, Cat)などのオブジェクト ケースの場合、プロパティ名の変更は、構造分類子が選択するケースに影響を与える可能性があります。
型と JSON コントラクトを制御すると、 推論されたポリモーフィズムを持つ閉じた階層で 、代わりに識別子を使用してオブジェクト ケースを識別できます。
[JsonPolymorphic(InferClosedTypePolymorphism = true)]
public closed record Event;
public sealed record Created(int Id) : Event;
public sealed record Deleted(int Id) : Event;
JsonSerializer.Serialize<Event>(new Created(42)) は {"$type":"Created","Id":42}を書き込みます。 どちらの派生型も Idを宣言しますが、識別子はそのプロパティとは別にケースを識別します。 これにより、プロパティが進化するにつれて、ケースの選択がより安定します。 共用体のケースとは異なり、派生型は基底クラスを共有する必要があり、推論されたポリモーフィズムをオプトインする必要があります。 closed 修飾子だけでは、識別子は追加されません。
カスタム分類子を提供する
既定のトークン分類または組み込みのJsonUnionTypeStructuralClassifierが要件を満たしていない場合に、JsonTypeClassifierFactoryから派生します。 カスタム分類器は、ほかの構造上のルールを使ってユニオンのケースを選択できます。 次のいずれかの場所にファクトリを登録します。
- コントラクトをカスタマイズするときに、デリゲートを JsonTypeInfo.TypeClassifier に割り当てます。
- 1つの共用体に対してJsonUnionAttribute.TypeClassifierを設定します。
- リフレクション ベースのシリアル化のために、ファクトリを JsonSerializerOptions.TypeClassifiers に追加します。
- ソース生成コンテキストの JsonSourceGenerationOptionsAttribute.TypeClassifiers を設定します。
分類子は現在の JSON 値を読み取り、 JsonTypeClassifierContext.UnionCasesからケース型のいずれかを返します。 シリアライザーは、最初にコントラクト デリゲートをチェックし、次に共用体ごとのファクトリ、オプション レベルのファクトリ、組み込みのトークン分類を確認します。
あいまいな共用体の場合、生成時に分類子が構成されていない限り、ソース ジェネレーターは診断を報告します。
null 値と既定の共用体値を処理する
共用体では、Null 許容のケース型を宣言することができます。 JSON null は、最初の null 許容ケースを選択します。 共用体に null 許容ケースがない場合、JSON null は既定の共用体値を生成します。 コンパイラによって生成された構造体共用体の場合、既定値にはアクティブなケースがなく、JSON nullとしてシリアル化されます。
ユニオン型のコントラクトをカスタマイズする
高度なシナリオでは、 JsonTypeInfoを使用して共用体メタデータをカスタマイズします。 共用体の JsonTypeInfo.Kind の値は JsonTypeInfoKind.Union です。 そのコントラクトは、次の内容を公開します。
- JsonTypeInfo.UnionCasesには、 JsonUnionCaseInfo エントリが含まれます。
- JsonTypeInfo.UnionConstructor は、case 型と値からユニオンを作成します。
- JsonTypeInfo.UnionDeconstructor: アクティブなケースの種類と値を返します。
- JsonTypeInfo.TypeClassifierは、デシリアライズ時に case を選択するものです。
JsonTypeInfoの変更の詳細については、「JSON コントラクトのカスタマイズ」を参照してください。
こちらも参照ください
.NET