다음을 통해 공유


초기화된 속성 채우기

.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/runtime 문제 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]}""");

구조체 속성 예제

다음 클래스에는 구조체 속성 S1이 있으며, 그 역직렬화 동작은 Populate으로 설정되어 있습니다. 이 코드를 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 값이 새 인스턴스로 대체되기 때문입니다. (c.S1.Value2 JSON이 기본값을 대체하므로 여전히 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
    };