Sebuah List<Pengiriman> bertipe abstract class boleh berisi object turunan: pengiriman reguler dan kargo. Serializer menghadapi masalah dua arah pada situasi ini. Tanpa petunjuk, JsonSerializer.Serialize hanya menulis properti tipe deklarasi, sehingga properti turunan seperti EstimasiHari hilang; sebaliknya, deserialisasi ke tipe abstract tidak tahu class turunan mana yang harus dibuat.
Deklarasi Hierarki dengan Atribut #
Atribut [JsonPolymorphic] menetapkan nama properti discriminator, dan setiap [JsonDerivedType] mendaftarkan satu turunan beserta labelnya:
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$tipe")]
[JsonDerivedType(typeof(PengirimanReguler), "reguler")]
[JsonDerivedType(typeof(PengirimanKargo), "kargo")]
public abstract class Pengiriman
{
public int Biaya { get; set; }
}Serialisasi dan Round-Trip #
List<Pengiriman> daftar = new()
{
new PengirimanReguler { Biaya = 15000, EstimasiHari = 2 },
new PengirimanKargo { Biaya = 250000, Kilogram = 40 }
};
string json = JsonSerializer.Serialize(daftar);
Console.WriteLine(json);
List<Pengiriman> hasil = JsonSerializer.Deserialize<List<Pengiriman>>(json);
foreach (Pengiriman p in hasil)
{
Console.WriteLine(p.GetType().Name);
}Output yang Dihasilkan:
[{"$tipe":"reguler","EstimasiHari":2,"Biaya":15000},{"$tipe":"kargo","Kilogram":40,"Biaya":250000}]
PengirimanReguler
PengirimanKargoMari kita bedah hasilnya:
- Properti
"$tipe": label discriminator menempel pada setiap object JSON dan menjadi kunci pemilihan tipe saat dibaca kembali. - Properti turunan lebih dulu: serializer menulis properti class turunan (
EstimasiHari,Kilogram) sebelum properti base classBiaya. - Deserialization:
Deserialize<List<Pengiriman>>membaca"$tipe", mencocokkannya dengan daftar[JsonDerivedType], lalu membuat object turunan yang tepat;GetType().Namemembuktikan keduanya kembali sebagai class asal.
Perlu dicatat bahwa label discriminator dapat berupa angka alih-alih teks, misalnya [JsonDerivedType(typeof(PengirimanKargo), 2)], dan payload JSON menjadi menyimpan "$tipe":2. Mekanisme ini juga berlaku pada properti bertipe interface, bukan hanya abstract class. Yang tidak berubah adalah aturan keamanannya: hanya turunan yang terdaftar pada atribut yang boleh dibuat, sehingga daftar discriminator berfungsi sebagai allowlist.