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
클래스에 WeatherForecast
또는 object
형식으로 정의할 수 있는 PreviousForecast
라는 속성이 있다고 가정하겠습니다.
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
가 object
로 정의되어 있으므로 Tuesday
만 WindSpeed
속성을 표시합니다.
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
은 특성 주석을 사용하여 다형 형식 계층 구조 직렬화 및 역직렬화를 지원합니다.
attribute | 설명 |
---|---|
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)
이 시나리오에서는 City
속성이 직렬화됩니다. weatherForecastBase
개체가 실제로 WeatherForecastWithCity
개체이기 때문입니다. 이 구성은 특히 런타임 형식이 WeatherForecastWithCity
인 경우 WeatherForecastBase
에 대해 다형 직렬화를 지원합니다.
{
"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
참고
형식 판별자는 JSON 개체의 시작 부분에 배치되어야 하며, $id
및 $ref
와 같은 다른 메타데이터 속성과 함께 그룹화되어야 합니다.
Dim value As WeatherForecastBase = JsonSerializer.Deserialize(json)
Console.WriteLine(value is WeatherForecastWithCity) // True
형식 판별자 형식 혼합 및 일치
형식 판별자 식별자는 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
특성은 "$discriminator"
값에 TypeDiscriminatorPropertyName
을 구성합니다. 형식 판별자 이름을 구성했으면 다음 예제에서는 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이 throw됩니다.
// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());
' throws NotSupportedException
JsonSerializer.Serialize(Of IPoint)(New BasePointWithTimeSeries())
계약 모델을 사용하여 다형성 구성
특성 주석이 비실용적이거나 불가능한 사용 사례(예: 대규모 도메인 모델, 어셈블리 간 계층 구조 또는 타사 종속성의 계층 구조)의 경우 다형성을 구성하려면 계약 모델을 사용합니다. 계약 모델은 다음 예제와 같이 형식별로 다형 구성을 동적으로 제공하는 사용자 지정 DefaultJsonTypeInfoResolver 하위 클래스를 만들어 형식 계층 구조에서 다형성을 구성하는 데 사용할 수 있는 API 집합입니다.
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
형식 모두에 대해 지원됩니다. - 형식 판별자를 사용하는 다형성은 개체, 컬렉션 및 사전 형식에 대한 기본 변환기를 사용하는 형식 계층 구조에서만 지원됩니다.
- 다형성은 메타데이터 기반 소스 생성에서 지원되지만 빠른 경로 소스 생성에서는 지원되지 않습니다.
참고 항목
.NET
피드백
https://aka.ms/ContentUserFeedback
출시 예정: 2024년 내내 콘텐츠에 대한 피드백 메커니즘으로 GitHub 문제를 단계적으로 폐지하고 이를 새로운 피드백 시스템으로 바꿀 예정입니다. 자세한 내용은 다음을 참조하세요.다음에 대한 사용자 의견 제출 및 보기