System.Text.Json ソース ジェネレーターのフォールバック

JsonSerializerOptions が許可される JsonSerializer メソッドのいずれかを使用するとき、型が認識できない場合、System.Text.Json ソース ジェネレーターではリフレクション ベースのシリアル化にフォールバックしなくなりました。

以前の動作

次の .NET 6 でのソース生成の例を考えてみましょう。

JsonSerializer.Serialize(new Poco2(), typeof(Poco2), MyContext.Default);

[JsonSerializable(typeof(Poco1))]
public partial class MyContext : JsonSerializerContext {}

public class Poco1 { }
public class Poco2 { }

MyContext のシリアル化可能な型に Poco2 は含まれないため、次の例外でシリアル化は正常に失敗します。

System.InvalidOperationException:

'Metadata for type 'Poco2' was not provided to the serializer. The serializer method used does not 
support reflection-based creation of serialization-related type metadata. If using source generation, 
ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', 
along with any types that might be serialized polymorphically.

次に、ソース ジェネレーターによって構築された JsonSerializerOptions インスタンスを使用して同じ型 (MyContext) をシリアル化しようとする次の呼び出しを考えてみてください。

JsonSerializer.Serialize(new Poco2(), MyContext.Default.Options);

options インスタンスでは、フォールバック メカニズムとして既定のリフレクション ベースのコントラクト リゾルバーがサイレントで組み込まれているため、リフレクションを使用し型が正常にシリアル化されます。

JsonSerializerOptions.GetConverter(Type)JsonSerializerContext にアタッチされている options インスタンスにも、同じフォールバック ロジックが適用されます。 次のステートメントは、組み込みのリフレクション コンバーターを使用してコンバーターを返します。

JsonConverter converter = MyContext.Default.Options.GetConverter(typeof(Poco2));

新しい動作

.NET 7 以降、次の呼び出しは、JsonSerializerContext オーバーロードを使用した場合と同じ例外 (InvalidOperationException) で失敗します。

JsonSerializer.Serialize(new Poco2(), MyContext.Default.Options);

さらに、次のステートメントは NotSupportedException で失敗します。

JsonConverter converter = MyContext.Default.Options.GetConverter(typeof(Poco2));

導入されたバージョン

.NET 7

破壊的変更の種類

この変更は、バイナリの互換性に影響を与える可能性があります。

変更理由

以前の動作は、驚き最小の原則に違反し、最終的にソース生成が機能しません。 型の JSON シリアル化コントラクトをカスタマイズする機能のリリースにより、コントラクト メタデータのソースを微調整できるようになります。 これを念頭に置いて、代替ソースをサイレントで導入することは、さらに望ましくなくなります。

意図的に、または意図せず以前の動作に依存している場合があります。 お勧めの手順は、JsonSerializerContext の定義を更新して、すべての型の依存関係が含まれるようにすることです。

[JsonSerializable(typeof(Poco1))]
[JsonSerializable(typeof(Poco2))]
public partial class MyContext : JsonSerializerContext {}

これにより、トリミングの安全性など、ソース生成の利点をアプリケーションで最大限に活用できます。

ただし、特定のケースでは、このような変更を行うのが実用的でなかったり、不可能であったりする場合があります。 お勧めはできませんが、ソース生成シリアライザーでリフレクション フォールバックを再度有効にする方法がいくつかあります。

カスタム コントラクト リゾルバーを使用する

新しいコントラクト カスタマイズ機能を使って、必要に応じてリフレクションベースの解決にフォールバックするカスタム コントラクト リゾルバーを作成できます。

var options = new JsonSerializerOptions
{
     TypeInfoResolver = JsonTypeInfoResolver.Combine(MyContext.Default, new DefaultJsonTypeInfoResolver());
}

JsonSerializer.Serialize(new Poco2(), options); // Contract resolution falls back to the default reflection-based resolver.
options.GetConverter(typeof(Poco2)); // Returns the reflection-based converter.

AppContext スイッチを使用する

.NET 7 以降では、指定された AppContext 互換性スイッチを使用して、リフレクション フォールバックをグローバルに再度有効にすることができます。 アプリケーションのプロジェクト ファイルに次のエントリを追加して、アプリ内のすべてのソース生成コンテキストのリフレクション フォールバックを再度有効にします。 AppContext スイッチの使用について詳しくは、.NET ランタイム構成設定に関する記事を参照してください。

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Text.Json.Serialization.EnableSourceGenReflectionFallback" Value="true" />
</ItemGroup>

影響を受ける API