Megosztás a következőn keresztül:


Származtatott osztályok tulajdonságainak szerializálása System.Text.Json

Ebből a cikkből megtudhatja, hogyan szerializálhatja a származtatott osztályok tulajdonságait a System.Text.Json névtérrel.

Származtatott osztályok tulajdonságainak szerializálása

A .NET 7 System.Text.Json előtti verziók nem támogatják a polimorf típusú hierarchiák szerializálását. Ha például egy tulajdonság típusa interfész vagy absztrakt osztály, akkor a rendszer csak a felületen vagy absztrakt osztályban meghatározott tulajdonságokat szerializálja, még akkor is, ha a futtatókörnyezet típusa további tulajdonságokkal rendelkezik. A viselkedésre vonatkozó kivételeket ebben a szakaszban ismertetjük. A .NET 7 támogatásáról további információt a .NET 7 polimorf szerializációjában talál.

Tegyük fel például, hogy van egy WeatherForecast osztálya és egy származtatott osztálya 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

Tegyük fel, hogy a metódus típusargumentuma fordításkor Serialize a következő 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)

Ebben az esetben a WindSpeed tulajdonság akkor sem szerializálódik, ha az weatherForecast objektum objektum WeatherForecastDerived . Csak az alaposztály tulajdonságai vannak szerializálva:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot"
}

Ez a viselkedés segít megelőzni az adatok véletlen expozícióját egy származtatott futtatókörnyezet által létrehozott típusban.

Az előző példában a származtatott típus tulajdonságainak szerializálásához használja az alábbi módszerek egyikét:

  • Adjon meg egy túlterhelést Serialize , amely lehetővé teszi a típus futásidőben történő megadását:

    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)
    
  • Deklarálja a szerializálandó objektumot 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)
    

Az előző példaforgatókönyvben mindkét módszer miatt a WindSpeed tulajdonság szerepel a JSON-kimenetben:

{
  "WindSpeed": 35,
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot"
}

Fontos

Ezek a megközelítések csak a gyökérobjektum szerializálásához biztosítják a polimorf szerializálást, a gyökérobjektum tulajdonságaihoz nem.

Az alacsonyabb szintű objektumok polimorf szerializálását is lekérheti, ha típusként objectdefiniálja őket. Tegyük fel például, hogy az WeatherForecast osztálynak van egy olyan tulajdonsága, PreviousForecast amely típusként WeatherForecast vagy 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

Ha a PreviousForecast tulajdonság a következő példányt WeatherForecastDerivedtartalmazza:

  • A szerializálásból WeatherForecastWithPrevious származó JSON-kimenet nem tartalmazza a következőtWindSpeed: .
  • A szerializálásból WeatherForecastWithPreviousAsObject származó JSON-kimenet tartalmazza a következőtWindSpeed: .

A szerializáláshoz WeatherForecastWithPreviousAsObjectnem szükséges meghívni Serialize<object> , vagy GetType mert nem a gyökérobjektum lehet származtatott típusú. A következő példakód nem hívható meg Serialize<object> vagy GetType:

options = new JsonSerializerOptions
{
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject, options);
options = New JsonSerializerOptions With {
    .WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject1, options)

Az előző kód helyesen szerializálja a következőt 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"
  }
}

A tulajdonságok definiálásának ugyanaz a megközelítése, mint az object interfészekkel. Tegyük fel, hogy a következő felülettel és implementációval rendelkezik, és implementációs példányokat tartalmazó tulajdonságokkal szeretne szerializálni egy osztályt:

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

Amikor szerializál egy példányt Forecasts, csak Tuesday a WindSpeed tulajdonság jelenik meg, mert Tuesday a következőként objectvan definiálva:

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)

Az alábbi példa az előző kódból származó JSON-t mutatja be:

{
  "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
  }
}

Feljegyzés

Ez a cikk szerializálásról, nem deszerializálásról szól. A .NET 7 előtti verziók nem támogatják a polimorf deszerializálást, de kerülő megoldásként egyéni konvertert írhat, például a Polimorf deszerializálás támogatása című példában. Ha többet szeretne tudni arról, hogy a .NET 7 hogyan támogatja a polimorf szerializálást és a deszerializálást, olvassa el a Származtatott osztályok System.Text.Json tulajdonságainak szerializálása a .NET 7-ben című témakört.

A .NET 7-től System.Text.Json kezdve támogatja a polimorf típusú hierarchia szerializálását és deszerializálását attribútumjegyzetekkel.

Attribútum Leírás
JsonDerivedTypeAttribute Típusdeklarációra helyezve azt jelzi, hogy a megadott altípust polimorf szerializálásra kell alkalmazni. Emellett lehetővé teszi a típuskriminatív megadását is.
JsonPolymorphicAttribute Típusdeklarációra helyezve azt jelzi, hogy a típust polimorfikusan kell szerializálni. Emellett különböző lehetőségeket is kínál a polimorf szerializálás és a deszerializálás konfigurálásához az adott típushoz.

Tegyük fel például, hogy van egy WeatherForecastBase osztálya és egy származtatott osztálya 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

Tegyük fel, hogy a metódus típusargumentuma fordításkor Serialize<TValue> a következő WeatherForecastBase:

options = new JsonSerializerOptions
{
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecastBase>(weatherForecastBase, options);
options = New JsonSerializerOptions With {
    .WriteIndented = True
}
jsonString = JsonSerializer.Serialize(WeatherForecastBase, options)

Ebben a forgatókönyvben a City tulajdonság szerializálva van, mert az weatherForecastBase objektum valójában egy WeatherForecastWithCity objektum. Ez a konfiguráció lehetővé teszi a polimorf szerializálástWeatherForecastBase, különösen akkor, ha a futtatókörnyezet típusa:WeatherForecastWithCity

{
  "City": "Milwaukee",
  "Date": "2022-09-26T00:00:00-05:00",
  "TemperatureCelsius": 15,
  "Summary": "Cool"
}

Bár a hasznos adat WeatherForecastBase ciklikus bemásolása a támogatott módon történik, nem fog futásidejű WeatherForecastWithCitytípusként megvalósulni. Ehelyett a következő futásidejű WeatherForecastBasetípusként fog megvalósulni:

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

Az alábbi szakasz azt ismerteti, hogyan adhat hozzá metaadatokat a származtatott típus ciklikus lehatolásának engedélyezéséhez.

Polimorf típusú diszkriminatívok

A polimorf deszerializálás engedélyezéséhez meg kell adnia egy típuskriminálót a származtatott osztályhoz:

[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

A hozzáadott metaadatokkal, pontosabban a típuskriminálóval a szerializáló szerializálhatja és deszerializálhatja a hasznos adatokat az WeatherForecastWithCity alaptípus WeatherForecastBasetípusaként. A szerializálás JSON-t bocsát ki a típuskriminatív metaadatokkal együtt:

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"
'   }

A típus diszkriminatív használatával a szerializáló a hasznos adatokat polimorfikusan deszerializálhatja a következő módon 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

Feljegyzés

Alapértelmezés szerint a megkülönböztetést $type a JSON-objektum elején kell elhelyezni, más metaadat-tulajdonságokkal( például $id és $ref. Ha adatokat olvas egy külső API-ból, amely a megkülönböztetést $type a JSON-objektum közepén helyezi el, állítsa a JsonSerializerOptions.AllowOutOfOrderMetadataProperties következőre true:

JsonSerializerOptions options = new() { AllowOutOfOrderMetadataProperties = true };
JsonSerializer.Deserialize<Base>("""{"Name":"Name","$type":"derived"}""", options);

Legyen óvatos, amikor engedélyezi ezt a jelzőt, mert ez túlpuffereltséget (és memóriakimaradást) okozhat a nagyon nagy JSON-objektumok streamelt deszerializálása során.

Típuskriminatív formátumok keverése és egyeztetése

A típuskriminatív azonosítók vagy string int űrlapok érvényesek, ezért a következők érvényesek:

[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...
'  }

Bár az API támogatja a keverő és a típuskriminatív konfigurációk egyeztetését, nem ajánlott. Az általános ajánlás az, hogy vagy minden string típus-diszkriminatívt, minden int típus-diszkriminatívt, vagy egyáltalán ne használjon diszkriminatívokat. Az alábbi példa a típuskriminatív konfigurációk keverését és egyezését mutatja be:

[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

Az előző példában a BasePoint típus nem rendelkezik típuskriminálóval, míg a ThreeDimensionalPoint típus int típuskriminatív, a FourDimensionalPoint string típus diszkriminatív.

Fontos

A polimorf szerializálás működéséhez a szerializált érték típusának a polimorf alaptípusnak kell lennie. Ez magában foglalja az alaptípus általános típusparaméterként való használatát a gyökérszintű értékek szerializálásakor, a szerializált tulajdonságok deklarált típusaként vagy a szerializált gyűjtemények gyűjteményelemeként.

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

A típus diszkriminatív nevének testreszabása

A típuskriminatív alapértelmezett tulajdonságneve a következő $type: . A tulajdonság nevének testreszabásához használja az JsonPolymorphicAttribute alábbi példában látható módon:

[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

Az előző kódban az JsonPolymorphic attribútum konfigurálja az TypeDiscriminatorPropertyName "$discriminator" értéket. A típuskriminatív név konfigurálva van, az alábbi példa a ThreeDimensionalPoint JSON-ként szerializált típust mutatja be:

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.

Ne használjon JsonPolymorphicAttribute.TypeDiscriminatorPropertyName olyan tulajdonságot, amely ütközik a típushierarchiában lévő tulajdonsággal.

Ismeretlen származtatott típusok kezelése

Az ismeretlen származtatott típusok kezeléséhez az alaptípuson lévő jegyzetekkel kell engedélyeznie az ilyen támogatást. Vegye figyelembe a következő típushierarchiát:

[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

Mivel a konfiguráció nem támogatja FourDimensionalPointexplicit módon, a példányok szerializálásának FourDimensionalPoint BasePoint megkísérlése futásidejű kivételt eredményez:

JsonSerializer.Serialize<BasePoint>(new FourDimensionalPoint()); // throws NotSupportedException
JsonSerializer.Serialize(Of BasePoint)(New FourDimensionalPoint()) ' throws NotSupportedException

Az alapértelmezett viselkedést az JsonUnknownDerivedTypeHandling alábbi enumerálással módosíthatja:

[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

Ahelyett, hogy visszaesik az alaptípusra, a FallBackToNearestAncestor beállítással visszaeshet a legközelebbi deklarált származtatott típus szerződésére:

[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

Az előző példához hasonló konfigurációval a ThreeDimensionalPoint típus szerializálva BasePointlesz:

// Serializes using the contract for BasePoint
JsonSerializer.Serialize<IPoint>(new ThreeDimensionalPoint());
' Serializes using the contract for BasePoint
JsonSerializer.Serialize(Of IPoint)(New ThreeDimensionalPoint())

Azonban a legközelebbi ősre visszatérve elismeri a "gyémánt" kétértelműség lehetőségét. Vegyük példaként a következő típushierarchiát:

[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

Ebben az esetben a BasePointWithTimeSeries típus szerializálható, BasePoint vagy IPointWithTimeSeries mivel mindkettő közvetlen előd. Ez a kétértelműség azt eredményezi, hogy a rendszer eldobja a NotSupportedException példány BasePointWithTimeSeries IPointas .

// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());
' throws NotSupportedException
JsonSerializer.Serialize(Of IPoint)(New BasePointWithTimeSeries())

Polimorfizmus konfigurálása a szerződésmodellel

Olyan esetekben, amikor az attribútumjegyzetek nem praktikusak vagy lehetetlenek (például nagy tartománymodellek, szerelvényközi hierarchiák vagy harmadik féltől származó függőségek hierarchiái), a szerződésmodell használatával konfigurálhatja a polimorfizmust. A szerződésmodell olyan API-k készlete, amelyek a típushierarchiák polimorfizmusának konfigurálására használhatók egy egyéni DefaultJsonTypeInfoResolver alosztály létrehozásával, amely típusonként dinamikusan biztosít polimorfikus konfigurációt, ahogyan az alábbi példában látható:

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

További polimorf szerializálási részletek

  • A polimorf szerializálás támogatja azokat a származtatott típusokat, amelyek kifejezetten a JsonDerivedTypeAttribute. A be nem jelentett típusok futásidejű kivételt eredményeznek. A viselkedés a tulajdonság konfigurálásával JsonPolymorphicAttribute.UnknownDerivedTypeHandling módosítható.
  • A származtatott típusokban megadott polimorfikus konfigurációt nem örökli az alaptípusok polimorf konfigurációja. Az alaptípust egymástól függetlenül kell konfigurálni.
  • A polimorf hierarchiák mindkét interface típus class esetében támogatottak.
  • A típuskriminálókat használó polimorfizmus csak olyan típushierarchiák esetében támogatott, amelyek az objektumok, gyűjtemények és szótártípusok alapértelmezett konvertereit használják.
  • A polimorfizmus a metaadatokon alapuló forráslétrehozásban támogatott, a gyorsútvonalú forráslétrehozásban azonban nem.

Lásd még