System.Text.Json で派生クラスのプロパティをシリアル化する方法
この記事では、System.Text.Json
名前空間を使用して派生クラスのプロパティをシリアル化する方法について説明します。
派生クラスのプロパティのシリアル化
.NET 7 より前のバージョンでは、System.Text.Json
はポリモーフィック型階層のシリアル化を "サポートしていません"。 たとえば、プロパティの種類がインターフェイスまたは抽象クラスである場合は、ランタイム型に追加のプロパティが含まれる場合でも、インターフェイスまたは抽象クラスに対して定義されたプロパティのみがシリアル化されます。 この動作の例外については、このセクションで説明します。 .NET 7 でのサポートについては、.NET 7 でのポリモーフィックなシリアル化に関するページを参照してください。
たとえば、WeatherForecast
クラスと派生クラス WeatherForecastDerived
があるとします。
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
Public Class WeatherForecast
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
End Class
public class WeatherForecastDerived : WeatherForecast
{
public int WindSpeed { get; set; }
}
Public Class WeatherForecastDerived
Inherits WeatherForecast
Public Property WindSpeed As Integer
End Class
また、コンパイル時の Serialize
メソッドの型引数が WeatherForecast
であるとします。
var options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecast>(weatherForecast, options);
Dim options As JsonSerializerOptions = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecast1, options)
このシナリオでは、weatherForecast
オブジェクトが WeatherForecastDerived
オブジェクトであっても、WindSpeed
プロパティはシリアル化されません。 基本クラスのプロパティのみがシリアル化されます。
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
この動作は、ランタイムによって作成される派生型内でデータが誤って公開されるのを防ぐためのものです。
前の例で派生型のプロパティをシリアル化するには、次のいずれかのアプローチを使用します。
実行時に型を指定できるようにする Serialize のオーバーロードを呼び出します。
options = new JsonSerializerOptions { WriteIndented = true }; jsonString = JsonSerializer.Serialize(weatherForecast, weatherForecast.GetType(), options);
options = New JsonSerializerOptions With { .WriteIndented = True } jsonString = JsonSerializer.Serialize(weatherForecast1, weatherForecast1.[GetType](), options)
オブジェクトを
object
としてシリアル化するように宣言します。options = new JsonSerializerOptions { WriteIndented = true }; jsonString = JsonSerializer.Serialize<object>(weatherForecast, options);
options = New JsonSerializerOptions With { .WriteIndented = True } jsonString = JsonSerializer.Serialize(Of Object)(weatherForecast1, options)
前の例のシナリオでは、どちらのアプローチでも、WindSpeed
プロパティが JSON 出力に含まれるようになります。
{
"WindSpeed": 35,
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
重要
これらのアプローチでは、シリアル化するルート オブジェクトに対してのみポリモーフィックなシリアル化が提供され、そのルート オブジェクトのプロパティに対しては提供されません。
object
型として定義すると、下位レベルのオブジェクトのポリモーフィックなシリアル化を取得できます。 たとえば、WeatherForecast
クラスに、WeatherForecast
または object
型として定義できる PreviousForecast
という名前のプロパティがあるとします。
public class WeatherForecastWithPrevious
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public WeatherForecast? PreviousForecast { get; set; }
}
Public Class WeatherForecastWithPrevious
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
Public Property PreviousForecast As WeatherForecast
End Class
public class WeatherForecastWithPreviousAsObject
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public object? PreviousForecast { get; set; }
}
Public Class WeatherForecastWithPreviousAsObject
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
Public Property PreviousForecast As Object
End Class
PreviousForecast
プロパティに WeatherForecastDerived
のインスタンスが含まれる場合:
WeatherForecastWithPrevious
のシリアル化からの JSON 出力にはWindSpeed
が含まれていません。WeatherForecastWithPreviousAsObject
のシリアル化からの JSON 出力にはWindSpeed
が含まれています。
ルート オブジェクトは派生型である可能性があるものではないため、WeatherForecastWithPreviousAsObject
をシリアル化するために Serialize<object>
または GetType
を呼び出す必要はありません。 次のコード例では Serialize<object>
または GetType
は呼び出されません。
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject1, options)
上記のコードでは、WeatherForecastWithPreviousAsObject
が正しくシリアル化されます。
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"PreviousForecast": {
"WindSpeed": 35,
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
}
object
と同じプロパティ定義のアプローチがインターフェイスで機能します。 次のインターフェイスと実装があり、実装インスタンスを含むプロパティを使用してクラスをシリアル化するとします。
namespace SystemTextJsonSamples
{
public interface IForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
public class Forecast : IForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public int WindSpeed { get; set; }
}
public class Forecasts
{
public IForecast? Monday { get; set; }
public object? Tuesday { get; set; }
}
}
Namespace SystemTextJsonSamples
Public Interface IForecast
Property [Date] As DateTimeOffset
Property TemperatureCelsius As Integer
Property Summary As String
End Interface
Public Class Forecast
Implements IForecast
Public Property [Date] As DateTimeOffset Implements IForecast.[Date]
Public Property TemperatureCelsius As Integer Implements IForecast.TemperatureCelsius
Public Property Summary As String Implements IForecast.Summary
Public Property WindSpeed As Integer
End Class
Public Class Forecasts
Public Property Monday As IForecast
Public Property Tuesday As Object
End Class
End Namespace
Forecasts
のインスタンスをシリアル化する場合、Tuesday
は object
として定義されているので、Tuesday
にのみ WindSpeed
プロパティが表示されます。
var forecasts = new Forecasts
{
Monday = new Forecast
{
Date = DateTime.Parse("2020-01-06"),
TemperatureCelsius = 10,
Summary = "Cool",
WindSpeed = 8
},
Tuesday = new Forecast
{
Date = DateTime.Parse("2020-01-07"),
TemperatureCelsius = 11,
Summary = "Rainy",
WindSpeed = 10
}
};
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(forecasts, options);
Dim forecasts1 As New Forecasts With {
.Monday = New Forecast With {
.[Date] = Date.Parse("2020-01-06"),
.TemperatureCelsius = 10,
.Summary = "Cool",
.WindSpeed = 8
},
.Tuesday = New Forecast With {
.[Date] = Date.Parse("2020-01-07"),
.TemperatureCelsius = 11,
.Summary = "Rainy",
.WindSpeed = 10
}
}
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(forecasts1, options)
次の例は、前のコードから生成された JSON を示しています。
{
"Monday": {
"Date": "2020-01-06T00:00:00-08:00",
"TemperatureCelsius": 10,
"Summary": "Cool"
},
"Tuesday": {
"Date": "2020-01-07T00:00:00-08:00",
"TemperatureCelsius": 11,
"Summary": "Rainy",
"WindSpeed": 10
}
}
Note
この記事では、逆シリアル化ではなくシリアル化について説明します。 .NET 7 より前のバージョンでは、ポリモーフィックな逆シリアル化はサポートされていませんが、回避策として、「ポリモーフィックな逆シリアル化のサポート」の例に示すようなカスタム コンバーターを記述できます。 .NET 7 でポリモーフィックなシリアル化と逆シリアル化をサポートする方法の詳細については、.NET 7 で System.Text.Json を使用して派生クラスのプロパティをシリアル化する方法に関するページを参照してください。
.NET 7 以降の System.Text.Json
では、ポリモーフィック型の階層のシリアル化と属性注釈による逆シリアル化がサポートされています。
属性 | 説明 |
---|---|
JsonDerivedTypeAttribute | 型宣言に配置された場合、指定したサブタイプをポリモーフィックなシリアル化にオプトインする必要があることを示します。 また、型判別子を指定する機能も公開されます。 |
JsonPolymorphicAttribute | 型宣言に配置された場合、型をポリモーフィックにシリアル化する必要があることを示します。 また、その型のポリモーフィックなシリアル化と逆シリアル化を構成するためのさまざまなオプションも公開されています。 |
たとえば、WeatherForecastBase
クラスと派生クラス WeatherForecastWithCity
があるとします。
[JsonDerivedType(typeof(WeatherForecastWithCity))]
public class WeatherForecastBase
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
<JsonDerivedType(GetType(WeatherForecastWithCity))>
Public Class WeatherForecastBase
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
End Class
public class WeatherForecastWithCity : WeatherForecastBase
{
public string? City { get; set; }
}
Public Class WeatherForecastWithCity
Inherits WeatherForecastBase
Public Property City As String
End Class
また、コンパイル時の Serialize<TValue>
メソッドの型引数が WeatherForecastBase
であるとします。
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecastBase>(weatherForecastBase, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(WeatherForecastBase, options)
このシナリオでは、weatherForecastBase
オブジェクトが実際には WeatherForecastWithCity
オブジェクトであるため、City
プロパティはシリアル化されません。 この構成では、特にランタイムの種類が WeatherForecastWithCity
である場合に、WeatherForecastBase
のポリモーフィックなシリアル化が有効になります。
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}
WeatherForecastBase
としてのペイロードのラウンドトリップは、サポートされていますが、WeatherForecastWithCity
のランタイム タイプとして具体化されません。 代わりに、WeatherForecastBase
の実行時型として具体化されます。
WeatherForecastBase value = JsonSerializer.Deserialize<WeatherForecastBase>("""
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}
""");
Console.WriteLine(value is WeatherForecastWithCity); // False
Dim value As WeatherForecastBase = JsonSerializer.Deserialize(@"
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}")
Console.WriteLine(value is WeatherForecastWithCity) // False
次のセクションでは、派生型のラウンドトリップを有効にするためにメタデータを追加する方法について説明します。
ポリモーフィック型の判別子
ポリモーフィックな逆シリアル化を有効にするには、派生クラスの型判別子を指定する必要があります。
[JsonDerivedType(typeof(WeatherForecastBase), typeDiscriminator: "base")]
[JsonDerivedType(typeof(WeatherForecastWithCity), typeDiscriminator: "withCity")]
public class WeatherForecastBase
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
public class WeatherForecastWithCity : WeatherForecastBase
{
public string? City { get; set; }
}
<JsonDerivedType(GetType(WeatherForecastBase), "base")>
<JsonDerivedType(GetType(WeatherForecastWithCity), "withCity")>
Public Class WeatherForecastBase
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
End Class
Public Class WeatherForecastWithCity
Inherits WeatherForecastBase
Public Property City As String
End Class
追加されたメタデータ (具体的には、型判別子) を使用すると、シリアライザーでは、基本型 WeatherForecastBase
からペイロードを WeatherForecastWithCity
型としてシリアル化および逆シリアル化できます。 シリアル化を行うと、型判別子メタデータと共に JSON が出力されます。
WeatherForecastBase weather = new WeatherForecastWithCity
{
City = "Milwaukee",
Date = new DateTimeOffset(2022, 9, 26, 0, 0, 0, TimeSpan.FromHours(-5)),
TemperatureCelsius = 15,
Summary = "Cool"
}
var json = JsonSerializer.Serialize<WeatherForecastBase>(weather, options);
Console.WriteLine(json);
// Sample output:
// {
// "$type" : "withCity",
// "City": "Milwaukee",
// "Date": "2022-09-26T00:00:00-05:00",
// "TemperatureCelsius": 15,
// "Summary": "Cool"
// }
Dim weather As WeatherForecastBase = New WeatherForecastWithCity With
{
.City = "Milwaukee",
.[Date] = New DateTimeOffset(2022, 9, 26, 0, 0, 0, TimeSpan.FromHours(-5)),
.TemperatureCelsius = 15,
.Summary = "Cool"
}
Dim json As String = JsonSerializer.Serialize(weather, options)
Console.WriteLine(json)
' Sample output:
' {
' "$type" : "withCity",
' "City": "Milwaukee",
' "Date": "2022-09-26T00:00:00-05:00",
' "TemperatureCelsius": 15,
' "Summary": "Cool"
' }
型判別子を使用すると、シリアライザーでは、次のように、ペイロードを WeatherForecastWithCity
としてポリモーフィックに逆シリアル化できます。
WeatherForecastBase value = JsonSerializer.Deserialize<WeatherForecastBase>(json);
Console.WriteLine(value is WeatherForecastWithCity); // True
Note
型識別子は、JSON オブジェクトの先頭に配置し、$id
や $ref
のような他のメタデータ プロパティと一緒にグループ化する必要があります。
Dim value As WeatherForecastBase = JsonSerializer.Deserialize(json)
Console.WriteLine(value is WeatherForecastWithCity) // True
型判別子の形式の混在と一致
型判別子の識別子は、string
または int
のいずれかの形式で有効であるため、以下は有効となります。
[JsonDerivedType(typeof(WeatherForecastWithCity), 0)]
[JsonDerivedType(typeof(WeatherForecastWithTimeSeries), 1)]
[JsonDerivedType(typeof(WeatherForecastWithLocalNews), 2)]
public class WeatherForecastBase { }
var json = JsonSerializer.Serialize<WeatherForecastBase>(new WeatherForecastWithTimeSeries());
Console.WriteLine(json);
// Sample output:
// {
// "$type" : 1,
// Omitted for brevity...
// }
<JsonDerivedType(GetType(WeatherForecastWithCity), 0)>
<JsonDerivedType(GetType(WeatherForecastWithTimeSeries), 1)>
<JsonDerivedType(GetType(WeatherForecastWithLocalNews), 2)>
Public Class WeatherForecastBase
End Class
Dim json As String = JsonSerializer.Serialize(Of WeatherForecastBase)(New WeatherForecastWithTimeSeries())
Console.WriteLine(json)
' Sample output:
' {
' "$type" : 1,
' Omitted for brevity...
' }
API では、型判別子の構成の混在と一致がサポートされていますが、推奨はされません。 一般的に推奨されるのは、すべての string
型判別子を使用する、すべての int
型判別子を使用する、まったく判別子を使用しない、のいずれかです。 次の例は、型判別子の構成を混在させ、一致させる方法を示しています。
[JsonDerivedType(typeof(ThreeDimensionalPoint), typeDiscriminator: 3)]
[JsonDerivedType(typeof(FourDimensionalPoint), typeDiscriminator: "4d")]
public class BasePoint
{
public int X { get; set; }
public int Y { get; set; }
}
public class ThreeDimensionalPoint : BasePoint
{
public int Z { get; set; }
}
public sealed class FourDimensionalPoint : ThreeDimensionalPoint
{
public int W { get; set; }
}
<JsonDerivedType(GetType(ThreeDimensionalPoint), 3)>
<JsonDerivedType(GetType(FourDimensionalPoint), "4d")>
Public Class BasePoint
Public Property X As Integer
Public Property Y As Integer
End Class
Public Class ThreeDimensionalPoint
Inherits BasePoint
Public Property Z As Integer
End Class
Public NotInheritable Class FourDimensionalPoint
Inherits ThreeDimensionalPoint
Public Property W As Integer
End Class
前の例では、BasePoint
型には型判別子がありませんが、ThreeDimensionalPoint
型には int
型判別子があり、FourDimensionalPoint
には string
型判別子があります。
重要
ポリモーフィックなシリアル化を機能させるには、シリアル化される値の型をポリモーフィック基本型の型とする必要があります。 これには、ルートレベルの値をシリアル化するときのジェネリック型パラメーターとして、シリアル化されたプロパティの宣言された型として、またはシリアル化されたコレクションのコレクション要素として、基本型を使用することが含まれます。
using System.Text.Json;
using System.Text.Json.Serialization;
PerformRoundTrip<BasePoint>();
PerformRoundTrip<ThreeDimensionalPoint>();
PerformRoundTrip<FourDimensionalPoint>();
static void PerformRoundTrip<T>() where T : BasePoint, new()
{
var json = JsonSerializer.Serialize<BasePoint>(new T());
Console.WriteLine(json);
BasePoint? result = JsonSerializer.Deserialize<BasePoint>(json);
Console.WriteLine($"result is {typeof(T)}; // {result is T}");
Console.WriteLine();
}
// Sample output:
// { "X": 541, "Y": 503 }
// result is BasePoint; // True
//
// { "$type": 3, "Z": 399, "X": 835, "Y": 78 }
// result is ThreeDimensionalPoint; // True
//
// { "$type": "4d", "W": 993, "Z": 427, "X": 508, "Y": 741 }
// result is FourDimensionalPoint; // True
Imports System.Text.Json
Imports System.Text.Json.Serialization
Module Program
Sub Main()
PerformRoundTrip(Of BasePoint)()
PerformRoundTrip(Of ThreeDimensionalPoint)()
PerformRoundTrip(Of FourDimensionalPoint)()
End Sub
Private Sub PerformRoundTrip(Of T As {BasePoint, New})()
Dim json = JsonSerializer.Serialize(Of BasePoint)(New T())
Console.WriteLine(json)
Dim result As BasePoint = JsonSerializer.Deserialize(Of BasePoint)(json)
Console.WriteLine($"result is {GetType(T)}; // {TypeOf result Is T}")
Console.WriteLine()
End Sub
End Module
' Sample output:
' { "X": 649, "Y": 754 }
' result is BasePoint; // True
'
' { "$type": 3, "Z": 247, "X": 814, "Y": 56 }
' result is ThreeDimensionalPoint; // True
'
' { "$type": "4d", "W": 427, "Z": 193, "X": 112, "Y": 935 }
' result is FourDimensionalPoint; // True
型識別子名をカスタマイズする
型識別子の既定のプロパティ名は $type
です。 プロパティ名をカスタマイズするには、JsonPolymorphicAttribute を次の例のように使用します。
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$discriminator")]
[JsonDerivedType(typeof(ThreeDimensionalPoint), typeDiscriminator: "3d")]
public class BasePoint
{
public int X { get; set; }
public int Y { get; set; }
}
public sealed class ThreeDimensionalPoint : BasePoint
{
public int Z { get; set; }
}
<JsonPolymorphic(TypeDiscriminatorPropertyName:="$discriminator")>
<JsonDerivedType(GetType(ThreeDimensionalPoint), "3d")>
Public Class BasePoint
Public Property X As Integer
Public Property Y As Integer
End Class
Public Class ThreeDimensionalPoint
Inherits BasePoint
Public Property Z As Integer
End Class
上記のコードでは、JsonPolymorphic
属性によって TypeDiscriminatorPropertyName
が "$discriminator"
値に構成されます。 次の例では、型識別子の名前が構成されている場合に、JSON としてシリアル化された ThreeDimensionalPoint
型を示しています。
BasePoint point = new ThreeDimensionalPoint { X = 1, Y = 2, Z = 3 };
var json = JsonSerializer.Serialize<BasePoint>(point);
Console.WriteLine(json);
// Sample output:
// { "$discriminator": "3d", "X": 1, "Y": 2, "Z": 3 }
Dim point As BasePoint = New ThreeDimensionalPoint With { .X = 1, .Y = 2, .Z = 3 }
Dim json As String = JsonSerializer.Serialize(Of BasePoint)(point)
Console.WriteLine(json)
' Sample output:
' { "$discriminator": "3d", "X": 1, "Y": 2, "Z": 3 }
ヒント
型階層内のプロパティと競合する JsonPolymorphicAttribute.TypeDiscriminatorPropertyName の使用は避けてください。
不明な派生型を処理する
不明な派生型を処理するには、基本型の注釈を使用して、このようなサポートを選択する必要があります。 たとえば、次の型について考えます。
[JsonDerivedType(typeof(ThreeDimensionalPoint))]
public class BasePoint
{
public int X { get; set; }
public int Y { get; set; }
}
public class ThreeDimensionalPoint : BasePoint
{
public int Z { get; set; }
}
public class FourDimensionalPoint : ThreeDimensionalPoint
{
public int W { get; set; }
}
<JsonDerivedType(GetType(ThreeDimensionalPoint))>
Public Class BasePoint
Public Property X As Integer
Public Property Y As Integer
End Class
Public Class ThreeDimensionalPoint
Inherits BasePoint
Public Property Z As Integer
End Class
Public NotInheritable Class FourDimensionalPoint
Inherits ThreeDimensionalPoint
Public Property W As Integer
End Class
この構成では FourDimensionalPoint
のサポートが明示的にオプトインされていないため、FourDimensionalPoint
のインスタンスを BasePoint
としてシリアル化しようとすると、実行時例外が発生します。
JsonSerializer.Serialize<BasePoint>(new FourDimensionalPoint()); // throws NotSupportedException
JsonSerializer.Serialize(Of BasePoint)(New FourDimensionalPoint()) ' throws NotSupportedException
JsonUnknownDerivedTypeHandling 列挙型を使用すれば、既定の動作を変更できます。これは、次のように指定できます。
[JsonPolymorphic(
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
[JsonDerivedType(typeof(ThreeDimensionalPoint))]
public class BasePoint
{
public int X { get; set; }
public int Y { get; set; }
}
public class ThreeDimensionalPoint : BasePoint
{
public int Z { get; set; }
}
public class FourDimensionalPoint : ThreeDimensionalPoint
{
public int W { get; set; }
}
<JsonPolymorphic(
UnknownDerivedTypeHandling:=JsonUnknownDerivedTypeHandling.FallBackToBaseType)>
<JsonDerivedType(GetType(ThreeDimensionalPoint))>
Public Class BasePoint
Public Property X As Integer
Public Property Y As Integer
End Class
Public Class ThreeDimensionalPoint
Inherits BasePoint
Public Property Z As Integer
End Class
Public NotInheritable Class FourDimensionalPoint
Inherits ThreeDimensionalPoint
Public Property W As Integer
End Class
基本型にフォールバックするのでなく、FallBackToNearestAncestor
設定を使用して最も近い宣言された派生型のコントラクトにフォールバックできます。
[JsonPolymorphic(
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)]
[JsonDerivedType(typeof(BasePoint)]
public interface IPoint { }
public class BasePoint : IPoint { }
public class ThreeDimensionalPoint : BasePoint { }
<JsonPolymorphic(
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)>
<JsonDerivedType(GetType(BasePoint)>
Public Interface IPoint
End Interface
Public Class BasePoint
Inherits IPoint
End Class
Public Class ThreeDimensionalPoint
Inherits BasePoint
End Class
前の例のような構成を使用すると、ThreeDimensionalPoint
型は次のように BasePoint
としてシリアル化されます。
// Serializes using the contract for BasePoint
JsonSerializer.Serialize<IPoint>(new ThreeDimensionalPoint());
' Serializes using the contract for BasePoint
JsonSerializer.Serialize(Of IPoint)(New ThreeDimensionalPoint())
ただし、最も近い祖先にフォールバックすると、"ダイヤモンド" のあいまいさの可能性が認められます。 例として次の型階層を考えてみます。
[JsonPolymorphic(
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)]
[JsonDerivedType(typeof(BasePoint))]
[JsonDerivedType(typeof(IPointWithTimeSeries))]
public interface IPoint { }
public interface IPointWithTimeSeries : IPoint { }
public class BasePoint : IPoint { }
public class BasePointWithTimeSeries : BasePoint, IPointWithTimeSeries { }
<JsonPolymorphic(
UnknownDerivedTypeHandling:=JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)>
<JsonDerivedType(GetType(BasePoint))>
<JsonDerivedType(GetType(IPointWithTimeSeries))>
Public Interface IPoint
End Interface
Public Interface IPointWithTimeSeries
Inherits IPoint
End Interface
Public Class BasePoint
Implements IPoint
End Class
Public Class BasePointWithTimeSeries
Inherits BasePoint
Implements IPointWithTimeSeries
End Class
この場合、BasePointWithTimeSeries
型は BasePoint
または IPointWithTimeSeries
としてシリアル化することが可能です。それらは、両方とも直接の先祖だからです。 このあいまいさが原因で、BasePointWithTimeSeries
のインスタンスを IPoint
としてシリアル化しようとすると NotSupportedException がスローされます。
// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());
' throws NotSupportedException
JsonSerializer.Serialize(Of IPoint)(New BasePointWithTimeSeries())
コントラクト モデルを使用してポリモーフィズムを構成する
属性注釈が実用的でない、または適切でないというユース ケース (大規模なドメイン モデル、アセンブリ間階層、サード パーティの依存関係の階層など) の場合、ポリモーフィズムを構成するには、コントラクト モデルを使用します。 コントラクト モデルは API のセットであり、これを使用することで、次の例に示すように、型ごとに動的にポリモーフィック構成を提供するカスタム DefaultJsonTypeInfoResolver サブクラスを作成して、型階層内にポリモーフィズムを構成することができます。
public class PolymorphicTypeResolver : DefaultJsonTypeInfoResolver
{
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);
Type basePointType = typeof(BasePoint);
if (jsonTypeInfo.Type == basePointType)
{
jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
{
TypeDiscriminatorPropertyName = "$point-type",
IgnoreUnrecognizedTypeDiscriminators = true,
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization,
DerivedTypes =
{
new JsonDerivedType(typeof(ThreeDimensionalPoint), "3d"),
new JsonDerivedType(typeof(FourDimensionalPoint), "4d")
}
};
}
return jsonTypeInfo;
}
}
Public Class PolymorphicTypeResolver
Inherits DefaultJsonTypeInfoResolver
Public Overrides Function GetTypeInfo(
ByVal type As Type,
ByVal options As JsonSerializerOptions) As JsonTypeInfo
Dim jsonTypeInfo As JsonTypeInfo = MyBase.GetTypeInfo(type, options)
Dim basePointType As Type = GetType(BasePoint)
If jsonTypeInfo.Type = basePointType Then
jsonTypeInfo.PolymorphismOptions = New JsonPolymorphismOptions With {
.TypeDiscriminatorPropertyName = "$point-type",
.IgnoreUnrecognizedTypeDiscriminators = True,
.UnknownDerivedTypeHandling =
JsonUnknownDerivedTypeHandling.FailSerialization
}
jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(
New JsonDerivedType(GetType(ThreeDimensionalPoint), "3d"))
jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(
New JsonDerivedType(GetType(FourDimensionalPoint), "4d"))
End If
Return jsonTypeInfo
End Function
End Class
その他のポリモーフィックなシリアル化の詳細
- ポリモーフィックなシリアル化では、JsonDerivedTypeAttribute を介して明示的にオプトインされた派生型がサポートされます。 宣言されていない型を使用すると、実行時例外が発生します。 この動作は、JsonPolymorphicAttribute.UnknownDerivedTypeHandling プロパティを構成することにより変更できます。
- 派生型で指定されたポリモーフィック構成は、基本型のポリモーフィック構成では継承されません。 基本型は個別に構成する必要があります。
- ポリモーフィック階層は、
interface
およびclass
の両方の型でサポートされています。 - 型判別子を使用するポリモーフィズムは、オブジェクト、コレクション、ディクショナリ型用の既定のコンバーターを使用する型階層でのみサポートされます。
- ポリモーフィズムは、メタデータ ベースのソース生成ではサポートされていますが、高速パス ソースの生成ではサポートされていません。
関連項目
.NET