Share via


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.Jsontidak 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 WeatherForecastWithPrevioustidak meliputiWindSpeed.
  • Output JSON dari serialisasi WeatherForecastWithPreviousAsObjectmeliputiWindSpeed.

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 WeatherForecastBasedasarnya . 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 FourDimensionalPointBasePoint 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 BasePointIPointWithTimeSeries karena keduanya adalah leluhur langsung. Ambiguitas ini akan menyebabkan NotSupportedException dilemparkan ketika mencoba menserialisasikan instans sebagai BasePointWithTimeSeriesIPoint.

// 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 dan class .
  • 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