Sprache
Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel lernen Sie, wie Sie Eigenschaften von abgeleiteten Klassen mit dem System.Text.Json-Namespace serialisieren können.
Serialisieren von Eigenschaften abgeleiteter Klassen
Ab .NET 7 unterstützt System.Text.Json die Serialisierung und Deserialisierung der polymorphen Typhierarchie mit Attributanmerkungen.
| attribute | BESCHREIBUNG |
|---|---|
| JsonDerivedTypeAttribute | Gibt beim Platzieren in einer Typdeklaration an, dass der angegebene Untertyp für die polymorphe Serialisierung ausgewählt werden soll. Dadurch wird zugleich die Möglichkeit zur Angabe eines Typdiskriminators verfügbar gemacht. |
| JsonPolymorphicAttribute | Gibt bei Platzierung in einer Typdeklaration an, dass der Typ polymorph serialisiert werden soll. Außerdem werden verschiedene Optionen zum Konfigurieren der polymorphen Serialisierung und Deserialisierung für diesen Typ verfügbar gemacht. |
Nehmen Sie beispielsweise an, Sie besitzen eine WeatherForecastBase-Klasse und eine abgeleitete Klasse 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
Ferner sei angenommen, dass das Typargument der Serialize<TValue>-Methode zur Kompilierzeit WeatherForecastBase ist:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecastBase>(weatherForecastBase, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(WeatherForecastBase, options)
In diesem Szenario wird die City-Eigenschaft serialisiert, da es sich bei dem weatherForecastBase-Objekt tatsächlich um ein WeatherForecastWithCity-Objekt handelt. Diese Konfiguration ermöglicht die polymorphe Serialisierung für WeatherForecastBase, insbesondere wenn der Laufzeittyp WeatherForecastWithCity lautet:
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}
Während der Roundtrip der Nutzdaten als WeatherForecastBase unterstützt wird, materialisieren sie sich nicht als Laufzeittyp von WeatherForecastWithCity. Stattdessen wird es als ein Laufzeittyp von WeatherForecastBase erscheinen.
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
Im folgenden Abschnitt wird beschrieben, wie Sie Metadaten hinzufügen, um die Roundtripfähigkeit des abgeleiteten Typs zu ermöglichen.
Polymorphe Typdiskriminatoren
Zum Aktivieren der polymorphen Deserialisierung müssen Sie einen Typdiskriminator für die abgeleitete Klasse angeben:
[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
Mit den hinzugefügten Metadaten, insbesondere dem Typdiskriminator, kann der Serialisierer die Nutzdaten als Typ WeatherForecastWithCity von ihrem Basistyp WeatherForecastBase serialisieren und deserialisieren. Bei der Serialisierung wird JSON zusammen mit den Metadaten des Typdiskriminators ausgegeben:
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"
' }
Mit dem Typdiskriminator kann der Serialisierer die Nutzdaten polymorph als WeatherForecastWithCity deserialisieren:
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
Hinweis
Standardmäßig muss der $type-Diskriminator am Anfang des JSON-Objekts platziert werden, zusammen mit anderen Metadaten-Eigenschaften wie $id und $ref. Wenn Sie Daten aus einer externen API lesen, die den $type-Diskriminator in der Mitte des JSON-Objekts platziert, legen Sie JsonSerializerOptions.AllowOutOfOrderMetadataProperties auf true fest:
JsonSerializerOptions options = new() { AllowOutOfOrderMetadataProperties = true };
JsonSerializer.Deserialize<Base>("""{"Name":"Name","$type":"derived"}""", options);
Seien Sie vorsichtig, wenn Sie dieses Flag aktivieren, da es bei der Streaming-Deserialisierung von sehr großen JSON-Objekten zu Overbuffering (und Out-of-Memory-Fehlern) führen kann.
Gemischte Verwendung und Abgleich von Typdiskriminatorformaten
Bezeichner von Typdiskriminatoren sind sowohl in der Form string als auch als int gültig, daher ist Folgendes gültig:
[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...
' }
Die API unterstützt zwar das Mischen und Abgleichen von Typdiskriminatorkonfigurationen, dies wird jedoch nicht empfohlen. Die allgemeine Empfehlung lautet, entweder nur string-Typdiskriminatoren, nur int-Typdiskriminatoren oder überhaupt keine Diskriminatoren zu verwenden. Das folgende Beispiel zeigt, wie Typdiskriminator-Konfigurationen kombiniert werden können:
[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
Im vorherigen Beispiel verfügt der Typ BasePoint nicht über einen Typdiskriminator, während der Typ ThreeDimensionalPoint einen int-Typdiskriminator aufweist und der FourDimensionalPoint einen string-Typdiskriminator besitzt.
Wichtig
Damit die polymorphe Serialisierung funktioniert, sollte der Typ des serialisierten Werts dem des polymorphen Basistyps entsprechen. Dazu gehört die Verwendung des Basistyps beim Serialisieren von Werten auf Stammebene als generischer Typparameter, als deklarierter Typ serialisierter Eigenschaften oder als Auflistungselement in serialisierten Auflistungen.
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
Den Namen des Typdiskriminators anpassen
Der Standardeigenschaftsname für den Typdiskriminator ist $type. Verwenden Sie zum Anpassen des Eigenschaftsnamens das JsonPolymorphicAttribute, wie im folgenden Beispiel gezeigt:
[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
Im vorstehenden Code konfiguriert das JsonPolymorphic-Attribut den TypeDiscriminatorPropertyName mit dem Wert "$discriminator". Bei konfiguriertem Typdiskriminatornamen zeigt das folgende Beispiel den ThreeDimensionalPoint-Typ in JSON serialisiert:
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 }
Tipp
Vermeiden Sie die Verwendung eines JsonPolymorphicAttribute.TypeDiscriminatorPropertyName, wenn dies zu einem Konflikt mit einer Eigenschaft in Ihrer Typhierarchie führt.
Unbekannte abgeleitete Typen verarbeiten
Zur Behandlung unbekannter abgeleiteter Typen müssen Sie sich für eine entsprechende Unterstützung mithilfe einer Anmerkung für den Basistyp entscheiden. Sehen Sie sich die folgende Typhierarchie an:
[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
Da die Konfiguration die Unterstützung für FourDimensionalPoint nicht explizit aktiviert, führt der Versuch, Instanzen von FourDimensionalPoint als BasePoint zu serialisieren, zu einer Laufzeitausnahme:
JsonSerializer.Serialize<BasePoint>(new FourDimensionalPoint()); // throws NotSupportedException
JsonSerializer.Serialize(Of BasePoint)(New FourDimensionalPoint()) ' throws NotSupportedException
Sie können das Standardverhalten mithilfe der JsonUnknownDerivedTypeHandling-Enumeration ändern, die wie folgt angegeben werden kann:
[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
Anstatt auf den Basistyp zurückzugreifen, können Sie die FallBackToNearestAncestor-Einstellung verwenden, um auf den Vertrag des nächstgelegenen deklarierten abgeleiteten Typs zurückzugreifen:
[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
Bei einer Konfiguration wie im vorherigen Beispiel wird der ThreeDimensionalPoint-Typ als BasePointserialisiert:
// Serializes using the contract for BasePoint
JsonSerializer.Serialize<IPoint>(new ThreeDimensionalPoint());
' Serializes using the contract for BasePoint
JsonSerializer.Serialize(Of IPoint)(New ThreeDimensionalPoint())
Der Rückgriff auf den nächsten Vorgänger lässt jedoch die Möglichkeit einer „Rauten-Mehrdeutigkeit“ (diamond ambiguity) zu. Betrachten Sie die folgende Typhierarchie als Beispiel:
[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 diesem Fall kann der Typ BasePointWithTimeSeries entweder als BasePoint oder als IPointWithTimeSeries serialisiert werden, da beide direkte Vorgänger sind. Diese Mehrdeutigkeit führt dazu, dass beim Versuch, eine Instanz von BasePointWithTimeSeries als IPoint zu serialisieren, die NotSupportedException ausgelöst wird.
// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());
' throws NotSupportedException
JsonSerializer.Serialize(Of IPoint)(New BasePointWithTimeSeries())
Ableiten von Polymorphismus aus einer geschlossenen Hierarchie
Ab .NET 11 System.Text.Json können abgeleitete Typen aus einer geschlossenen C#15-Hierarchie abgeleitet werden. Inferenz für eine Hierarchie über JsonPolymorphicAttribute.InferClosedTypePolymorphism aktivieren:
[JsonPolymorphic(InferClosedTypePolymorphism = true)]
public closed class Shape { }
public sealed class Circle : Shape { }
public sealed class Square : Shape { }
Der Serialisierer registriert jeden abgeleiteten Typ und verwendet seinen einfachen Typnamen als Zeichenfolgendiskriminator. Zum Beispiel enthält eine "$type":"Circle" Payload Circle.
Um die Schlussfolgerung für jede geschlossene Hierarchie zu aktivieren, die eine Optionsinstanz behandelt, legen Sie Folgendes fest JsonSerializerOptions.InferClosedTypePolymorphism:
var options = new JsonSerializerOptions
{
InferClosedTypePolymorphism = true
};
Legen Sie für die Quellgenerierung Folgendes fest JsonSourceGenerationOptionsAttribute.InferClosedTypePolymorphism:
[JsonSourceGenerationOptions(InferClosedTypePolymorphism = true)]
[JsonSerializable(typeof(Shape))]
internal partial class AppJsonContext : JsonSerializerContext;
Legen Sie die Ableitung für das Attribut der Quellgenerierung fest, wenn Sie einen generierten Kontext verwenden. Das Aktivieren der Laufzeiteigenschaft JsonSerializerOptions kann keine abgeleiteten Metadaten hinzufügen, die der Quellgenerator nicht ausgegeben hat.
Das Attribut auf Typebene hat Vorrang vor der globalen Einstellung. Setzen Sie InferClosedTypePolymorphism = false auf JsonPolymorphicAttribute, um eine Hierarchie von einer globalen Einstellung auszunehmen.
Explizite JsonDerivedTypeAttribute Registrierungen ersetzen die Ableitung für diese Hierarchie. Sie erweitern die abgeleitete Liste nicht. Das Anwenden von InferClosedTypePolymorphism = true auf einen Typ, der nicht closed ist, löst bei reflexionsbasierter Serialisierung eine InvalidOperationException aus und führt zu einem Fehler bei der Quellgenerierung. Abgeleitete Typen mit doppelten einfachen Namen erzeugen Diskriminatorkonflikte, und jeder abgeleitete Typ muss mindestens so zugänglich sein wie die geschlossene Basis.
Konfigurieren von geöffneten generischen abgeleiteten Typen
Ab .NET 11 akzeptiert JsonDerivedTypeAttribute einen offenen generischen abgeleiteten Typ, wenn der Serializer einen eindeutigen geschlossenen Typ aus dem serialisierten Basistyp auflösen kann:
[JsonDerivedType(typeof(Derived<>), "derived")]
public class Base<T>;
public class Derived<T> : Base<T>;
<JsonDerivedType(GetType(Derived(Of )), "derived")>
Public Class Base(Of T)
End Class
Public Class Derived(Of T)
Inherits Base(Of T)
End Class
Für Base<int> registriert der Serialisierer Derived<int>. Die gleiche Auflösung unterstützt generische Schnittstellen, neu angeordnete Typparameter, geschachtelte generische Argumente, Arrays und abgeleitete Typen, die einige Basistypargumente auf konkrete Typen korrigieren.
Jeder abgeleitete Typparameter muss vom geschlossenen Basistyp abgeleitet sein, die Ersetzung muss eindeutig sein, und der resultierende Typ muss seine generischen Einschränkungen erfüllen. Die reflexionsbasierte Serialisierung löst bei einer nicht unterstützten Spezialisierung InvalidOperationException aus. Die Quellgenerierung meldet SYSLIB1229, und die generierte Hierarchie schlägt weiterhin fehl, wenn der Serializer sie konfiguriert. Durch das Unterdrücken der Warnung wird die Registrierung nicht gültig.
Konfigurieren von Polymorphismus mit dem Vertragsmodell
Für Anwendungsfälle, in denen Attributanmerkungen nicht praktikabel oder unmöglich sind (z. B. in großen Domänenmodellen, assemblyübergreifenden Hierarchien oder Hierarchien in Abhängigkeiten von Drittanbietern), verwenden Sie zum Konfigurieren von Polymorphismus das Vertragsmodell. Das Vertragsmodell ist eine Sammlung von APIs, die zum Konfigurieren von Polymorphismus in einer Typhierarchie verwendet werden können, indem eine benutzerdefinierte Unterklasse DefaultJsonTypeInfoResolver erstellt wird, die dynamisch eine polymorphe Konfiguration pro Typ bereitstellt, wie im folgenden Beispiel gezeigt:
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
Zusätzliche Details zur polymorphen Serialisierung
- Die polymorphe Serialisierung unterstützt abgeleitete Typen, die explizit über das JsonDerivedTypeAttribute aktiviert wurden. Nicht deklarierte Typen führen zu einer Laufzeit-Ausnahme. Sie können dieses Verhalten ändern, indem Sie die JsonPolymorphicAttribute.UnknownDerivedTypeHandling-Eigenschaft festlegen.
- Eine polymorphe Konfiguration, die in abgeleiteten Typen angegeben ist, wird nicht durch die polymorphe Konfiguration in Basistypen geerbt. Der Basistyp muss unabhängig konfiguriert werden.
- Polymorphe Hierarchien werden sowohl für
interface- als auch fürclass-Typen unterstützt. - Polymorphismus mithilfe von Typdiskriminatoren wird nur für Typhierarchien unterstützt, die die Standardkonverter für Objekte, Sammlungen und Wörterbuchtypen verwenden.
- Polymorphismus wird in der metadatenbasierten Quellgenerierung unterstützt, jedoch nicht in der Fast-Path-Quellgenerierung.