Share via


초기화된 속성 채우기

.NET 8부터 JSON이 역직렬화될 때 .NET 속성을 바꾸거나채우도록 기본 설정을 지정할 수 있습니다. JsonObjectCreationHandling 열거형은 개체 만들기 처리 선택 사항을 제공합니다.

기본(바꾸기) 동작

System.Text.Json 역직렬 변환기는 항상 대상 유형의 새 인스턴스를 만듭니다. 그러나 새 인스턴스가 만들어지더라도 일부 속성과 필드는 개체 만들기의 일부로 이미 초기화되었을 수 있습니다. 다음 형식을 고려해 보세요.

class A
{
    public List<int> Numbers1 { get; } = [1, 2, 3];
    public List<int> Numbers2 { get; set; } = [1, 2, 3];
}

이 클래스의 인스턴스를 만들 때 Numbers1(및 Numbers2) 속성 값은 세 가지 요소(1, 2, 3)가 포함된 목록입니다. JSON을 이 형식으로 역직렬화하는 경우 기본 동작은 속성 값이 바뀌는 것입니다.

  • Numbers1의 경우 읽기 전용(setter 없음)이므로 목록에 여전히 값 1, 2, 3이 있습니다.
  • 읽기-쓰기인 Numbers2의 경우 새 목록이 할당되고 JSON의 값이 추가됩니다.

예를 들어, 다음 역직렬화 코드를 실행하면 Numbers1에는 값 1, 2, 3이 포함되고 Numbers2에는 값 4, 5, 6이 포함됩니다.

A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");

채우기 동작

.NET 8부터 역직렬화 동작을 변경하여 속성과 필드를 바꾸는 대신 수정(채우기)할 수 있습니다.

  • 컬렉션형 속성의 경우 개체를 지우지 않고 재사용합니다. 컬렉션에 요소가 미리 채워져 있으면 해당 요소는 JSON의 값과 함께 역직렬화된 최종 결과에 표시됩니다. 예를 보려면 컬렉션 속성 예를 참조하세요.

  • 속성이 있는 개체인 속성의 경우 변경 가능한 속성은 JSON 값으로 업데이트되지만 개체 참조 자체는 변경되지 않습니다.

  • 구조체 형식 속성의 경우 변경 가능한 속성에 기존 값이 유지되고 JSON의 새 값이 추가되는 것이 효과적인 동작입니다. 그러나 참조 속성과 달리 개체 자체는 값 형식이므로 재사용되지 않습니다. 대신 구조체의 복사본이 수정된 다음 속성에 다시 할당됩니다. 예를 보려면 구조 속성 예를 참조하세요.

    구조체 속성에는 setter가 있어야 합니다. 그렇지 않으면 런타임 시 InvalidOperationException이 throw됩니다.

참고 항목

현재 매개 변수가 있는 생성자가 있는 형식에서는 채우기 동작이 작동하지 않습니다. 자세한 내용은 dotnet/런타임 문제 92877를 참조하세요.

읽기 전용 속성

변경 가능한 참조 속성을 채우는 경우 속성이 참조하는 인스턴스가 바꾸기되지 않으므로 속성에 setter가 필요하지 않습니다. 이 동작은 역직렬화가 읽기 전용 속성을 채울 수도 있음을 의미합니다.

참고 항목

인스턴스가 수정된 복사본으로 바뀌므로 구조체 속성에는 여전히 setter가 필요합니다.

컬렉션 속성 예

바꾸기 동작 예에서 동일한 클래스 A를 고려합니다. 하지만 이번에는 속성을 바꾸는 대신 채우기 속성에 대한 기본 설정으로 주석이 추가되었습니다.

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

다음 역직렬화 코드를 실행하면 Numbers1Numbers2 모두 값 1, 2, 3, 4, 5, 6을 포함합니다.

A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");

구조체 속성 예

다음 클래스에는 역직렬화 동작이 Populate로 설정된 구조체 속성 S1이 포함되어 있습니다. 이 코드를 실행한 후 c.S1.Value1의 값은 10(생성자에서)이고 c.S1.Value2의 값은 5(JSON에서)입니다.

C? c = JsonSerializer.Deserialize<C>("""{"S1": {"Value2": 5}}""");

class C
{
    public C()
    {
        _s1 = new S
        {
            Value1 = 10
        };
    }

    private S _s1;

    [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
    public S S1
    {
        get { return _s1; }
        set { _s1 = value; }
    }
}

struct S
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

대신 기본 Replace 동작을 사용한 경우 역직렬화 후 c.S1.Value1의 기본값은 0입니다. 이는 생성자 C()가 호출되어 c.S1.Value1을 10으로 설정하지만 S1의 값이 새 인스턴스로 바뀌기 때문입니다. (JSON이 기본값을 바꾸므로 c.S1.Value2는 여전히 5입니다.)

지정 방법

바꾸기 또는 채우기에 대한 기본 설정을 지정하는 방법에는 여러 가지가 있습니다.

  • 형식 또는 속성 수준에서 주석을 추가하려면 JsonObjectCreationHandlingAttribute 특성을 사용합니다. 형식 수준에서 특성을 설정하고 해당 Handling 속성을 Populate로 설정하면 동작은 채우기가 가능한 속성에만 적용됩니다(예: 값 형식에 setter가 있어야 함).

    형식 전체 기본 설정을 Populate로 설정하고 해당 동작에서 하나 이상의 속성을 제외하려는 경우 형식 수준에서 특성을 추가한 다음 속성 수준에서 다시 추가하여 상속된 동작을 재정의할 수 있습니다. 해당 패턴은 다음 코드에 나와 있습니다.

    // Type-level preference is Populate.
    [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
    class B
    {
        // For this property only, use Replace behavior.
        [JsonObjectCreationHandling(JsonObjectCreationHandling.Replace)]
        public List<int> Numbers1 { get; } = [1, 2, 3];
        public List<int> Numbers2 { get; set; } = [1, 2, 3];
    }
    
  • 전역 기본 설정을 지정하려면 JsonSerializerOptions.PreferredObjectCreationHandling(또는 원본 생성의 경우 JsonSourceGenerationOptionsAttribute.PreferredObjectCreationHandling)을 설정합니다.

    var options = new JsonSerializerOptions
    {
        PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate
    };