Eigenschappen van afgeleide klassen serialiseren met System.Text.Json
In dit artikel leert u hoe u eigenschappen van afgeleide klassen serialiseert met de System.Text.Json
naamruimte.
Eigenschappen van afgeleide klassen serialiseren
In versies vóór .NET 7 wordt System.Text.Json
de serialisatie van polymorfe typehiërarchieën niet ondersteund . Als het type van een eigenschap bijvoorbeeld een interface of een abstracte klasse is, worden alleen de eigenschappen die zijn gedefinieerd op de interface of abstracte klasse geserialiseerd, zelfs als het runtimetype aanvullende eigenschappen heeft. De uitzonderingen op dit gedrag worden in deze sectie uitgelegd. Zie Polymorfe serialisatie in .NET 7 voor meer informatie over ondersteuning in .NET 7.
Stel dat u een WeatherForecast
klasse en een afgeleide klasse WeatherForecastDerived
hebt:
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
Stel dat het typeargument van de Serialize
methode tijdens het compileren het volgende is 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)
In dit scenario wordt de WindSpeed
eigenschap niet geserialiseerd, zelfs niet als het weatherForecast
object een WeatherForecastDerived
object is. Alleen de eigenschappen van de basisklasse worden geserialiseerd:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
Dit gedrag is bedoeld om onbedoelde blootstelling van gegevens in een door runtime gemaakt type te voorkomen.
Als u de eigenschappen van het afgeleide type in het voorgaande voorbeeld wilt serialiseren, gebruikt u een van de volgende benaderingen:
Roep een overbelasting aan Serialize waarmee u het type tijdens runtime kunt opgeven:
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)
Declareer het object dat moet worden geserialiseerd als
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)
In het voorgaande voorbeeldscenario zorgen beide benaderingen ervoor dat de WindSpeed
eigenschap wordt opgenomen in de JSON-uitvoer:
{
"WindSpeed": 35,
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
Belangrijk
Deze benaderingen bieden alleen polymorfe serialisatie voor het hoofdobject dat moet worden geserialiseerd, niet voor eigenschappen van dat hoofdobject.
U kunt polymorfische serialisatie krijgen voor objecten op een lager niveau als u ze definieert als type object
. Stel dat uw WeatherForecast
klasse een eigenschap heeft met de naam PreviousForecast
die kan worden gedefinieerd als type WeatherForecast
of 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
Als de PreviousForecast
eigenschap een exemplaar van WeatherForecastDerived
:
- De JSON-uitvoer van serialiseren
WeatherForecastWithPrevious
bevatWindSpeed
niet. - De JSON-uitvoer van serialiseren
WeatherForecastWithPreviousAsObject
omvatWindSpeed
.
Als u wilt serialiseren WeatherForecastWithPreviousAsObject
, is het niet nodig om aan te roepen Serialize<object>
of GetType
omdat het hoofdobject niet het hoofdobject is dat van een afgeleid type kan zijn. In het volgende codevoorbeeld wordt geen aanroep Serialize<object>
uitgevoerd of GetType
:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject1, options)
Met de voorgaande code wordt het volgende correct geserialiseerd 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"
}
}
Dezelfde benadering van het definiëren van eigenschappen als object
met interfaces. Stel dat u de volgende interface en implementatie hebt en u een klasse wilt serialiseren met eigenschappen die implementatie-exemplaren bevatten:
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
Wanneer u een exemplaar Forecasts
serialiseert, wordt alleen Tuesday
de WindSpeed
eigenschap weergegeven, omdat Tuesday
deze is gedefinieerd als 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)
In het volgende voorbeeld ziet u de JSON die het resultaat is van de voorgaande code:
{
"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
}
}
Notitie
Dit artikel gaat over serialisatie, niet over deserialisatie. Polymorf deserialisatie wordt niet ondersteund in versies vóór .NET 7, maar als tijdelijke oplossing kunt u een aangepast conversieprogramma schrijven, zoals het voorbeeld in Polymorfische deserialisatie ondersteunen. Zie Eigenschappen van afgeleide klassen System.Text.Json serialiseren en deserialisatie in .NET 7 serialiseren voor meer informatie over hoe .NET 7 ondersteuning biedt voor polymorfe serialisatie en deserialisatie.
Vanaf .NET 7 ondersteunt System.Text.Json
polymorfe hiërarchieserialisatie en deserialisatie met kenmerkaantekeningen.
Kenmerk | Beschrijving |
---|---|
JsonDerivedTypeAttribute | Wanneer het wordt geplaatst op een typedeclaratie, geeft u aan dat het opgegeven subtype moet worden gekozen voor polymorfe serialisatie. Het geeft ook de mogelijkheid om een typediscriminator op te geven. |
JsonPolymorphicAttribute | Wanneer u een typedeclaratie plaatst, geeft u aan dat het type polymorf moet worden geserialiseerd. Er worden ook verschillende opties weergegeven voor het configureren van polymorfische serialisatie en deserialisatie voor dat type. |
Stel dat u een WeatherForecastBase
klasse en een afgeleide klasse WeatherForecastWithCity
hebt:
[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
Stel dat het typeargument van de Serialize<TValue>
methode tijdens het compileren het volgende is WeatherForecastBase
:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecastBase>(weatherForecastBase, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(WeatherForecastBase, options)
In dit scenario wordt de City
eigenschap geserialiseerd omdat het weatherForecastBase
object eigenlijk een WeatherForecastWithCity
object is. Deze configuratie maakt polymorfische serialisatie mogelijk voorWeatherForecastBase
, met name wanneer het runtimetype:WeatherForecastWithCity
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}
Terwijl round-tripping van de nettolading wordt WeatherForecastBase
ondersteund, wordt deze niet gerealiseerd als een runtimetype van WeatherForecastWithCity
. In plaats daarvan wordt het gerealiseerd als een runtimetype van 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
In de volgende sectie wordt beschreven hoe u metagegevens toevoegt om round-tripping van het afgeleide type mogelijk te maken.
Polymorfische typediscriminatoren
Als u polymorfische deserialisatie wilt inschakelen, moet u een typediscriminatie opgeven voor de afgeleide klasse:
[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
Met de toegevoegde metagegevens, met name het typediscriminator, kan de serialisatiefunctie de nettolading serialiseren en deserialiseren als het type van het WeatherForecastWithCity
basistype WeatherForecastBase
. Serialisatie verzendt JSON samen met de typediscriminatiemetagegevens:
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"
' }
Met het typediscriminator kan de serializer deserialiseren van de nettolading polymorf als WeatherForecastWithCity
:
WeatherForecastBase value = JsonSerializer.Deserialize<WeatherForecastBase>(json);
Console.WriteLine(value is WeatherForecastWithCity); // True
Dim value As WeatherForecastBase = JsonSerializer.Deserialize(json)
Console.WriteLine(value is WeatherForecastWithCity) // True
Notitie
Standaard moet de $type
discriminator aan het begin van het JSON-object worden geplaatst, gegroepeerd samen met andere metagegevenseigenschappen, zoals $id
en $ref
. Als u gegevens leest van een externe API die de $type
discriminator in het midden van het JSON-object plaatst, stelt u JsonSerializerOptions.AllowOutOfOrderMetadataProperties true
het volgende in:
JsonSerializerOptions options = new() { AllowOutOfOrderMetadataProperties = true };
JsonSerializer.Deserialize<Base>("""{"Name":"Name","$type":"derived"}""", options);
Wees voorzichtig wanneer u deze vlag inschakelt, omdat dit kan leiden tot overbuffering (en onvoldoende geheugenfouten) bij het uitvoeren van streamingdeserialisatie van zeer grote JSON-objecten.
Onderscheidsindelingen voor combinatie- en overeenkomsttypen
Type discriminator-id's zijn geldig in een string
van beide of int
formulieren, dus het volgende is geldig:
[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...
' }
Hoewel de API ondersteuning biedt voor het combineren en vergelijken van typediscriminatorconfiguraties, wordt dit niet aanbevolen. De algemene aanbeveling is om alle string
typediscriminaties, alle int
typediscriminaties of helemaal geen discriminators te gebruiken. In het volgende voorbeeld ziet u hoe u onderscheidsconfiguraties voor typen combineert en matcht:
[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
In het voorgaande voorbeeld heeft het BasePoint
type geen typediscriminator, terwijl het ThreeDimensionalPoint
type een int
typediscriminator heeft en het FourDimensionalPoint
type een string
typediscriminator heeft.
Belangrijk
Polymorfische serialisatie werkt alleen als het type geserialiseerde waarde dat van het polymorfe basistype is. Dit omvat het gebruik van het basistype als de algemene typeparameter bij het serialiseren van waarden op basisniveau, als het gedeclareerde type geserialiseerde eigenschappen of als het verzamelingselement in geserialiseerde verzamelingen.
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
De naam van het type discriminator aanpassen
De standaardeigenschapsnaam voor het type discriminator is $type
. Als u de naam van de eigenschap wilt aanpassen, gebruikt u de JsonPolymorphicAttribute naam zoals wordt weergegeven in het volgende voorbeeld:
[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
In de voorgaande code configureert het JsonPolymorphic
kenmerk de TypeDiscriminatorPropertyName
"$discriminator"
waarde. Wanneer de typediscriminatienaam is geconfigureerd, wordt in het volgende voorbeeld het ThreeDimensionalPoint
type geserialiseerd als JSON weergegeven:
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
Vermijd het gebruik van een JsonPolymorphicAttribute.TypeDiscriminatorPropertyName eigenschap die conflicteert met een eigenschap in uw typehiërarchie.
Onbekende afgeleide typen verwerken
Als u onbekende afgeleide typen wilt verwerken, moet u zich aanmelden voor dergelijke ondersteuning met behulp van een aantekening op het basistype. Houd rekening met de volgende typehiërarchie:
[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
Omdat de configuratie niet expliciet opt-in-ondersteuning voor FourDimensionalPoint
, pogingen om exemplaren FourDimensionalPoint
van als BasePoint
te serialiseren resulteert in een runtime-uitzondering:
JsonSerializer.Serialize<BasePoint>(new FourDimensionalPoint()); // throws NotSupportedException
JsonSerializer.Serialize(Of BasePoint)(New FourDimensionalPoint()) ' throws NotSupportedException
U kunt het standaardgedrag wijzigen met behulp van de JsonUnknownDerivedTypeHandling enum, die als volgt kan worden opgegeven:
[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
In plaats van terug te vallen op het basistype, kunt u de FallBackToNearestAncestor
instelling gebruiken om terug te vallen op het contract van het dichtstbijzijnde gedeclareerde afgeleide type:
[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
Met een configuratie zoals in het voorgaande voorbeeld wordt het ThreeDimensionalPoint
type geserialiseerd als BasePoint
:
// Serializes using the contract for BasePoint
JsonSerializer.Serialize<IPoint>(new ThreeDimensionalPoint());
' Serializes using the contract for BasePoint
JsonSerializer.Serialize(Of IPoint)(New ThreeDimensionalPoint())
Als u echter terugvalt op de dichtstbijzijnde voorouder, geeft u de mogelijkheid van dubbelzinnigheid "diamant" toe. Bekijk de volgende typehiërarchie als voorbeeld:
[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
In dit geval kan het BasePointWithTimeSeries
type worden geserialiseerd als een van BasePoint
beide of IPointWithTimeSeries
omdat ze beide directe voorouders zijn. Deze dubbelzinnigheid zorgt ervoor dat de NotSupportedException fout wordt gegenereerd bij het serialiseren van een exemplaar als BasePointWithTimeSeries
IPoint
.
// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());
' throws NotSupportedException
JsonSerializer.Serialize(Of IPoint)(New BasePointWithTimeSeries())
Polymorfisme configureren met het contractmodel
Voor gebruiksvoorbeelden waarbij kenmerkaantekeningen onpraktisch of onmogelijk zijn (zoals grote domeinmodellen, cross-assemblyhiërarchieën of hiërarchieën in afhankelijkheden van derden), gebruikt u het contractmodel om polymorfisme te configureren. Het contractmodel is een set API's die kunnen worden gebruikt om polymorfisme in een typehiërarchie te configureren door een aangepaste DefaultJsonTypeInfoResolver subklasse te maken die dynamisch polymorf configuratie per type biedt, zoals wordt weergegeven in het volgende voorbeeld:
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
Aanvullende polymorfe serialisatiedetails
- Polymorfische serialisatie ondersteunt afgeleide typen die expliciet zijn aangemeld via de JsonDerivedTypeAttribute. Niet-declaratieve typen resulteren in een runtime-uitzondering. Het gedrag kan worden gewijzigd door de JsonPolymorphicAttribute.UnknownDerivedTypeHandling eigenschap te configureren.
- Polymorfe configuratie die is opgegeven in afgeleide typen wordt niet overgenomen door polymorfe configuratie in basistypen. Het basistype moet onafhankelijk worden geconfigureerd.
- Polymorfische hiërarchieën worden ondersteund voor beide
interface
enclass
typen. - Polymorfisme met behulp van typediscriminatoren wordt alleen ondersteund voor typehiërarchieën die gebruikmaken van de standaardconversieprogramma's voor objecten, verzamelingen en woordenlijsttypen.
- Polymorfisme wordt ondersteund bij het genereren van bron op basis van metagegevens, maar niet bij het genereren van snel padbronnen.