Share via


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 WeatherForecastDerivedhebt:

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 bevat WindSpeedniet.
  • De JSON-uitvoer van serialiseren WeatherForecastWithPreviousAsObject omvat WindSpeed.

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 Forecastsserialiseert, 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 WeatherForecastWithCityhebt:

[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 truehet 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 en class 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.

Zie ook