변경할 수 없는 형식 및 속성 사용

변경할 수 없는 형식은 인스턴스화된 후 개체의 속성 또는 필드 값을 변경하는 것을 방지하는 형식입니다. 형식은 레코드이거나, 공용 속성 또는 필드가 없거나, 읽기 전용 속성이 있거나, private 또는 init 전용 setter가 있는 속성이 있을 수 있습니다. System.String(은)는 변경할 수 없는 형식의 예입니다. System.Text.Json에서는 JSON을 변경할 수 없는 형식으로 역직렬화할 수 있는 다양한 방법을 제공합니다.

매개 변수 있는 생성자

기본적으로 System.Text.Json(은)는 기본 공용 매개 변수가 없는 생성자를 사용합니다. 그러나 매개 변수가 있는 생성자를 사용하도록 지시할 수 있습니다. 이 생성자를 사용하면 변경 불가능한 클래스 또는 구조체를 역직렬화할 수 있습니다.

  • 클래스의 경우 유일한 생성자가 매개 변수가 있는 생성자이면 해당 생성자가 사용됩니다.

  • 구조체 또는 여러 생성자가 포함된 클래스의 경우 JsonConstructor 특성을 적용하여 사용할 생성자를 지정합니다. 특성을 사용하지 않는 경우 매개 변수가 없는 public 생성자가 있으면 항상 사용됩니다.

    다음 예제는 [JsonConstructor] 특성을 사용합니다.

    using System.Text.Json;
    using System.Text.Json.Serialization;
    
    namespace ImmutableTypes
    {
        public struct Forecast
        {
            public DateTime Date { get; }
            public int TemperatureC { get; }
            public string Summary { get; }
     
            [JsonConstructor]
            public Forecast(DateTime date, int temperatureC, string summary) =>
                (Date, TemperatureC, Summary) = (date, temperatureC, summary);
        }
    
        public class Program
        {
            public static void Main()
            {
                string json = """
                    {
                        "date":"2020-09-06T11:31:01.923395-07:00",
                        "temperatureC":-1,
                        "summary":"Cold"
                    }
                    """;
                Console.WriteLine($"Input JSON: {json}");
    
                var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
    
                Forecast forecast = JsonSerializer.Deserialize<Forecast>(json, options);
    
                Console.WriteLine($"forecast.Date: {forecast.Date}");
                Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}");
                Console.WriteLine($"forecast.Summary: {forecast.Summary}");
    
                string roundTrippedJson =
                    JsonSerializer.Serialize<Forecast>(forecast, options);
    
                Console.WriteLine($"Output JSON: {roundTrippedJson}");
            }
        }
    }
    
    // Produces output like the following example:
    //
    //Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}
    //forecast.Date: 9 / 6 / 2020 11:31:01 AM
    //forecast.TemperatureC: -1
    //forecast.Summary: Cold
    //Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}
    
    Imports System.Text.Json
    Imports System.Text.Json.Serialization
    
    Namespace ImmutableTypes
    
        Public Structure Forecast
            Public ReadOnly Property [Date] As Date
            Public ReadOnly Property TemperatureC As Integer
            Public ReadOnly Property Summary As String
    
            <JsonConstructor>
            Public Sub New([Date] As Date, TemperatureC As Integer, Summary As String)
                Me.Date = [Date]
                Me.TemperatureC = TemperatureC
                Me.Summary = Summary
            End Sub
    
        End Structure
    
        Public NotInheritable Class Program
    
            Public Shared Sub Main()
                Dim json As String = "{""date"":""2020-09-06T11:31:01.923395-07:00"",""temperatureC"":-1,""summary"":""Cold""}"
                Console.WriteLine($"Input JSON: {json}")
    
                Dim options As New JsonSerializerOptions(JsonSerializerDefaults.Web)
    
                Dim forecast1 As Forecast = JsonSerializer.Deserialize(Of Forecast)(json, options)
    
                Console.WriteLine($"forecast.Date: {forecast1.[Date]}")
                Console.WriteLine($"forecast.TemperatureC: {forecast1.TemperatureC}")
                Console.WriteLine($"forecast.Summary: {forecast1.Summary}")
    
                Dim roundTrippedJson As String = JsonSerializer.Serialize(forecast1, options)
    
                Console.WriteLine($"Output JSON: {roundTrippedJson}")
            End Sub
    
        End Class
    
    End Namespace
    
    ' Produces output like the following example:
    '
    'Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}
    'forecast.Date: 9 / 6 / 2020 11:31:01 AM
    'forecast.TemperatureC: -1
    'forecast.Summary: Cold
    'Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}
    

    .NET 7 및 이전 버전에서는 공용 생성자에서만 [JsonConstructor] 특성을 사용할 수 있습니다.

매개 변수가 있는 생성자의 매개 변수 이름은 속성 이름 및 형식과 일치해야 합니다. 일치는 대/소문자를 구분하지 않으며 [JsonPropertyName]을 사용하여 속성 이름을 바꾸는 경우에도 생성자 매개 변수가 실제 속성 이름과 일치해야 합니다. 다음 예제에서는 TemperatureC 속성의 이름은 JSON에서 celsius로 변경되지만 생성자 매개 변수의 이름은 여전히 temperatureC입니다.

using System.Text.Json;
using System.Text.Json.Serialization;

namespace ImmutableTypesCtorParms
{
    public readonly struct Forecast
    {
        public DateTime Date { get; }
        [JsonPropertyName("celsius")]
        public int TemperatureC { get; }
        public string Summary { get; }
 
        [JsonConstructor]
        public Forecast(DateTime date, int temperatureC, string summary) =>
            (Date, TemperatureC, Summary) = (date, temperatureC, summary);
    }

    public class Program
    {
        public static void Main()
        {
            string json = """
                {
                    "date":"2020-09-06T11:31:01.923395-07:00",
                    "celsius":-1,
                    "summary":"Cold"
                }
                """;
            Console.WriteLine($"Input JSON: {json}");

            var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);

            Forecast forecast = JsonSerializer.Deserialize<Forecast>(json, options);

            Console.WriteLine($"forecast.Date: {forecast.Date}");
            Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}");
            Console.WriteLine($"forecast.Summary: {forecast.Summary}");

            string roundTrippedJson =
                JsonSerializer.Serialize<Forecast>(forecast, options);

            Console.WriteLine($"Output JSON: {roundTrippedJson}");

        }
    }
}

// Produces output like the following example:
//
//Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","celsius":-1,"summary":"Cold"}
//forecast.Date: 9 / 6 / 2020 11:31:01 AM
//forecast.TemperatureC: -1
//forecast.Summary: Cold
//Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","celsius":-1,"summary":"Cold"}

다음 특성은 [JsonPropertyName] 외에도 매개 변수가 있는 생성자를 사용한 deserialization을 지원합니다.

레코드

다음 예제와 같이 serialization 및 deserialization 모두에 대해 레코드가 지원됩니다.

using System.Text.Json;

namespace Records
{
    public record Forecast(DateTime Date, int TemperatureC)
    {
        public string? Summary { get; init; }
    };

    public class Program
    {
        public static void Main()
        {
            Forecast forecast = new(DateTime.Now, 40)
            {
                Summary = "Hot!"
            };

            string forecastJson = JsonSerializer.Serialize<Forecast>(forecast);
            Console.WriteLine(forecastJson);
            Forecast? forecastObj = JsonSerializer.Deserialize<Forecast>(forecastJson);
            Console.WriteLine(forecastObj);
        }
    }
}

// Produces output like the following example:
//
//{ "Date":"2020-10-21T15:26:10.5044594-07:00","TemperatureC":40,"Summary":"Hot!"}
//Forecast { Date = 10 / 21 / 2020 3:26:10 PM, TemperatureC = 40, Summary = Hot! }

특성의 property: 대상을 사용하여 속성 이름에 특성을 적용할 수 있습니다. 위치 레코드에 대한 자세한 내용은 C# 언어 참조의 레코드 문서를 참조하세요.

Public이 아닌 멤버 및 속성 접근자

다음 예제와 같이 [JsonInclude] 특성을 사용하여 속성에서 nonpublic 접근자를 사용하도록 설정할 수 있습니다.

using System.Text.Json;
using System.Text.Json.Serialization;

namespace NonPublicAccessors
{
    public class Forecast
    {
        public DateTime Date { get; init; }

        [JsonInclude]
        public int TemperatureC { get; private set; }

        [JsonInclude]
        public string? Summary { private get; set; }
    };

    public class Program
    {
        public static void Main()
        {
            string json = """
                {
                    "Date":"2020-10-23T09:51:03.8702889-07:00",
                    "TemperatureC":40,
                    "Summary":"Hot"
                }
                """;
            Console.WriteLine($"Input JSON: {json}");

            Forecast forecastDeserialized = JsonSerializer.Deserialize<Forecast>(json)!;
            Console.WriteLine($"Date: {forecastDeserialized.Date}");
            Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}");

            json = JsonSerializer.Serialize<Forecast>(forecastDeserialized);
            Console.WriteLine($"Output JSON: {json}");
        }
    }
}

// Produces output like the following example:
//
//Input JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
//Date: 10 / 23 / 2020 9:51:03 AM
//TemperatureC: 40
//Output JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
Imports System.Text.Json
Imports System.Text.Json.Serialization

Namespace NonPublicAccessors

    Public Class Forecast
        Public Property [Date] As Date

        Private _temperatureC As Integer

        <JsonInclude>
        Public Property TemperatureC As Integer
            Get
                Return _temperatureC
            End Get
            Private Set(Value As Integer)
                _temperatureC = Value
            End Set
        End Property

        Private _summary As String

        <JsonInclude>
        Public Property Summary As String
            Private Get
                Return _summary
            End Get
            Set(Value As String)
                _summary = Value
            End Set
        End Property

    End Class

    Public NotInheritable Class Program

        Public Shared Sub Main()
            Dim json As String = "{""Date"":""2020-10-23T09:51:03.8702889-07:00"",""TemperatureC"":40,""Summary"":""Hot""}"
            Console.WriteLine($"Input JSON: {json}")

            Dim forecastDeserialized As Forecast = JsonSerializer.Deserialize(Of Forecast)(json)
            Console.WriteLine($"Date: {forecastDeserialized.[Date]}")
            Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}")

            json = JsonSerializer.Serialize(forecastDeserialized)
            Console.WriteLine($"Output JSON: {json}")
        End Sub

    End Class

End Namespace

' Produces output like the following example:
'
'Input JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
'Date: 10 / 23 / 2020 9:51:03 AM
'TemperatureC: 40
'Output JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}

프라이빗 setter와 함께 속성을 포함하면 해당 속성을 역직렬화할 수 있습니다.

.NET 8 이상 버전에서는 [JsonInclude] 특성을 사용하여 지정된 형식에 대한 serialization 계약에 nonpublic 멤버를 선택할 수도 있습니다.

참고 항목

원본 생성 모드에서는 private 멤버를 직렬화 하거나 [JsonInclude] 특성으로 주석을 추가하여 private 접근자를 사용할 수 없습니다. 또한 생성된 JsonSerializerContext(와)과 동일한 어셈블리에 있는 경우에만 internal 멤버를 직렬화 하거나 internal 접근자를 사용할 수 있습니다.

읽기 전용 속성

.NET 8 이상 버전에서는 읽기 전용 속성 또는 프라이빗 또는 퍼블릭 setter가 없는 속성도 역직렬화할 수 있습니다. 속성이 참조하는 인스턴스는 변경할 수 없지만 속성 형식이 변경 가능한 경우 수정할 수 있습니다. 예를 들어 목록에 요소를 추가할 수 있습니다. 읽기 전용 속성을 역직렬화하려면 개체 만들기 처리 동작을 바꾸는 대신 채우도록 설정해야 합니다. 예를 들어 JsonObjectCreationHandlingAttribute 특성으로 속성에 주석을 달 수 있습니다.

class A
{
    [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
    public List<int> Numbers1 { get; } = new List<int>() { 1, 2, 3 };
}

자세한 내용은 초기화된 속성 채우기를 참조하세요.

참고 항목