如何使用 System.Text.Json 序列化派生类的属性
本文介绍如何使用 System.Text.Json
命名空间序列化派生类的属性。
序列化派生类的属性
在 .NET 7 之前的版本中,System.Text.Json
不支持多态类型层次结构的序列化。 例如,如果属性的类型为接口或抽象类,那么即使运行时类型具有其他属性,也只会序列化对接口或抽象类定义的属性。 此部分中介绍了此行为的例外情况。 有关 .NET 7 中支持的信息,请参阅 .NET 7 中的多态序列化。
例如,假设有一个 WeatherForecast
类和一个派生类 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
并且假设 Serialize
方法的类型参数在编译时为 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)
在这种情况下,即使 weatherForecast
对象是 WeatherForecastDerived
对象,也不会序列化 WindSpeed
属性。 仅序列化基类属性:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
此行为旨在帮助防止在运行时创建的派生类型中发生意外数据泄露。
若要序列化前面示例中派生类型的属性,请使用以下方法之一:
调用 Serialize 的重载,以便在运行时指定类型:
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)
将要序列化的对象声明为
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)
在前面的示例方案中,这两种方法都会使 WindSpeed
属性包含在 JSON 输出中:
{
"WindSpeed": 35,
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
重要
这些方法只为要序列化的根对象提供多态序列化,而不为该根对象的属性提供。
如果将较低级别的对象定义为类型 object
,则可以对它们进行多态序列化。 例如,假设 WeatherForecast
类具有一个名为 PreviousForecast
的属性,该属性可以定义为类型 WeatherForecast
或 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
如果 PreviousForecast
属性包含 WeatherForecastDerived
的实例:
- 序列化
WeatherForecastWithPrevious
的 JSON 输出不包含WindSpeed
。 - 序列化
WeatherForecastWithPreviousAsObject
的 JSON 输出包含WindSpeed
。
若要序列化 WeatherForecastWithPreviousAsObject
,无需调用 Serialize<object>
或 GetType
,因为根对象不是可能属于派生类型的对象。 下面的代码示例不调用 Serialize<object>
或 GetType
:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject1, options)
前面的代码会正确地序列化 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"
}
}
将属性定义为 object
的相同方法适用于接口。 假设具有以下接口和实现,并且要使用包含实现实例的属性序列化类:
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
序列化 Forecasts
的实例时,只有 Tuesday
显示 WindSpeed
属性,因为 Tuesday
定义为 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)
下面的示例显示前面代码生成的 JSON:
{
"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
}
}
注意
本文介绍的是序列化,而不是反序列化。 .NET 7 之前的版本中不支持多态反序列化,解决方法是,你编写一个自定义转换器,例如支持多态反序列化中的示例。 若要详细了解 .NET 7 如何支持多态序列化和反序列化,请参阅如何在 .NET 7 中使用 System.Text.Json 序列化派生类的属性。
从 .NET 7 开始,System.Text.Json
支持使用属性注释的多态类型层次结构序列化和反序列化。
属性 | 描述 |
---|---|
JsonDerivedTypeAttribute | 当放置在类型声明中时,则指示应选择指定的子类型进行多态序列化。 它还公开用于指定类型鉴别器的功能。 |
JsonPolymorphicAttribute | 当放置在类型声明中时,则指示应以多态方式序列化该类型。 它还公开各种选项来配置该类型的多态序列化和反序列化。 |
例如,假设有一个 WeatherForecastBase
类和一个派生类 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
并且假设 Serialize<TValue>
方法的类型参数在编译时为 WeatherForecastBase
:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecastBase>(weatherForecastBase, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(WeatherForecastBase, options)
在这种情况下,因为 weatherForecastBase
对象实际上是 WeatherForecastWithCity
对象,所以会序列化 City
属性。 此配置启用 WeatherForecastBase
的多态序列化,尤其是当运行时类型为 WeatherForecastWithCity
时:
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}
虽然支持将有效负载作为 WeatherForecastBase
进行往返,但是它不会具体化为运行时类型 WeatherForecastWithCity
。 而是会具体化为运行时类型 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
以下部分介绍如何添加元数据以启用派生类型的往返。
多态类型鉴别器
若要启用多态反序列化,必须为派生类指定类型鉴别器:
[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
通过添加的元数据,特别是类型鉴别器,序列化程序可以将有效负载从其基类型 WeatherForecastBase
序列化和反序列化为 WeatherForecastWithCity
类型。 序列化会在发出类型鉴别器元数据时同时发出 JSON:
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"
' }
借助类型鉴别器,序列化程序可以将有效负载以多态方式反序列化为 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
注意
默认情况下,$type
鉴别器必须放在 JSON 对象的开头,与其他元数据属性(如 $id
和 $ref
)组合在一起。 如果要从外部 API 读取的数据在 JSON 对象中间放置了 $type
鉴别器,请将 JsonSerializerOptions.AllowOutOfOrderMetadataProperties 设置为 true
:
JsonSerializerOptions options = new() { AllowOutOfOrderMetadataProperties = true };
JsonSerializer.Deserialize<Base>("""{"Name":"Name","$type":"derived"}""", options);
启用此标志时要小心,因为在对超大 JSON 对象进行流反序列化时,它可能会导致缓冲过大(和内存不足故障)。
混合和匹配类型鉴别器格式
类型鉴别器标识符的有效形式为 string
或 int
,因此以下形式有效:
[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 支持混合和匹配类型鉴别器配置,但不建议这样做。 通常建议要么使用所有 string
类型鉴别器、要么使用所有 int
类型鉴别器,或者要么根本不使用鉴别器。 以下示例演示如何混合和匹配类型鉴别器配置:
[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
在前面的示例中,BasePoint
类型没有类型鉴别器,而 ThreeDimensionalPoint
类型有 int
类型鉴别器,FourDimensionalPoint
有 string
类型鉴别器。
重要
为了正常多态序列化,序列化后的值的类型须为多态基类型的类型。 这包括将基类型用作泛型类型参数(在序列化根级别值时)、已声明的序列化属性类型或经过序列化的集合中的集合元素。
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
自定义类型鉴别器名称
类型鉴别器的默认属性名称为 $type
。 若要自定义属性名称,请按下例中所示使用 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
在前面的代码中,JsonPolymorphic
属性将 TypeDiscriminatorPropertyName
配置为 "$discriminator"
值。 配置类型鉴别器名称后,以下示例显示序列化为 JSON 的 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 }
提示
避免使用与类型层次结构中的属性产生冲突的 JsonPolymorphicAttribute.TypeDiscriminatorPropertyName。
处理未知派生类型
若要处理未知派生类型,必须使用基类型上的注释来加入此类支持。 考虑以下类型层次结构:
[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
由于该配置未显式选择加入对 FourDimensionalPoint
的支持,如果尝试将 FourDimensionalPoint
的实例序列化为 BasePoint
会导致运行时异常:
JsonSerializer.Serialize<BasePoint>(new FourDimensionalPoint()); // throws NotSupportedException
JsonSerializer.Serialize(Of BasePoint)(New FourDimensionalPoint()) ' throws NotSupportedException
可以使用 JsonUnknownDerivedTypeHandling 枚举更改默认行为,可按以下方式指定:
[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
可以使用 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
使用前例中那样的配置后,ThreeDimensionalPoint
类型将序列化为 BasePoint
:
// Serializes using the contract for BasePoint
JsonSerializer.Serialize<IPoint>(new ThreeDimensionalPoint());
' Serializes using the contract for BasePoint
JsonSerializer.Serialize(Of IPoint)(New ThreeDimensionalPoint())
然而,回退到最近的上级即承认了“钻石”歧义的可能性。 作为例子,考虑以下类型层次结构:
[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
在此例中,BasePointWithTimeSeries
类型可序列化为 BasePoint
或 IPointWithTimeSeries
,因为它们都是直接上级。 当尝试将 BasePointWithTimeSeries
的实例序列化为 IPoint
时,此歧义将导致引发 NotSupportedException。
// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());
' throws NotSupportedException
JsonSerializer.Serialize(Of IPoint)(New BasePointWithTimeSeries())
使用协定模型配置多形性
对于属性注释不切实际或不可能的用例(例如大型域模型、跨程序集层次结构或第三方依赖项中的层次结构),可以使用协定模型配置多形性。 协定模型是一组 API,可用于通过创建自定义 DefaultJsonTypeInfoResolver 子类来配置类型层次结构中的多形性,该子类以动态的方式提供每个类型的多态配置,如下例所示:
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
其他多态序列化详细信息
- 多态序列化支持通过 JsonDerivedTypeAttribute 显式选择加入的派生类型。 未声明的类型将造成运行时异常。 可通过配置 JsonPolymorphicAttribute.UnknownDerivedTypeHandling 属性更改此行为。
- 派生类型中指定的多态配置不是由基类型中的多态配置继承的。 基类型必须独立配置。
interface
和class
类型都支持多态层次结构。- 使用类型鉴别器的多形性仅支持为对象、集合和字典类型使用默认转换器的类型层次结构。
- 基于元数据的源生成支持多形性,快速路径源生成但不支持。