ile türetilmiş sınıfların özelliklerini seri hale getirme System.Text.Json
Bu makalede, türetilmiş sınıfların özelliklerini ad alanıyla serileştirmeyi System.Text.Json
öğreneceksiniz.
Türetilmiş sınıfların özelliklerini seri hale getirme
.NET 7 ile başlayarak, System.Text.Json
öznitelik ek açıklamalarıyla çok biçimli tür hiyerarşi serileştirmesini ve seri durumdan kaldırmayı destekler.
Öznitelik | Açıklama |
---|---|
JsonDerivedTypeAttribute | Tür bildirimine yerleştirildiğinde, belirtilen alt türün polimorfik serileştirmeye kabul edilmesi gerektiğini belirtir. Ayrıca bir tür ayrıştırıcısı belirtme özelliğini de kullanıma sunar. |
JsonPolymorphicAttribute | Bir tür bildirimine yerleştirildiğinde, türün çok biçimli olarak serileştirilmesi gerektiğini gösterir. Ayrıca, bu tür için çok biçimli serileştirme ve seri durumdan çıkarma yapılandırmaya yönelik çeşitli seçenekleri de kullanıma sunar. |
Örneğin, bir WeatherForecastBase
sınıfınız ve türetilmiş bir sınıfınız WeatherForecastWithCity
olduğunu varsayalım:
[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
Ve derleme zamanında yönteminin tür bağımsız değişkeninin Serialize<TValue>
olduğunu WeatherForecastBase
varsayalım:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecastBase>(weatherForecastBase, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(WeatherForecastBase, options)
Bu senaryoda, City
nesne aslında bir WeatherForecastWithCity
nesne olduğundan weatherForecastBase
özelliği seri hale getirilir. Bu yapılandırma, özellikle çalışma zamanı türü olduğunda WeatherForecastWithCity
için WeatherForecastBase
çok biçimli serileştirmeyi etkinleştirir:
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}
Yükün desteklendiği gibi WeatherForecastBase
yuvarlanması destekleniyor olsa da, çalışma zamanı türü WeatherForecastWithCity
olarak gerçekleşmez. Bunun yerine, bir çalışma zamanı türü WeatherForecastBase
olarak gerçekleştirilmesi gerekir:
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
Aşağıdaki bölümde, türetilmiş türün yuvarlama özelliğini etkinleştirmek için meta verilerin nasıl ekleneceği açıklanmaktadır.
Polimorfik tip ayrımcıları
Polimorfik seri durumdan çıkarma özelliğini etkinleştirmek için türetilen sınıf için bir tür ayrıştırıcısı belirtmeniz gerekir:
[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
Eklenen meta verilerle, özellikle de tür ayrıştırıcısıyla, seri hale getirici yükü temel türünden WeatherForecastBase
tür olarak seri hale getirebilir ve seri durumdan WeatherForecastWithCity
çıkarabilir. Serileştirme, tür ayrıştırıcı meta verileriyle birlikte JSON yayar:
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"
' }
Tür ayrıştırıcısı ile seri hale getirici yükü polimorfik olarak WeatherForecastWithCity
seri durumdan çıkarabilir:
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
Not
Varsayılan olarak, $type
ayrımcı JSON nesnesinin başına yerleştirilmelidir ve $ref
gibi $id
diğer meta veri özellikleriyle birlikte gruplandırılmalıdır. Ayrıştırıcıyı JSON nesnesinin $type
ortasına yerleştiren bir dış API'deki verileri okuyorsanız olarak ayarlayın JsonSerializerOptions.AllowOutOfOrderMetadataProperties true
:
JsonSerializerOptions options = new() { AllowOutOfOrderMetadataProperties = true };
JsonSerializer.Deserialize<Base>("""{"Name":"Name","$type":"derived"}""", options);
Çok büyük JSON nesnelerinin seri durumdan çıkarma akışlarını gerçekleştirirken aşırı arabelleğe alma (ve bellek dışı hatalara) neden olabileceğinden bu bayrağı etkinleştirirken dikkatli olun.
Tür ayrıştırıcı biçimlerini karıştırma ve eşleştirme
Tür ayrıştırıcı tanımlayıcıları veya int
formlarında string
geçerlidir, bu nedenle aşağıdakiler geçerlidir:
[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...
' }
API, tür ayrıştırıcı yapılandırmalarını karıştırmayı ve eşleştirmeyi desteklese de önerilmez. Genel öneri, tüm string
tür ayrımcılarını, tüm int
tür ayrımcılarını veya hiç ayrımcı kullanmamaktır. Aşağıdaki örnek, tür ayrıştırıcı yapılandırmalarının nasıl karıştırılıp eşleştirilip eşleştirilip eşleştirileceğini gösterir:
[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
Yukarıdaki örnekte türün BasePoint
tür ayrıştırıcısı yoktur, türün ThreeDimensionalPoint
tür int
ayrıştırıcısı ve FourDimensionalPoint
türü ayrıştırıcısı vardır string
.
Önemli
Polimorfik serileştirmenin çalışması için seri hale getirilmiş değerin türü, polimorfik taban türüne ait olmalıdır. Buna kök düzeyi değerleri serileştirilirken genel tür parametresi olarak, bildirilen serileştirilmiş özelliklerin türü olarak veya serileştirilmiş koleksiyonlardaki koleksiyon öğesi olarak temel türün kullanılması dahildir.
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
Tür ayrıştırıcı adını özelleştirme
Tür ayrıştırıcısının varsayılan özellik adıdır $type
. Özellik adını özelleştirmek için aşağıdaki örnekte gösterildiği gibi öğesini kullanın JsonPolymorphicAttribute :
[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
Yukarıdaki kodda JsonPolymorphic
özniteliği değerini yapılandırıyor TypeDiscriminatorPropertyName
"$discriminator"
. Tür ayrıştırıcı adı yapılandırıldığında, aşağıdaki örnekte JSON olarak seri hale getirilmiş tür gösterilir ThreeDimensionalPoint
:
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 }
İpucu
Tür hiyerarşinizdeki bir JsonPolymorphicAttribute.TypeDiscriminatorPropertyName özellik ile çakişen bir kullanmaktan kaçının.
Bilinmeyen türetilmiş türleri işleme
Bilinmeyen türetilmiş türleri işlemek için temel türdeki bir ek açıklamayı kullanarak bu tür desteği kabul etmeniz gerekir. Aşağıdaki tür hiyerarşisini göz önünde bulundurun:
[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
Yapılandırma açıkça için FourDimensionalPoint
desteğini kabul etmediğinden, örneğini FourDimensionalPoint
BasePoint
seri hale getirme girişimi bir çalışma zamanı özel durumuyla sonuçlanır:
JsonSerializer.Serialize<BasePoint>(new FourDimensionalPoint()); // throws NotSupportedException
JsonSerializer.Serialize(Of BasePoint)(New FourDimensionalPoint()) ' throws NotSupportedException
Aşağıdaki gibi belirtilebilen sabit listesi kullanarak JsonUnknownDerivedTypeHandling varsayılan davranışı değiştirebilirsiniz:
[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
Temel türe geri dönmek yerine, en yakın bildirilen türetilmiş türün sözleşmesine geri dönmek için ayarını kullanabilirsiniz FallBackToNearestAncestor
:
[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
Yukarıdaki örneğe benzer bir yapılandırmayla ThreeDimensionalPoint
, tür olarak BasePoint
seri hale getirilir:
// Serializes using the contract for BasePoint
JsonSerializer.Serialize<IPoint>(new ThreeDimensionalPoint());
' Serializes using the contract for BasePoint
JsonSerializer.Serialize(Of IPoint)(New ThreeDimensionalPoint())
Ancak, en yakın üste geri düşmek "elmas" belirsizlik olasılığını kabul eder. Örnek olarak aşağıdaki tür hiyerarşisini göz önünde bulundurun:
[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
Bu durumda, BasePointWithTimeSeries
türü ya da BasePoint
IPointWithTimeSeries
her ikisi de doğrudan ataları olduğundan seri hale getirilebilir. Bu belirsizlik, örneğini NotSupportedException BasePointWithTimeSeries
olarak IPoint
seri hale getirme girişiminde bulunulmasına neden olur.
// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());
' throws NotSupportedException
JsonSerializer.Serialize(Of IPoint)(New BasePointWithTimeSeries())
Sözleşme modeliyle çok biçimliliği yapılandırma
Çok biçimliliği yapılandırmak için öznitelik ek açıklamalarının pratik olmadığı veya imkansız olduğu kullanım örnekleri (büyük etki alanı modelleri, çapraz derleme hiyerarşileri veya üçüncü taraf bağımlılıklarındaki hiyerarşiler gibi) için sözleşme modelini kullanın. Sözleşme modeli, aşağıdaki örnekte gösterildiği gibi tür başına dinamik olarak polimorfik yapılandırma sağlayan özel DefaultJsonTypeInfoResolver bir alt sınıf oluşturarak tür hiyerarşisinde çok biçimliliği yapılandırmak için kullanılabilecek bir API kümesidir:
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
Ek polimorfik serileştirme ayrıntıları
- Polimorfik serileştirme, aracılığıyla JsonDerivedTypeAttributeaçıkça kabul edilen türetilmiş türleri destekler. Bildirilmeyen türler bir çalışma zamanı özel durumuyla sonuçlanır. Özelliği yapılandırılarak JsonPolymorphicAttribute.UnknownDerivedTypeHandling davranış değiştirilebilir.
- Türetilmiş türlerde belirtilen polimorfik yapılandırma, temel türlerdeki polimorfik yapılandırma tarafından devralınmıyor. Temel türün bağımsız olarak yapılandırılması gerekir.
- Hem hem de
interface
class
türleri için polimorfik hiyerarşiler desteklenir. - Tür ayrımcılarını kullanan polimorfizm yalnızca nesneler, koleksiyonlar ve sözlük türleri için varsayılan dönüştürücüleri kullanan tür hiyerarşileri için desteklenir.
- Çok biçimlilik meta veri tabanlı kaynak oluşturmada desteklenir, ancak hızlı yol kaynak oluşturmada desteklenmez.