Cara menserialisasi properti kelas turunan dengan System.Text.Json
Dalam artikel ini, Anda akan mempelajari cara menserialisasikan properti kelas turunan dengan System.Text.Json
namespace layanan.
Menserialisasikan properti kelas turunan
Dalam versi sebelum .NET 7, System.Text.Json
tidak mendukung serialisasi hierarki jenis polimorfik. Misalnya, jika jenis properti adalah antarmuka atau kelas abstrak, hanya properti yang ditentukan pada antarmuka atau kelas abstrak yang diserialisasikan, bahkan jika jenis runtime memiliki properti tambahan. Pengecualian untuk perilaku ini dijelaskan di bagian ini. Untuk informasi tentang dukungan di .NET 7, lihat Serialisasi Polimorfik di .NET 7.
Misalnya, Anda memiliki WeatherForecast
kelas dan kelas turunan 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
Dan misalkan argumen jenis Serialize
metode pada waktu kompilasi adalah 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)
Dalam skenario ini, WindSpeed
properti tidak diserialisasikan meskipun weatherForecast
objek adalah WeatherForecastDerived
objek. Hanya properti kelas dasar yang diserialisasikan:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
Perilaku ini dimaksudkan untuk membantu mencegah paparan data yang tidak disengaja dalam jenis yang dibuat runtime bahasa umum turunan.
Untuk menserialisasikan properti jenis turunan dalam contoh sebelumnya, gunakan salah satu pendekatan berikut:
Memanggil kelebihan beban Serialize yang memungkinkan Anda menentukan jenis pada durasi:
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)
Objek yang akan diserialisasikan sebagai
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)
Dalam skenario contoh sebelumnya, kedua pendekatan menyebabkan WindSpeed
properti disertakan dalam output JSON:
{
"WindSpeed": 35,
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
Penting
Pendekatan ini menyediakan serialisasi polimorfik hanya untuk objek akar yang akan diserialisasikan, bukan untuk properti objek akar tersebut.
Anda bisa mendapatkan serialisasi polimorfik untuk objek tingkat bawah jika Anda mendefinisikannya sebagai jenis object
. Misalnya, kelas Anda WeatherForecast
memiliki properti bernama PreviousForecast
yang dapat didefinisikan sebagai jenis WeatherForecast
atau object
:
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
Jika PreviousForecast
properti berisi instans WeatherForecastDerived
:
- Output JSON dari serialisasi
WeatherForecastWithPrevious
tidak meliputiWindSpeed
. - Output JSON dari serialisasi
WeatherForecastWithPreviousAsObject
meliputiWindSpeed
.
Untuk menserialisasikan WeatherForecastWithPreviousAsObject
, tidak perlu memanggil Serialize<object>
atau GetType
karena objek akar bukan objek yang mungkin berasal dari jenis turunan. Contoh kode berikut tidak memanggil Serialize<object>
atau GetType
:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject1, options)
Kode sebelumnya bersambung dengan benarWeatherForecastWithPreviousAsObject
:
{
"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"
}
}
Pendekatan yang sama untuk mendefinisikan properti seperti object
yang berfungsi dengan antarmuka. Misalkan Anda memiliki antarmuka dan implementasi berikut, dan Anda ingin membuat serial kelas dengan properti yang berisi instans implementasi:
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
Saat Anda membuat serialisasi instans Forecasts
, hanya Tuesday
menampilkan WindSpeed
properti, karena Tuesday
didefinisikan sebagai object
:
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)
Contoh berikut menunjukkan JSON yang dihasilkan dari kode sebelumnya:
{
"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
}
}
Catatan
Artikel ini berisi tentang serialisasi, bukan deserialisasi. Deserialisasi polimorfik tidak didukung dalam versi sebelum .NET 7, tetapi sebagai solusinya, Anda dapat menulis pengonversi kustom, seperti contoh dalam Mendukung deserialisasi polimorfik. Untuk informasi selengkapnya tentang bagaimana .NET 7 mendukung serialisasi polimorfik dan deserialisasi, lihat Cara membuat serialisasi properti kelas turunan dengan System.Text.Json di .NET 7.
Dimulai dengan .NET 7, System.Text.Json
mendukung serialisasi dan deserialisasi hierarki jenis polimorfik dengan anotasi atribut.
Atribut | Deskripsi |
---|---|
JsonDerivedTypeAttribute | Ketika ditempatkan pada deklarasi jenis, menunjukkan bahwa subjenis yang ditentukan harus dipilih ke dalam serialisasi polimorfik. Ini juga mengekspos kemampuan untuk menentukan jenis diskriminator. |
JsonPolymorphicAttribute | Ketika ditempatkan pada deklarasi jenis, menunjukkan bahwa jenis harus diserialisasikan secara polimorfik. Ini juga mengekspos berbagai opsi untuk mengonfigurasi serialisasi polimorfik dan deserialisasi untuk jenis tersebut. |
Misalnya, Anda memiliki WeatherForecastBase
kelas dan kelas turunan 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
Dan misalkan argumen jenis Serialize<TValue>
metode pada waktu kompilasi adalah WeatherForecastBase
:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecastBase>(weatherForecastBase, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(WeatherForecastBase, options)
Dalam skenario ini, properti diserialisasikan City
karena weatherForecastBase
objek sebenarnya adalah WeatherForecastWithCity
objek. Konfigurasi ini memungkinkan serialisasi polimorfik untuk WeatherForecastBase
, khususnya ketika jenis runtime adalah WeatherForecastWithCity
:
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}
Meskipun round-tripping payload seperti WeatherForecastBase
yang didukung, payload tidak akan terwujud sebagai jenis run-time .WeatherForecastWithCity
Sebaliknya, itu akan terwujud sebagai jenis run-time dari 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
Bagian berikut menjelaskan cara menambahkan metadata untuk mengaktifkan round-tripping dari jenis turunan.
Diskriminator jenis polimorfik
Untuk mengaktifkan deserialisasi polimorfik, Anda harus menentukan jenis diskriminator untuk kelas turunan:
[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
Dengan metadata tambahan, khususnya, diskriminator jenis, serializer dapat menserialisasikan dan mendeserialisasi payload sebagai WeatherForecastWithCity
jenis dari jenis WeatherForecastBase
dasarnya . Serialisasi akan memancarkan JSON bersama dengan metadata diskriminator jenis:
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"
' }
Dengan jenis diskriminator, serializer dapat mendeserialisasi payload secara polimorfik sebagai WeatherForecastWithCity
:
WeatherForecastBase value = JsonSerializer.Deserialize<WeatherForecastBase>(json);
Console.WriteLine(value is WeatherForecastWithCity); // True
Catatan
Diskriminator jenis harus ditempatkan di awal objek JSON, dikelompokkan bersama dengan properti metadata lainnya seperti $id
dan $ref
.
Dim value As WeatherForecastBase = JsonSerializer.Deserialize(json)
Console.WriteLine(value is WeatherForecastWithCity) // True
Mencampur dan mencocokkan format diskriminator jenis
Pengidentifikasi diskriminator jenis valid dalam bentuk string
atau int
, sehingga berikut ini valid:
[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...
' }
Meskipun API mendukung konfigurasi diskriminator jenis pencampuran dan pencocokan, api tidak disarankan. Rekomendasi umumnya adalah menggunakan semua string
jenis diskriminator, semua int
jenis diskriminator, atau tidak ada diskriminator sama sekali. Contoh berikut menunjukkan cara mencampur dan mencocokkan konfigurasi diskriminator jenis:
[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
Dalam contoh sebelumnya, BasePoint
jenis tidak memiliki diskriminator jenis, sementara ThreeDimensionalPoint
jenis memiliki int
diskriminator jenis, dan FourDimensionalPoint
memiliki string
diskriminator jenis.
Penting
Agar serialisasi polimorfik berfungsi, jenis nilai berseri harus dari jenis dasar polimorfik. Ini termasuk menggunakan jenis dasar sebagai parameter jenis generik saat menserialisasikan nilai tingkat akar, sebagai jenis properti serial yang dideklarasikan, atau sebagai elemen koleksi dalam koleksi yang diserialisasikan.
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
Mengkustomisasi nama diskriminator jenis
Nama properti default untuk jenis diskriminator adalah $type
. Untuk mengkustomisasi nama properti, gunakan seperti yang JsonPolymorphicAttribute diperlihatkan dalam contoh berikut:
[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
Dalam kode sebelumnya, JsonPolymorphic
atribut mengonfigurasi TypeDiscriminatorPropertyName
ke "$discriminator"
nilai . Dengan nama diskriminator jenis yang dikonfigurasi, contoh berikut menunjukkan jenis yang diserialisasikan ThreeDimensionalPoint
sebagai JSON:
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 }
Tip
Hindari menggunakan JsonPolymorphicAttribute.TypeDiscriminatorPropertyName yang berkonflik dengan properti dalam hierarki jenis Anda.
Menangani jenis turunan yang tidak diketahui
Untuk menangani jenis turunan yang tidak diketahui, Anda harus memilih dukungan tersebut menggunakan anotasi pada jenis dasar. Pertimbangkan hierarki jenis berikut:
[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
Karena konfigurasi tidak secara eksplisit memilih dukungan untuk FourDimensionalPoint
, mencoba membuat serialisasi instans sebagaimana FourDimensionalPoint
BasePoint
akan mengakibatkan pengecualian run-time:
JsonSerializer.Serialize<BasePoint>(new FourDimensionalPoint()); // throws NotSupportedException
JsonSerializer.Serialize(Of BasePoint)(New FourDimensionalPoint()) ' throws NotSupportedException
Anda dapat mengubah perilaku default dengan menggunakan JsonUnknownDerivedTypeHandling enum, yang dapat ditentukan sebagai berikut:
[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
Alih-alih kembali ke jenis dasar, Anda dapat menggunakan FallBackToNearestAncestor
pengaturan untuk kembali ke kontrak jenis turunan terdekat yang dinyatakan:
[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
Dengan konfigurasi seperti contoh sebelumnya, ThreeDimensionalPoint
jenis akan diserialisasikan sebagai BasePoint
:
// Serializes using the contract for BasePoint
JsonSerializer.Serialize<IPoint>(new ThreeDimensionalPoint());
' Serializes using the contract for BasePoint
JsonSerializer.Serialize(Of IPoint)(New ThreeDimensionalPoint())
Namun, jatuh kembali ke leluhur terdekat mengakui kemungkinan ambiguitas "berlian". Pertimbangkan hierarki jenis berikut sebagai contoh:
[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
Dalam hal ini, jenisnya BasePointWithTimeSeries
dapat diserialisasikan sebagai atau BasePoint
IPointWithTimeSeries
karena keduanya adalah leluhur langsung. Ambiguitas ini akan menyebabkan NotSupportedException dilemparkan ketika mencoba menserialisasikan instans sebagai BasePointWithTimeSeries
IPoint
.
// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());
' throws NotSupportedException
JsonSerializer.Serialize(Of IPoint)(New BasePointWithTimeSeries())
Mengonfigurasi polimorfisme dengan model kontrak
Untuk kasus penggunaan di mana anotasi atribut tidak praktis atau tidak mungkin (seperti model domain besar, hierarki perakitan silang, atau hierarki dalam dependensi pihak ketiga), untuk mengonfigurasi polimorfisme menggunakan model kontrak. Model kontrak adalah sekumpulan API yang dapat digunakan untuk mengonfigurasi polimorfisme dalam hierarki jenis dengan membuat subkelas kustom DefaultJsonTypeInfoResolver yang secara dinamis menyediakan konfigurasi polimorfik per jenis, seperti yang ditunjukkan dalam contoh berikut:
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
Detail serialisasi polimorfik tambahan
- Serialisasi polimorfik mendukung jenis turunan yang telah secara eksplisit dipilih melalui JsonDerivedTypeAttribute. Jenis yang tidak dinyatakan akan menghasilkan pengecualian run-time. Perilaku dapat diubah dengan mengonfigurasi JsonPolymorphicAttribute.UnknownDerivedTypeHandling properti.
- Konfigurasi polimorfik yang ditentukan dalam jenis turunan tidak diwariskan oleh konfigurasi polimorfik dalam jenis dasar. Jenis dasar harus dikonfigurasi secara independen.
- Hierarki polimorfik didukung untuk jenis
interface
danclass
. - Polimorfisme menggunakan diskriminator jenis hanya didukung untuk hierarki jenis yang menggunakan pengonversi default untuk objek, koleksi, dan jenis kamus.
- Polimorfisme didukung dalam pembuatan sumber berbasis metadata, tetapi bukan pembuatan sumber jalur cepat.
Lihat juga
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk