Newtonsoft.Json에서 System.Text.Json(으)로 마이그레이션

이 문서에서는 Newtonsoft.Json에서 System.Text.Json로 마이그레이션하는 방법을 보여줍니다.

System.Text.Json 네임스페이스는 JSON(JavaScript Object Notation)에서 직렬화 및 역직렬화하는 기능을 제공합니다. System.Text.Json 라이브러리는 .NET Core 3.1 이상 버전의 런타임에 포함되어 있습니다. 다른 대상 프레임워크의 경우 System.Text.Json NuGet 패키지를 설치합니다. 패키지는 다음을 지원합니다.

  • .NET Standard 2.0 이상 버전
  • .NET Framework 4.7.2 이상 버전
  • .NET Core 2.0, 2.1, 2.2

System.Text.Json은 성능, 보안 및 표준 규정 준수에 중점을 둡니다. 기본 동작에서 몇 가지 큰 차이가 있으며 Newtonsoft.Json과의 기능 패리티를 목표로 하지 않습니다. 일부 시나리오에서는 현재 System.Text.Json에 기본 제공 기능이 없지만, 권장 해결 방법이 있습니다. 그 외의 시나리오에서는 해결 방법이 실용적이지 않습니다.

Microsoft는 가장 자주 요청된 기능을 추가하는 데 투자하고 있습니다. 애플리케이션에서 사용하는 기능이 없는 경우 dotnet/runtime GitHub 리포지토리에서 이슈를 제출하여 해당 시나리오에 대한 지원을 추가할 수 있는지 알아보세요.

이 문서의 대부분은 JsonSerializer API 사용 방법에 대한 내용이지만, JsonDocument(DOM(문서 개체 모델)을 나타냄), Utf8JsonReaderUtf8JsonWriter 형식을 사용하는 방법에 대한 지침도 포함되어 있습니다.

Visual Basic에서는 Utf8JsonReader를 사용할 수 없습니다. 즉, 사용자 지정 변환기를 작성할 수 없습니다. 여기에 제시된 대부분의 해결 방법은 사용자 지정 변환기를 작성해야 합니다. C#에서 사용자 지정 변환기를 작성하고 Visual Basic 프로젝트에 등록할 수 있습니다. 자세한 내용은 Visual Basic 지원을 참조하세요.

차이점 표

다음 표에는 Newtonsoft.Json 기능과 그에 상응하는 System.Text.Json 기능이 나열되어 있습니다. 상응하는 기능은 다음 범주로 분류됩니다.

  • ✔️ 기본 제공 기능에서 지원됩니다. System.Text.Json에서 유사한 동작을 가져오려면 특성 또는 글로벌 옵션을 사용해야 할 수도 있습니다.
  • ⚠ 지원되지 않지만 해결이 가능합니다. 해결 방법은 사용자 지정 변환기이며, 사용자 지정 변환기는 Newtonsoft.Json 기능과의 완전한 패리티를 제공하지 않을 수 있습니다. 그 중 일부는 샘플 코드가 예제로 제공됩니다. 이러한 Newtonsoft.Json 기능을 사용하는 경우 마이그레이션을 수행하려면 .NET 개체 모델 또는 기타 코드 변경 내용을 수정해야 합니다.
  • ❌ 지원되지 않으며 해결 방법이 실용적이지 않거나 가능하지 않습니다. 이러한 Newtonsoft.Json 기능을 사용하는 경우 중요한 변경 없이는 마이그레이션을 수행할 수 없습니다.
Newtonsoft.Json 기능 System.Text.Json 해당 항목
기본적으로 대/소문자를 구분하지 않는 역직렬화 ✔️ PropertyNameCaseInsensitive 글로벌 설정
카멜식 대/소문자 속성 이름 ✔️ PropertyNamingPolicy 글로벌 설정
스네이크 케이스 속성 이름 ✔️ 스네이크 표기법 이름 지정 정책
최소 문자 이스케이프 ✔️ 엄격한 문자 이스케이프, 구성 가능
NullValueHandling.Ignore 글로벌 설정 ✔️ DefaultIgnoreCondition 전역 옵션
주석 허용 ✔️ ReadCommentHandling 글로벌 설정
후행 쉼표 허용 ✔️ AllowTrailingCommas 글로벌 설정
사용자 지정 변환기 등록 ✔️ 우선 순위가 다름
기본 최대 깊이는 64, 구성 가능 ✔️ 기본 최대 깊이는 64, 구성 가능
PreserveReferencesHandling 글로벌 설정 ✔️ ReferenceHandling 전역 설정
따옴표 안의 숫자를 직렬화하거나 역직렬화 ✔️ NumberHandling 전역 설정, [JsonNumberHandling] 특성
변경할 수 없는 클래스 및 구조체로 역직렬화 ✔️ JsonConstructor, C# 9 레코드
필드에 대한 지원 ✔️ IncludeFields 전역 설정, [JsonInclude] 특성
DefaultValueHandling 글로벌 설정 ✔️ DefaultIgnoreCondition 전역 설정
[JsonProperty]에서의 NullValueHandling 설정 ✔️ JsonIgnore 특성
[JsonProperty]에서의 DefaultValueHandling 설정 ✔️ JsonIgnore 특성
문자열이 아닌 키로 Dictionary 역직렬화 ✔️ 지원됨
public이 아닌 속성 setter 및 getter 지원 ✔️ JsonInclude 특성
[JsonConstructor] 특성 ✔️ JsonConstructor 특성
ReferenceLoopHandling 글로벌 설정 ✔️ ReferenceHandling 전역 설정
콜백 ✔️ 콜백
NaN, Infinity, -Infinity ✔️ 지원됨
[JsonProperty] 특성에 대한 Required 설정 ✔️ [JsonRequired] 특성 및 C# 필수 한정자
속성을 무시하는 DefaultContractResolver ✔️ DefaultJsonTypeInfoResolver 클래스
다형 직렬화 ✔️ [JsonDerivedType] 특성
다형 역직렬화 ✔️ [JsonDerivedType] 특성에 대한 형식 판별자
문자열 열거형 값 역직렬화 ✔️ 문자열 열거형 값 역직렬화
MissingMemberHandling 글로벌 설정 ✔️ 누락된 멤버 처리
setter 없이 속성 채우기 ✔️ setter 없이 속성 채우기
ObjectCreationHandling 글로벌 설정 ✔️ 속성을 바꾸지 않고 재사용
광범위한 형식 지원 ⚠️ 일부 형식은 사용자 지정 변환기 필요
유추 형식을 object 속성으로 역직렬화 ⚠️ 지원되지 않음, 해결 가능, 샘플
JSON null 리터럴을 null을 허용하지 않는 값 형식으로 역직렬화 ⚠️ 지원되지 않음, 해결 가능, 샘플
DateTimeZoneHandling, DateFormatString 설정 ⚠️ 지원되지 않음, 해결 가능, 샘플
JsonConvert.PopulateObject 메서드 지원되지 않음, 해결 가능
System.Runtime.Serialization 특성 지원 ⚠️ 지원되지 않음, 해결 가능, 샘플
JsonObjectAttribute ⚠️ 지원되지 않음, 해결 가능
따옴표 없는 속성 이름 허용 디자인에서 지원되지 않음
문자열 값 주변에 작은따옴표 허용 디자인에서 지원되지 않음
문자열 속성에 문자열이 아닌 JSON 값 허용 디자인에서 지원되지 않음
TypeNameHandling.All 글로벌 설정 디자인에서 지원되지 않음
JsonPath 쿼리 지원 지원되지 않음
구성 가능한 제한 지원되지 않음
Newtonsoft.Json 기능 System.Text.Json 해당 항목
기본적으로 대/소문자를 구분하지 않는 역직렬화 ✔️ PropertyNameCaseInsensitive 글로벌 설정
카멜식 대/소문자 속성 이름 ✔️ PropertyNamingPolicy 글로벌 설정
최소 문자 이스케이프 ✔️ 엄격한 문자 이스케이프, 구성 가능
NullValueHandling.Ignore 글로벌 설정 ✔️ DefaultIgnoreCondition 전역 옵션
주석 허용 ✔️ ReadCommentHandling 글로벌 설정
후행 쉼표 허용 ✔️ AllowTrailingCommas 글로벌 설정
사용자 지정 변환기 등록 ✔️ 우선 순위가 다름
기본 최대 깊이는 64, 구성 가능 ✔️ 기본 최대 깊이는 64, 구성 가능
PreserveReferencesHandling 글로벌 설정 ✔️ ReferenceHandling 전역 설정
따옴표 안의 숫자를 직렬화하거나 역직렬화 ✔️ NumberHandling 전역 설정, [JsonNumberHandling] 특성
변경할 수 없는 클래스 및 구조체로 역직렬화 ✔️ JsonConstructor, C# 9 레코드
필드에 대한 지원 ✔️ IncludeFields 전역 설정, [JsonInclude] 특성
DefaultValueHandling 글로벌 설정 ✔️ DefaultIgnoreCondition 전역 설정
[JsonProperty]에서의 NullValueHandling 설정 ✔️ JsonIgnore 특성
[JsonProperty]에서의 DefaultValueHandling 설정 ✔️ JsonIgnore 특성
문자열이 아닌 키로 Dictionary 역직렬화 ✔️ 지원됨
public이 아닌 속성 setter 및 getter 지원 ✔️ JsonInclude 특성
[JsonConstructor] 특성 ✔️ JsonConstructor 특성
ReferenceLoopHandling 글로벌 설정 ✔️ ReferenceHandling 전역 설정
콜백 ✔️ 콜백
NaN, Infinity, -Infinity ✔️ 지원됨
[JsonProperty] 특성에 대한 Required 설정 ✔️ [JsonRequired] 특성 및 C# 필수 한정자
속성을 무시하는 DefaultContractResolver ✔️ DefaultJsonTypeInfoResolver 클래스
다형 직렬화 ✔️ [JsonDerivedType] 특성
다형 역직렬화 ✔️ [JsonDerivedType] 특성에 대한 형식 판별자
문자열 열거형 값 역직렬화 ✔️ 문자열 열거형 값 역직렬화
광범위한 형식 지원 ⚠️ 일부 형식은 사용자 지정 변환기 필요
유추 형식을 object 속성으로 역직렬화 ⚠️ 지원되지 않음, 해결 가능, 샘플
JSON null 리터럴을 null을 허용하지 않는 값 형식으로 역직렬화 ⚠️ 지원되지 않음, 해결 가능, 샘플
DateTimeZoneHandling, DateFormatString 설정 ⚠️ 지원되지 않음, 해결 가능, 샘플
JsonConvert.PopulateObject 메서드 ⚠️ 지원되지 않음, 해결 가능
ObjectCreationHandling 글로벌 설정 지원되지 않음, 해결 가능
setter 없이 컬렉션에 추가 지원되지 않음, 해결 가능
스네이크 케이스 속성 이름 지원되지 않음, 해결 가능
System.Runtime.Serialization 특성 지원 ⚠️ 지원되지 않음, 해결 가능, 샘플
MissingMemberHandling 글로벌 설정 ⚠️ 지원되지 않음, 해결 가능, 샘플
JsonObjectAttribute ⚠️ 지원되지 않음, 해결 가능
따옴표 없는 속성 이름 허용 디자인에서 지원되지 않음
문자열 값 주변에 작은따옴표 허용 디자인에서 지원되지 않음
문자열 속성에 문자열이 아닌 JSON 값 허용 디자인에서 지원되지 않음
TypeNameHandling.All 글로벌 설정 디자인에서 지원되지 않음
JsonPath 쿼리 지원 지원되지 않음
구성 가능한 제한 지원되지 않음
Newtonsoft.Json 기능 System.Text.Json 해당 항목
기본적으로 대/소문자를 구분하지 않는 역직렬화 ✔️ PropertyNameCaseInsensitive 글로벌 설정
카멜식 대/소문자 속성 이름 ✔️ PropertyNamingPolicy 글로벌 설정
최소 문자 이스케이프 ✔️ 엄격한 문자 이스케이프, 구성 가능
NullValueHandling.Ignore 글로벌 설정 ✔️ DefaultIgnoreCondition 전역 옵션
주석 허용 ✔️ ReadCommentHandling 글로벌 설정
후행 쉼표 허용 ✔️ AllowTrailingCommas 글로벌 설정
사용자 지정 변환기 등록 ✔️ 우선 순위가 다름
기본 최대 깊이는 64, 구성 가능 ✔️ 기본 최대 깊이는 64, 구성 가능
PreserveReferencesHandling 글로벌 설정 ✔️ ReferenceHandling 전역 설정
따옴표 안의 숫자를 직렬화하거나 역직렬화 ✔️ NumberHandling 전역 설정, [JsonNumberHandling] 특성
변경할 수 없는 클래스 및 구조체로 역직렬화 ✔️ JsonConstructor, C# 9 레코드
필드에 대한 지원 ✔️ IncludeFields 전역 설정, [JsonInclude] 특성
DefaultValueHandling 글로벌 설정 ✔️ DefaultIgnoreCondition 전역 설정
[JsonProperty]에서의 NullValueHandling 설정 ✔️ JsonIgnore 특성
[JsonProperty]에서의 DefaultValueHandling 설정 ✔️ JsonIgnore 특성
문자열이 아닌 키로 Dictionary 역직렬화 ✔️ 지원됨
public이 아닌 속성 setter 및 getter 지원 ✔️ JsonInclude 특성
[JsonConstructor] 특성 ✔️ JsonConstructor 특성
ReferenceLoopHandling 글로벌 설정 ✔️ ReferenceHandling 전역 설정
콜백 ✔️ 콜백
NaN, Infinity, -Infinity ✔️ 지원됨
문자열 열거형 값 역직렬화 ✔️ 문자열 열거형 값 역직렬화
광범위한 형식 지원 ⚠️ 일부 형식은 사용자 지정 변환기 필요
다형 직렬화 ⚠️ 지원되지 않음, 해결 가능, 샘플
다형 역직렬화 ⚠️ 지원되지 않음, 해결 가능, 샘플
유추 형식을 object 속성으로 역직렬화 ⚠️ 지원되지 않음, 해결 가능, 샘플
JSON null 리터럴을 null을 허용하지 않는 값 형식으로 역직렬화 ⚠️ 지원되지 않음, 해결 가능, 샘플
[JsonProperty] 특성에 대한 Required 설정 ⚠️ 지원되지 않음, 해결 가능, 샘플
속성을 무시하는 DefaultContractResolver ⚠️ 지원되지 않음, 해결 가능, 샘플
DateTimeZoneHandling, DateFormatString 설정 ⚠️ 지원되지 않음, 해결 가능, 샘플
JsonConvert.PopulateObject 메서드 ⚠️ 지원되지 않음, 해결 가능
ObjectCreationHandling 글로벌 설정 지원되지 않음, 해결 가능
setter 없이 컬렉션에 추가 지원되지 않음, 해결 가능
스네이크 케이스 속성 이름 지원되지 않음, 해결 가능
JsonObjectAttribute 지원되지 않음, 해결 가능
System.Runtime.Serialization 특성 지원 지원되지 않음
MissingMemberHandling 글로벌 설정 지원되지 않음
따옴표 없는 속성 이름 허용 디자인에서 지원되지 않음
문자열 값 주변에 작은따옴표 허용 디자인에서 지원되지 않음
문자열 속성에 문자열이 아닌 JSON 값 허용 디자인에서 지원되지 않음
TypeNameHandling.All 글로벌 설정 디자인에서 지원되지 않음
JsonPath 쿼리 지원 지원되지 않음
구성 가능한 제한 지원되지 않음

이 목록은 Newtonsoft.Json 기능의 전체 목록이 아닙니다. 이 목록에는 GitHub 이슈 또는 StackOverflow 게시물에 요청된 여러 시나리오가 포함되어 있습니다. 여기에 나열된 시나리오 중에서 현재 샘플 코드가 없는 시나리오에 대한 해결 방법을 구현하셨으며 그 방법을 공유하려는 분들은 이 페이지 하단의 피드백 섹션에서 이 페이지를 선택하세요. 그러면 이 설명서의 GitHub 리포지토리에 이슈가 작성되고 이 페이지의 피드백 섹션에도 이슈가 나열됩니다.

기본 동작의 차이점

System.Text.Json은 기본적으로 엄격하며, 호출자를 대신하여 추측하거나 해석하는 것을 금지하고 결정적 동작을 강조합니다. 이 라이브러리는 성능과 보안을 위해 의도적으로 이렇게 설계되었습니다. Newtonsoft.Json은 기본적으로 유연합니다. 이러한 기본적인 디자인의 차이로 인해 기본 동작에서 다음과 같은 여러 가지 차이가 있습니다.

대/소문자를 구분하지 않는 역직렬화

역직렬화를 수행하는 동안 Newtonsoft.Json은 기본적으로 대/소문자를 구분하지 않는 속성 이름을 매칭합니다. System.Text.Json은 기본적으로 대/소문자를 구분하며, 이 방법은 매칭을 정확히 수행하기 때문에 보다 나은 성능을 제공합니다. 대/소문자를 구분하지 않는 매칭 방법에 대한 자세한 내용은 대/소문자를 구분하지 않는 속성 매칭을 참조하세요.

ASP.NET Core를 사용하여 간접적으로 System.Text.Json을 사용하는 경우 Newtonsoft.Json과 같은 동작을 얻기 위해 아무것도 할 필요가 없습니다. ASP.NET Core는 System.Text.Json을(를) 사용할 때 카멜식 대/소문자 구분 속성 이름 및 대/소문자를 구분하지 않는 매칭에 대한 설정을 지정합니다.

ASP.NET Core에서는 기본적으로 따옴표 붙은 숫자의 역직렬화도 가능합니다.

최소 문자 이스케이프

직렬화할 때 Newtonsoft.Json은 문자를 이스케이프하지 않고 허용하는 것에 대해 비교적 관대합니다. 즉, 문자를 \uxxxx로 바꾸지 않으며, 여기서 xxxx는 문자의 코드 포인트입니다. 문자를 이스케이프할 때는 문자 앞에 \를 내보냅니다. 예를 들어 "\"가 됩니다. System.Text.Json은 XSS(교차 사이트 스크립팅) 또는 정보 공개 공격에 대한 심층 방어를 제공하기 위해 기본적으로 더 많은 문자를 이스케이프하며, 그러기 위해 6문자 시퀀스를 사용합니다. System.Text.Json은 기본적으로 ASCII가 아닌 모든 문자를 이스케이프하므로, Newtonsoft.Json에서 StringEscapeHandling.EscapeNonAscii를 사용 중이라면 아무것도 할 필요가 없습니다. 또한 System.Text.Json은 기본적으로 HTML 구분 문자를 이스케이프합니다. 기본 System.Text.Json 동작을 재정의하는 방법에 대한 자세한 내용은 문자 인코딩 사용자 지정을 참조하세요.

주석

역직렬화할 때 Newtonsoft.Json은 기본적으로 JSON의 주석을 무시합니다. RFC 8259 사양에 주석이 포함되지 않기 때문에 System.Text.Json은 기본적으로 주석에 대해 예외를 throw합니다. 주석을 허용하는 방법에 대한 자세한 내용은 주석과 후행 쉼표 허용을 참조하세요.

후행 쉼표

역직렬화할 때 Newtonsoft.Json은 기본적으로 후행 쉼표를 무시합니다. 또한 여러 개의 후행 쉼표를 무시합니다(예: [{"Color":"Red"},{"Color":"Green"},,]). RFC 8259 사양에서 후행 쉼표를 허용하지 않기 때문에 System.Text.Json은 기본적으로 후행 쉼표에 대해 예외를 throw합니다. System.Text.Json에서 후행 쉼표를 허용하게 만드는 방법은 주석과 후행 쉼표 허용을 참조하세요. 후행 쉼표를 여러 개 허용하는 방법은 없습니다.

변환기 등록 우선 순위

사용자 지정 변환기에 대한 Newtonsoft.Json 등록 우선 순위는 다음과 같습니다.

  • 속성의 특성
  • 형식의 특성
  • 변환기 컬렉션

이 순서는 형식 수준에서 특성을 적용하여 등록된 변환기가 Converters 컬렉션의 사용자 지정 변환기를 재정의한다는 뜻입니다. 두 등록 모두 속성 수준에서 특성에 의해 재정의됩니다.

사용자 지정 변환기에 대한 System.Text.Json 등록 우선 순위는 다음과 같이 다릅니다.

  • 속성의 특성
  • Converters 컬렉션
  • 형식의 특성

여기서 Converters 컬렉션의 사용자 지정 변환기가 형식 수준에서 특성을 재정의한다는 차이가 있습니다. 이 우선 순위 순서의 숨은 의도는 런타임 변경이 디자인 타임 선택 항목을 재정의하도록 하는 것입니다. 우선 순위를 변경할 수 있는 방법은 없습니다.

사용자 지정 변환기 등록에 대한 자세한 내용은 사용자 지정 변환기 등록을 참조하세요.

최대 깊이

Newtonsoft.Json의 최신 버전은 기본적으로 최대 깊이 제한이 64입니다. System.Text.Json도 기본 제한은 64이며, JsonSerializerOptions.MaxDepth를 설정하여 구성할 수 있습니다.

ASP.NET Core를 사용하여 간접적으로 System.Text.Json를 사용하는 경우 기본 최대 깊이 제한은 32입니다. 기본값은 모델 바인딩과 동일하며 JsonOptions 클래스에 설정됩니다.

JSON 문자열(속성 이름 및 문자열 값)

역직렬화할 때 Newtonsoft.Json은 큰따옴표/작은따옴표로 묶은 속성 이름 또는 따옴표 없는 속성 이름을 허용합니다. 큰따옴표 또는 작은따옴표로 묶은 문자열 값을 허용합니다. 예를 들어 Newtonsoft.Json은 다음 JSON을 허용합니다.

{
  "name1": "value",
  'name2': "value",
  name3: 'value'
}

System.Text.Json은 큰따옴표로 묶은 속성 이름과 문자열 값만 허용합니다. RFC 8259 사양에서 이 형식을 요구하며 유일하게 유효한 JSON으로 간주되는 형식이기 때문입니다.

작은따옴표로 묶은 값은 다음 메시지와 함께 JsonException을 반환합니다.

''' is an invalid start of a value.

문자열 속성에 문자열이 아닌 값 허용

Newtonsoft.Json은 형식 문자열의 속성으로 역직렬화할 때 숫자 또는 리터럴 truefalse처럼 문자열이 아닌 값을 허용합니다. 아래는 Newtonsoft.Json이 성공적으로 다음 클래스로 역직렬화하는 JSON 예제입니다.

{
  "String1": 1,
  "String2": true,
  "String3": false
}
public class ExampleClass
{
    public string String1 { get; set; }
    public string String2 { get; set; }
    public string String3 { get; set; }
}

System.Text.Json은 문자열이 아닌 값을 문자열 속성으로 역직렬화하지 않습니다. 문자열 필드에 대해 문자열이 아닌 값을 받으면 다음 메시지와 함께 JsonException이 반환됩니다.

The JSON value could not be converted to System.String.

JsonSerializer를 사용하는 시나리오

다음 시나리오는 기본 제공 기능에서 지원되지 않지만 해결이 가능합니다. 해결 방법은 사용자 지정 변환기이며, 사용자 지정 변환기는 Newtonsoft.Json 기능과의 완전한 패리티를 제공하지 않을 수 있습니다. 그 중 일부는 샘플 코드가 예제로 제공됩니다. 이러한 Newtonsoft.Json 기능을 사용하는 경우 마이그레이션을 수행하려면 .NET 개체 모델 또는 기타 코드 변경 내용을 수정해야 합니다.

다음 몇몇 시나리오는 해결 방법이 실용적이지 않거나 가능하지 않습니다. 이러한 Newtonsoft.Json 기능을 사용하는 경우 중요한 변경 없이는 마이그레이션을 수행할 수 없습니다.

따옴표 안에 숫자를 허용하거나 씁니다.

Newtonsoft.Json은 JSON 문자열로 표시되는(따옴표로 묶은) 숫자를 직렬화 또는 역직렬화할 수 있습니다. 예를 들어 {"DegreesCelsius":23} 대신 {"DegreesCelsius":"23"}을 허용할 수 있습니다. System.Text.Json에서 이 동작을 사용하도록 설정하려면 JsonSerializerOptions.NumberHandlingWriteAsString 또는 AllowReadingFromString으로 설정하거나 JsonNumberHandling 특성을 사용합니다.

ASP.NET Core를 사용하여 간접적으로 System.Text.Json을 사용하는 경우 Newtonsoft.Json과 같은 동작을 얻기 위해 아무것도 할 필요가 없습니다. ASP.NET Core는 System.Text.Json을 사용할 때 웹 기본값을 지정하고, 웹 기본값은 따옴표 붙은 숫자를 허용합니다.

자세한 내용은 따옴표 안의 숫자 허용 또는 쓰기를 참조하세요.

역직렬화할 때 사용할 생성자 지정

Newtonsoft.Json[JsonConstructor] 특성을 사용하면 POCO로 역직렬화할 때 호출할 생성자를 지정할 수 있습니다.

System.Text.Json에는 JsonConstructor 특성도 있습니다. 자세한 내용은 변경 불가능한 형식 및 레코드를 참조하세요.

조건부로 속성 무시

Newtonsoft.Json은 직렬화 또는 역직렬화에서 속성을 조건부로 무시하는 여러 가지 방법이 있습니다.

  • DefaultContractResolver를 사용하면 임의의 조건에 따라 포함하거나 무시할 속성을 선택할 수 있습니다.
  • JsonSerializerSettingsNullValueHandlingDefaultValueHandling 설정을 사용하면 모든 Null 값 또는 기본값 속성을 무시하도록 지정할 수 있습니다.
  • [JsonProperty] 특성의 NullValueHandlingDefaultValueHandling 설정을 사용하면 Null 또는 기본값으로 설정된 경우에 무시할 개별 속성을 지정할 수 있습니다.

System.Text.Json은 직렬화하는 동안 다음과 같은 방법으로 속성이나 필드를 무시할 수 있습니다.

또한 .NET 7 이상 버전에서는 임의 조건에 따라 속성을 무시하도록 JSON 계약을 사용자 지정할 수 있습니다. 자세한 내용은 사용자 지정 계약을 참조하세요.

이러한 옵션은 런타임에 평가되는 임의 조건을 기준으로 선택한 속성을 무시하도록 허용하지 않습니다.

public 및 비-public 필드

Newtonsoft.Json은 속성뿐 아니라 필드까지 직렬화 및 역직렬화할 수 있습니다.

System.Text.Json에서 JsonSerializerOptions.IncludeFields 전역 설정 또는 JsonInclude 특성을 사용하여 직렬화 또는 역직렬화할 때 public 필드를 포함합니다. 예제는 필드 포함을 참조하세요.

개체 참조 유지 및 루프 처리

기본적으로 Newtonsoft.Json은 값으로 직렬화합니다. 예를 들어 개체에 두 개의 속성이 있고 두 속성은 동일한 Person 개체에 대한 참조를 포함하는 경우 해당 Person 개체의 속성 값이 JSON에서 중복됩니다.

Newtonsoft.Json에는 참조로 직렬화할 수 있는 JsonSerializerSettings에 대한 PreserveReferencesHandling 설정이 있습니다.

  • 첫 번째 Person 개체에 대해 만든 JSON에 식별자 메타데이터가 추가됩니다.
  • 두 번째 Person 개체에 대해 만든 JSON에는 속성 값 대신 해당 식별자에 대한 참조가 포함됩니다.

Newtonsoft.Json에는 예외를 throw하는 대신 순환 참조를 무시할 수 있는 ReferenceLoopHandling 설정도 있습니다.

System.Text.Json에서 참조를 보존하고 순환 참조를 처리하려면 JsonSerializerOptions.ReferenceHandlerPreserve로 설정합니다. ReferenceHandler.Preserve 설정은 Newtonsoft.JsonPreserveReferencesHandling = PreserveReferencesHandling.All과 동일합니다.

ReferenceHandler.IgnoreCycles 옵션에는 Newtonsoft.JsonReferenceLoopHandling.Ignore와 유사한 동작이 있습니다. 한 가지 차이점은 System.Text.Json 구현이 개체 참조를 무시하는 대신 참조 루프를 null JSON 토큰으로 대체한다는 것입니다. 자세한 내용은 순환 참조 무시를 참조하세요.

Newtonsoft.JsonReferenceResolver와 마찬가지로 System.Text.Json.Serialization.ReferenceResolver 클래스는 직렬화 및 역직렬화 시 참조를 보존하는 동작을 정의합니다. 파생 클래스를 만들어 사용자 지정 동작을 지정합니다. 예제는 GuidReferenceResolver를 참조하세요.

일부 관련 Newtonsoft.Json 기능은 지원되지 않습니다.

자세한 내용은 참조 보존 및 순환 참조 처리를 참조하세요.

키가 문자열이 아닌 사전

Newtonsoft.JsonSystem.Text.Json은 모두 Dictionary<TKey, TValue> 형식의 컬렉션을 지원합니다. 그러나 System.Text.Json에서 TKey는 사용자 지정 형식이 아닌 기본 형식이어야 합니다. 자세한 내용은 지원되는 키 형식을 참조하세요.

주의

TKeystring 이외의 형식으로 지정되는 Dictionary<TKey, TValue>로 역직렬화하면 소비 애플리케이션에 보안 취약성이 발생할 수 있습니다. 자세한 내용은 dotnet/runtime#4761을 참조하세요.

기본적으로 지원되지 않는 형식

System.Text.Json은 기본적으로 다음 형식을 지원하지 않습니다.

기본적으로 지원되지 않는 형식에 대한 사용자 지정 변환기를 구현할 수 있습니다.

다형 직렬화

Newtonsoft.Json은 다형 직렬화를 자동으로 수행합니다. .NET 7부터 System.Text.JsonJsonDerivedTypeAttribute 특성을 통한 다형 직렬화를 지원합니다. 자세한 내용은 파생 클래스의 직렬화 속성을 참조하세요.

다형 역직렬화

Newtonsoft.Json에는 직렬화하는 동안 JSON에 형식 이름 메타데이터를 추가하는 TypeNameHandling 설정이 있습니다. 이 설정은 역직렬화하는 동안 메타데이터를 사용하여 다형 역직렬화를 수행합니다. .NET 7부터 System.Text.Json은 형식 판별자 정보를 사용하여 다형 역직렬화를 수행합니다. 이 메타데이터는 JSON으로 내보낸 다음 역직렬화하는 동안 기본 형식으로 역직렬화할지 또는 파생 형식으로 역직렬화할지 여부를 결정하는 데 사용됩니다. 자세한 내용은 파생 클래스의 직렬화 속성을 참조하세요.

이전 .NET 버전에서 다형 역직렬화를 지원하려면 사용자 지정 변환기를 작성하는 방법의 예제와 같은 변환기를 만듭니다.

문자열 열거형 값 역직렬화

기본적으로 System.Text.Json 문자열 열거형 값은 역직렬화하는 것을 지원하지 않지만, Newtonsoft.Json는 그렇지 않습니다. 예를 들어 다음 코드는 JsonException을(를) 발생시킵니다.

string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";
var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.

class MyObj
{
    public string Text { get; set; } = "";
    public MyEnum Enum { get; set; }
}

enum MyEnum
{
    One,
    Two,
    Three
}

그러나 JsonStringEnumConverter 변환기를 사용하여 문자열 열거형 값의 역직렬화를 사용하도록 설정할 수 있습니다. 자세한 내용은 열거형을 문자열로 참조하세요.

개체 속성 역직렬화

Newtonsoft.JsonObject로 역직렬화할 때 다음을 수행합니다.

  • JSON 페이로드의 기본 값 형식(null 제외)을 유추하고 저장된 string, long, double, boolean 또는 DateTime을 boxed 개체로 반환합니다. 기본 값은 JSON 숫자, 문자열, true, false, null 등의 단일 JSON 값입니다.
  • JSON 페이로드의 복합 값에 대한 JObject 또는 JArray를 반환합니다. 복합 값은 중괄호({}) 안에 있는 JSON 키-값 쌍 컬렉션 또는 대괄호([]) 안에 있는 값 목록입니다. 중괄호 또는 대괄호 안에 있는 속성과 값이 추가 속성 또는 값을 가질 수 있습니다.
  • 페이로드에 null JSON 리터럴이 있으면 null 참조를 반환합니다.

System.Text.JsonObject로 역직렬화할 때마다 기본 값과 복합 값 모두에 대한 boxed JsonElement를 저장하며, 다음은 그 예입니다.

  • object 속성
  • object 사전 값
  • object 배열 값
  • 루트 object

그러나 페이로드에 null JSON 리터럴이 있으면 System.Text.JsonnullNewtonsoft.Json과 같은 방법으로 처리하고 null 참조를 반환합니다.

object 속성에 대한 형식 유추를 구현하려면 사용자 지정 변환기를 작성하는 방법의 예제와 같은 변환기를 만듭니다.

null을 허용하지 않는 형식으로 Null 역직렬화

Newtonsoft.Json은 는 다음과 같은 시나리오에서 예외를 throw하지 않습니다.

  • NullValueHandlingIgnore로 설정된 경우
  • 역직렬화를 수행하는 동안 JSON은 null을 허용하지 않는 값 형식에 대해 Null 값을 포함합니다.

동일한 시나리오에서 System.Text.Json은 예외를 throw합니다. (System.Text.Json에서 해당하는 null 처리 설정은 JsonSerializerOptions.IgnoreNullValues = true입니다.)

대상 형식을 소유하고 있는 경우 가장 좋은 해결 방법은 문제가 되는 속성을 null 허용으로 만드는 것입니다(예를 들어 intint?로 변경).

또 다른 해결 방법은 DateTimeOffset 형식의 Null 값을 처리하는 다음 예제처럼 형식에 대한 변환기를 만드는 것입니다.

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

namespace SystemTextJsonSamples
{
    public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
    {
        public override DateTimeOffset Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options) =>
            reader.TokenType == JsonTokenType.Null
                ? default
                : reader.GetDateTimeOffset();

        public override void Write(
            Utf8JsonWriter writer,
            DateTimeOffset dateTimeValue,
            JsonSerializerOptions options) =>
            writer.WriteStringValue(dateTimeValue);
    }
}

속성에 대한 특성을 사용하거나 Converters 컬렉션에 변환기를 추가하여 이 사용자 지정 변환기를 등록합니다.

참고: 위의 변환기는 기본값을 지정하는 POCO를 Newtonsoft.Json이 처리하는 방법과는 다르게 Null 값을 처리합니다. 예를 들어 다음 코드가 대상 개체를 보여준다고 가정하겠습니다.

public class WeatherForecastWithDefault
{
    public WeatherForecastWithDefault()
    {
        Date = DateTimeOffset.Parse("2001-01-01");
        Summary = "No summary";
    }
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}

그리고 앞의 변환기를 사용하여 다음 JSON을 역직렬화한다고 가정합니다.

{
  "Date": null,
  "TemperatureCelsius": 25,
  "Summary": null
}

역직렬화 후 Date 속성에 1/1/0001(default(DateTimeOffset))이 있습니다. 즉, 생성자에서 설정한 값이 덮어쓰기 되었습니다. POCO 및 JSON이 동일할 때 Newtonsoft.Json 역직렬화 시 Date 속성에 1/1/2001이 남습니다.

변경할 수 없는 클래스 및 구조체로 역직렬화

Newtonsoft.Json은 매개 변수가 있는 생성자를 사용할 수 있기 때문에 변경 불가능한 클래스 및 구조체로 역직렬화할 수 있습니다.

System.Text.Json에서 JsonConstructor 특성을 사용하여 매개 변수가 있는 생성자의 사용을 지정합니다. C# 9의 레코드도 변경할 수 없으며, 역직렬화 대상으로 지원됩니다. 자세한 내용은 변경 불가능한 형식 및 레코드를 참조하세요.

필수 속성

Newtonsoft.Json에서 [JsonProperty] 특성에 대한 Required를 설정하여 속성을 필수로 지정합니다. 필수로 지정된 속성에 대해 JSON에서 값을 받지 못하면 Newtonsoft.Json이 예외를 throw합니다.

.NET 7부터 필수 속성에서 C# required 한정자 또는 JsonRequiredAttribute 특성을 사용할 수 있습니다. System.Text.Json은 JSON 페이로드에 표시된 속성에 대한 값이 포함되어 있지 않으면 예외를 throw합니다. 자세한 내용은 필수 속성을 참조하세요.

대상 형식의 속성 중 하나에 대한 값을 받지 못해도 System.Text.Json은 예외를 throw하지 않습니다. 예를 들어 WeatherForecast 클래스가 있는 경우 다음과 같습니다.

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}

다음 JSON은 오류 없이 역직렬화됩니다.

{
    "TemperatureCelsius": 25,
    "Summary": "Hot"
}

JSON에 Date 속성이 없을 경우 역직렬화가 실패하도록 하려면 다음 옵션 중 하나를 선택합니다.

다음 샘플 변환기 코드는 역직렬화 완료 후 Date 속성이 설정되지 않으면 예외를 throw합니다.

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

namespace SystemTextJsonSamples
{
    public class WeatherForecastRequiredPropertyConverter : JsonConverter<WeatherForecast>
    {
        public override WeatherForecast Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // Don't pass in options when recursively calling Deserialize.
            WeatherForecast forecast = JsonSerializer.Deserialize<WeatherForecast>(ref reader)!;

            // Check for required fields set by values in JSON
            return forecast!.Date == default
                ? throw new JsonException("Required property not received in the JSON")
                : forecast;
        }

        public override void Write(
            Utf8JsonWriter writer,
            WeatherForecast forecast, JsonSerializerOptions options)
        {
            // Don't pass in options when recursively calling Serialize.
            JsonSerializer.Serialize(writer, forecast);
        }
    }
}

JsonSerializerOptions.Converters 컬렉션에 변환기를 추가하여 이 사용자 지정 변환기를 등록합니다.

변환기를 재귀적으로 호출하는 이 패턴을 사용하려면 특성이 아닌 JsonSerializerOptions를 사용하여 변환기를 등록해야 합니다. 특성을 사용하여 변환기를 등록하는 경우 사용자 지정 변환기가 재귀적으로 자신을 호출합니다. 그 결과 스택 오버플로 예외로 이어지는 무한 루프가 발생합니다.

옵션 개체를 사용하여 변환기를 등록하는 경우 Serialize 또는 Deserialize를 재귀적으로 호출할 때 옵션 개체를 전달하지 않음으로써 무한 루프 발생을 방지합니다. options 개체는 Converters 컬렉션을 포함하고 있습니다. 이 개체를 Serialize 또는 Deserialize에 전달하면 사용자 지정 변환기가 자신을 호출하여 스택 오버플로 예외로 이어지는 무한 루프가 발생합니다. 기본 옵션이 실현 불가능한 경우 필요한 설정을 사용하여 새로운 옵션 인스턴스를 만듭니다. 이 방법은 각각의 새 인스턴스가 독립적으로 캐시하므로 속도가 느립니다.

변환할 클래스에서 JsonConverterAttribute 등록을 사용할 수 있는 대체 패턴이 있습니다. 이 방식으로 변환기 코드는 변환할 클래스에서 파생된 클래스에 대해 Serialize 또는 Deserialize를 호출합니다. 파생 클래스에는 JsonConverterAttribute가 적용되지 않습니다. 이 대체 패턴의 다음 예제에서:

  • WeatherForecastWithRequiredPropertyConverterAttribute는 역직렬화할 클래스이며 JsonConverterAttribute가 적용됩니다.
  • WeatherForecastWithoutRequiredPropertyConverterAttribute는 변환기 특성이 없는 파생 클래스입니다.
  • 변환기의 코드는 WeatherForecastWithoutRequiredPropertyConverterAttribute에서 SerializeDeserialize를 호출하여 무한 루프를 방지합니다. 추가 개체 인스턴스화 및 속성 값 복사로 인해 직렬화에 대한 이 방식에 성능 비용이 발생합니다.

WeatherForecast* 형식은 다음과 같습니다.

[JsonConverter(typeof(WeatherForecastRequiredPropertyConverterForAttributeRegistration))]
public class WeatherForecastWithRequiredPropertyConverterAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}

public class WeatherForecastWithoutRequiredPropertyConverterAttribute :
    WeatherForecastWithRequiredPropertyConverterAttribute
{
}

변환기는 다음과 같습니다.

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

namespace SystemTextJsonSamples
{
    public class WeatherForecastRequiredPropertyConverterForAttributeRegistration :
        JsonConverter<WeatherForecastWithRequiredPropertyConverterAttribute>
    {
        public override WeatherForecastWithRequiredPropertyConverterAttribute Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // OK to pass in options when recursively calling Deserialize.
            WeatherForecastWithRequiredPropertyConverterAttribute forecast =
                JsonSerializer.Deserialize<WeatherForecastWithoutRequiredPropertyConverterAttribute>(
                    ref reader,
                    options)!;

            // Check for required fields set by values in JSON.
            return forecast!.Date == default
                ? throw new JsonException("Required property not received in the JSON")
                : forecast;
        }

        public override void Write(
            Utf8JsonWriter writer,
            WeatherForecastWithRequiredPropertyConverterAttribute forecast,
            JsonSerializerOptions options)
        {
            var weatherForecastWithoutConverterAttributeOnClass =
                new WeatherForecastWithoutRequiredPropertyConverterAttribute
                {
                    Date = forecast.Date,
                    TemperatureCelsius = forecast.TemperatureCelsius,
                    Summary = forecast.Summary
                };

            // OK to pass in options when recursively calling Serialize.
            JsonSerializer.Serialize(
                writer,
                weatherForecastWithoutConverterAttributeOnClass,
                options);
        }
    }
}

특성(예: [JsonIgnore] 또는 다른 옵션(예: 사용자 지정 인코더))을 처리해야 하는 경우에는 필수 속성 변환기에 추가 논리가 필요합니다. 또한 예제 코드는 생성자에서 기본값이 설정된 속성을 처리하지 않습니다. 이 방법은 다음과 같은 시나리오를 구분하지 않습니다.

  • JSON에서 속성이 누락되었습니다.
  • null을 허용하지 않는 형식의 속성은 JSON에 있지만, 값은 형식에 대한 기본값입니다(int는 0).
  • null 허용 값 형식의 속성이 JSON에 있지만, 값은 Null입니다.

참고

ASP.NET Core 컨트롤러에서 System.Text.Json을 사용하는 경우 System.Text.Json 변환기를 구현하는 대신 모델 클래스의 속성에 [Required] 특성을 사용할 수 있습니다.

날짜 형식 지정

Newtonsoft.JsonDateTimeDateTimeOffset 형식의 속성 직렬화 및 역직렬화 방식을 여러 가지 방법으로 제어할 수 있습니다.

  • DateTimeZoneHandling 설정을 사용하여 모든 DateTime 값을 UTC 날짜로 직렬화할 수 있습니다.
  • DateFormatString 설정 및 DateTime 변환기를 사용하여 날짜 문자열의 형식을 사용자 지정할 수 있습니다.

System.Text.Json은 RFC 3339 프로필을 포함하여 ISO 8601-1:2019를 지원합니다. 이 형식은 널리 채택되었으며, 명확하고, 정확하게 왕복합니다. 다른 형식을 사용하려면 사용자 지정 변환기를 만듭니다. 예를 들어 다음 변환기는 표준 시간대 오프셋(/Date(1590863400000-0700)/ 또는 /Date(1590863400000)/와 같은 값)이 포함되거나 포함되지 않은 Unix epoch 형식을 사용하는 JSON을 직렬화 및 역직렬화합니다.

sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
    static readonly DateTimeOffset s_epoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);

    public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
                || !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
                || !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
        {
            throw new JsonException();
        }

        int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
        TimeSpan utcOffset = new(hours * sign, minutes * sign, 0);

        return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
    }

    public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
        TimeSpan utcOffset = value.Offset;

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");

        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
    static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
        {
            throw new JsonException();
        }

        return s_epoch.AddMilliseconds(unixTime);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime})/");
        writer.WriteStringValue(formatted);
    }
}

자세한 내용은 System.Text.Json의 DateTime 및 DateTimeOffset 지원을 참조하세요.

콜백

Newtonsoft.Json은 다음과 같이 직렬화 또는 역직렬화 프로세스의 여러 지점에서 사용자 지정 코드를 실행할 수 있습니다.

  • OnDeserializing(개체 역직렬화를 시작할 때)
  • OnDeserialized(개체 역직렬화가 완료될 때)
  • OnSerializing(개체 직렬화를 시작할 때)
  • OnSerialized(개체 직렬화가 완료될 때)

System.Text.Json은 직렬화 및 역직렬화 중에 동일한 알림을 노출합니다. 이를 사용하려면 System.Text.Json.Serialization 네임스페이스에서 다음 인터페이스 중 하나 이상을 구현합니다.

다음은 직렬화 및 역직렬화의 시작과 끝에서 null 속성을 확인하고 메시지를 쓰는 예제입니다.

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

namespace Callbacks
{
    public class WeatherForecast : 
        IJsonOnDeserializing, IJsonOnDeserialized, 
        IJsonOnSerializing, IJsonOnSerialized
    {
        public DateTime Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }

        void IJsonOnDeserializing.OnDeserializing() => Console.WriteLine("\nBegin deserializing");
        void IJsonOnDeserialized.OnDeserialized()
        {
            Validate();
            Console.WriteLine("Finished deserializing");
        }
        void IJsonOnSerializing.OnSerializing()
        {
            Console.WriteLine("Begin serializing");
            Validate();
        }
        void IJsonOnSerialized.OnSerialized() => Console.WriteLine("Finished serializing");

        private void Validate()
        {
            if (Summary is null)
            {
                Console.WriteLine("The 'Summary' property is 'null'.");
            }
        }
    }

    public class Program
    {
        public static void Main()
        {
            var weatherForecast = new WeatherForecast
            {
                Date = DateTime.Parse("2019-08-01"),
                TemperatureCelsius = 25,
            };

            string jsonString = JsonSerializer.Serialize(weatherForecast);
            Console.WriteLine(jsonString);

            weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
            Console.WriteLine($"Date={weatherForecast?.Date}");
            Console.WriteLine($"TemperatureCelsius={weatherForecast?.TemperatureCelsius}");
            Console.WriteLine($"Summary={weatherForecast?.Summary}");
        }
    }
}
// output:
//Begin serializing
//The 'Summary' property is 'null'.
//Finished serializing
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":null}

//Begin deserializing
//The 'Summary' property is 'null'.
//Finished deserializing
//Date=8/1/2019 12:00:00 AM
//TemperatureCelsius = 25
//Summary=

OnDeserializing 코드는 새 POCO 인스턴스에 액세스할 수 없습니다. 역직렬화를 시작할 때 새 POCO 인스턴스를 조작하려면 이 코드를 POCO 생성자에 배치하세요.

public이 아닌 속성 setter 및 getter

Newtonsoft.JsonJsonProperty 특성을 통해 private/internal 속성 setter 및 getter를 사용할 수 있습니다.

System.Text.JsonJsonInclude 특성을 통해 private 및 internal 속성 setter 및 getter를 지원합니다. 샘플 코드는 public이 아닌 속성 접근자를 참조하세요.

기존 개체 채우기

Newtonsoft.JsonJsonConvert.PopulateObject 메서드는 새 인스턴스를 만드는 대신 JSON 문서를 클래스의 기존 인스턴스로 역직렬화합니다. System.Text.Json은 항상 매개 변수 없는 기본 public 생성자를 사용하여 대상 형식의 새 인스턴스를 만듭니다. 사용자 지정 변환기는 기존 인스턴스로 역직렬화할 수 있습니다.

속성을 바꾸지 않고 재사용

.NET 8 System.Text.Json 부터는 초기화된 속성을 바꾸는 대신 다시 사용할 수 있습니다. API 제안에서 읽을 수 있는 동작에는 몇 가지 차이점이 있습니다.

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

Newtonsoft.JsonObjectCreationHandling 설정을 사용하면 역직렬화 중에 속성의 개체를 바꾸지 않고 재사용하도록 지정할 수 있습니다. System.Text.Json은 항상 속성의 개체를 바꿉니다. 사용자 지정 변환기는 이 기능을 제공하거나 채우기 기능을 제공하는 .NET 8로 업그레이드할 수 있습니다.

setter 없이 속성 채우기

.NET 8 System.Text.Json 부터 setter가 없는 속성을 포함하여 채우기 속성을 지원합니다. 자세한 내용은 초기화된 속성 채우기를 참조하세요.

역직렬화를 수행하는 동안 Newtonsoft.Json은 속성에 setter가 없는 경우에도 개체를 컬렉션에 추가합니다. System.Text.Json은 setter가 없는 속성을 무시합니다. 사용자 지정 변환기는 이 기능을 제공하거나 읽기 전용 속성을 채울 수 있는 .NET 8로 업그레이드할 수 있습니다.

스네이크 표기법 이름 지정 정책

System.Text.Json에는 뱀 케이스에 대한 기본 제공 명명 정책이 포함되어 있습니다. 그러나 일부 입력에는 Newtonsoft.Json와(과) 몇 가지 동작 차이점이 있습니다. 다음 표에서는 JsonNamingPolicy.SnakeCaseLower 정책을 사용하여 입력을 변환할 때의 몇 가지 차이점을 보여 줍니다.

입력 Newtonsoft.Json 결과 System.Text.Json 결과
"AB1" "a_b1" "ab1"
"SHA512Managed" "sh_a512_managed" "sha512_managed"
"abc123DEF456" "abc123_de_f456" "abc123_def456"
"KEBAB-CASE" "keba_b-_case" "kebab-case"

System.Text.Json의 유일한 기본 제공 속성 명명 정책은 카멜식 대/소문자에 대한 것입니다. Newtonsoft.Json은 속성 이름을 스네이크 케이스로 변환할 수 있습니다. 사용자 지정 명명 정책은 이 기능을 제공하거나 기본 제공 뱀 대/소문자 명명 정책을 포함하는 .NET 8 이상으로 업그레이드할 수 있습니다.

System.Runtime.Serialization 특성

DataContractAttribute, DataMemberAttributeIgnoreDataMemberAttribute같은 System.Runtime.Serialization 특성을 데이터 계약으로 정의할 수 있습니다. 데이터 계약 은 서비스와 클라이언트 사이에서 교환할 데이터를 추상적으로 설명한 공식 계약입니다. 데이터 계약은 교환을 위해 직렬화되는 속성을 정확하게 정의합니다.

System.Text.Json에는 이러한 특성에 대한 기본 제공 지원이 없습니다. 그러나 .NET 7부터 사용자 지정 형식 확인자를 사용하여 지원을 추가할 수 있습니다. 샘플은 ZCS.DataContractResolver를 참조하세요.

8진수

Newtonsoft.Json은 선행 0이 있는 숫자를 8진수로 처리합니다. RFC 8259 사양에서 선행 0을 허용하지 않으므로 System.Text.Json은 선행 0을 허용하지 않습니다.

누락된 멤버 처리

JSON이 대상 형식에 없는 속성을 포함하는 경우 역직렬화 중에 예외를 발생시키도록 Newtonsoft.Json(을)를 구성할 수 있습니다. 기본적으로, System.Text.Json[JsonExtensionData] 특성을 사용하는 경우를 제외하고 JSON의 추가 속성을 무시합니다.

.NET 8 이상 버전에서는 다음 방법 중 하나를 사용하여 매핑되지 않은 JSON 속성을 건너뛰거나 허용하지 않는지 여부에 대한 기본 설정을 지정할 수 있습니다.

JsonObjectAttribute

Newtonsoft.Json에는 직렬화되는 멤버, null 값 처리 방법 및 모든 멤버가 필요한지 여부를 제어하기 위해 형식 수준에서 적용할 수 있는 JsonObjectAttribute 특성이 있습니다. System.Text.Json에는 형식에 적용할 수 있는 동일한 특성이 없습니다. null 값 처리와 같은 일부 동작의 경우 전역 JsonSerializerOptions 또는 각 속성에서 개별적으로 동일한 동작을 구성할 수 있습니다.

모든 null 속성을 무시하도록 지정하는 데 사용하는 Newtonsoft.Json.JsonObjectAttribute(으)로 다음 예제를 고려합니다.

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Person { ... }

System.Text.Json에서 모든 형식 및 속성에 대한 동작을 설정할 수 있습니다.

JsonSerializerOptions options = new()
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

string json = JsonSerializer.Serialize<Person>(person, options);

또는 각 속성에 대한 동작을 별도로 설정할 수 있습니다.

public class Person
{
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Name { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public int? Age { get; set; }
}

다음으로, 모든 멤버 속성이 JSON에 있어야 하도록 지정하는 데 Newtonsoft.Json.JsonObjectAttribute(을)를 사용하는 다음 예제를 고려합니다.

[JsonObject(ItemRequired = Required.Always)]
public class Person { ... }

C# required 한정자 또는 JsonRequiredAttribute각 속성에 추가하여 System.Text.Json의 동일한 동작을 수행할 수 있습니다. 자세한 내용은 필수 속성을 참조하세요.

public class Person
{
    [JsonRequired]
    public string? Name { get; set; }

    public required int? Age { get; set; }
}

TraceWriter

Newtonsoft.Json은 직렬화 또는 역직렬화에서 생성된 로그를 살펴보는 TraceWriter를 사용하여 디버그할 수 있습니다. System.Text.Json은 로깅을 수행하지 않습니다.

JsonDocument 및 JsonElement과 JToken(예: JObject, JArray)의 비교

System.Text.Json.JsonDocument은 기존 JSON 페이로드의 읽기 전용 DOM(문서 개체 모델)을 구문 분석하고 빌드하는 기능을 제공합니다. DOM은 JSON 페이로드의 데이터에 대한 임의 액세스를 제공합니다. 페이로드를 구성하는 JSON 요소는 JsonElement 형식을 통해 액세스할 수 있습니다. JsonElement 형식은 JSON 텍스트를 일반적인 .NET 형식으로 변환하는 API를 제공합니다. JsonDocumentRootElement 속성을 노출합니다.

.NET 6부터 System.Text.Json.Nodes 네임스페이스의 JsonNode 형식 및 기타 형식을 사용하여 기존 JSON 페이로드에서 변경 가능한 DOM을 구문 분석하고 빌드할 수 있습니다. 자세한 내용은 JsonNode 사용을 참조하세요.

JsonDocument는 IDisposable

JsonDocument는 데이터의 메모리 내 보기를 풀링된 버퍼에 빌드합니다. 따라서 Newtonsoft.JsonJObject 또는 JArray와 달리, JsonDocument 형식은 IDisposable을 구현하며 using 블록 내에서 사용해야 합니다. 자세한 내용은 JsonDocument는 IDisposable을 참조하세요.

JsonDocument는 읽기 전용

System.Text.Json DOM은 JSON 요소를 추가, 제거 또는 수정할 수 없습니다. 이렇게 설계한 이유는 성능을 향상하고 일반 JSON 페이로드 크기를 구문 분석할 때 할당을 줄이는(즉, 1MB 미만) 것입니다.

JsonElement는 공용 구조체

JsonDocumentRootElement를 JSON 요소를 포함하는 공용 구조체 형식인 JsonElement 형식의 속성으로 노출합니다. Newtonsoft.JsonJObject, JArray, JToken 등의 전용 계층형 형식을 사용합니다. JsonElement는 검색하고 열거할 수 있으며, JsonElement를 사용하여 JSON 요소를 .NET 형식으로 구체화할 수 있습니다.

.NET 6부터 JsonNode 형식 및 System.Text.Json.Nodes 네임스페이스에서 JObject, JArrayJToken에 해당하는 형식을 사용할 수 있습니다. 자세한 내용은 JsonNode 사용을 참조하세요.

하위 요소의 JsonDocument 및 JsonElement를 검색하는 방법

Newtonsoft.Json에서 JObject 또는 JArray를 사용하여 JSON 토큰을 검색하면 일부 사전을 조회하기 때문에 비교적 속도가 빠릅니다. 그에 비해, JsonElement에서 검색하려면 속성을 순차적으로 검색해야 하므로 상대적으로 느립니다(예: TryGetProperty를 사용하는 경우). System.Text.Json은 조회 시간이 아닌 초기 구문 분석 시간을 최소화하도록 설계되었습니다. 자세한 내용은 하위 요소의 JsonDocument 및 JsonElement를 검색하는 방법을 참조하세요.

Utf8JsonReader vs. JsonTextReader

System.Text.Json.Utf8JsonReaderReadOnlySpan<byte> 또는 ReadOnlySequence<byte>에서 읽어온 UTF-8 인코딩 JSON 텍스트를 위한 고성능, 저할당, 전달 전용 판독기입니다. Utf8JsonReader는 사용자 지정 구문 분석기 및 역직렬 변환기를 빌드하는 데 활용할 수 있는 하위 수준 형식입니다.

Utf8JsonReader는 ref struct

Newtonsoft.JsonJsonTextReader는 클래스입니다. Utf8JsonReader 형식은 ref 구조체라는 점에서 다릅니다. 자세한 내용은 Utf8JsonReader에 대한 ref 구조체 제한을 참조하세요.

Null 값을 null 허용 값 형식으로 읽기

Newtonsoft.Json은 자동으로 bool?를 반환하여 NullTokenType을 처리하는 ReadAsBoolean처럼 Nullable<T>을 반환하는 API를 제공합니다. 기본 제공 System.Text.Json API는 null을 허용하지 않는 값 형식만 반환합니다. 자세한 내용은 nullable 값 형식으로 null 값 읽기를 참조하세요.

JSON 읽기를 위한 다중 대상

특정 대상 프레임워크에 Newtonsoft.Json을 계속 사용해야 하는 경우 다중 대상을 지정하여 두 가지를 구현할 수 있습니다. 그러나 이 방법은 간단하지 않으며 #ifdefs 및 원본 복제가 필요합니다. 최대한 많은 코드를 공유하는 한 가지 방법은 Utf8JsonReaderNewtonsoft.Json.JsonTextReader 주위에 ref struct 래퍼를 만드는 것입니다. 이 래퍼는 동작의 차이를 격리하면서 공개 노출 영역을 통합합니다. 이렇게 하면 새 형식을 참조로 전달하는 것과 함께 변경 내용을 주로 형식 생성으로 격리할 수 있습니다. 다음은 Microsoft.Extensions.DependencyModel 라이브러리가 따르는 패턴입니다.

Utf8JsonWriter vs. JsonTextWriter

System.Text.Json.Utf8JsonWriterString, Int32DateTime과 같은 일반적인 .NET 형식에서 UTF-8 인코딩 JSON 텍스트를 쓸 수 있는 고성능 방법을 제공합니다. writer는 사용자 지정 직렬 변환기를 빌드하는 데 사용할 수 있는 하위 수준 형식입니다.

원시 값 작성

Newtonsoft.Json에는 값이 필요한 원시 JSON을 작성하는 WriteRawValue 메서드가 있습니다. System.Text.Json에는 직접적으로 해당하는 항목 Utf8JsonWriter.WriteRawValue가 없습니다. 자세한 내용은 원시 JSON 작성을 참조하세요.

JSON 형식 사용자 지정

JsonTextWriter에는 다음과 같은 설정이 있으며, Utf8JsonWriter에는 해당 기능이 없습니다.

  • QuoteChar - 문자열 값을 묶는 데 사용할 문자를 지정합니다. Utf8JsonWriter는 항상 큰따옴표를 사용합니다.
  • QuoteName - 속성 이름을 따옴표로 묶을지 여부를 지정합니다. Utf8JsonWriter는 항상 속성 이름을 따옴표로 묶습니다.

.NET 9부터는 JsonWriterOptions 구조체에서 노출하는 옵션을 사용하기 위해 Utf8JsonWriter의 들여쓰기 문자와 크기를 사용자 지정할 수 있습니다.

  • JsonWriterOptions.IndentCharacter
  • JsonWriterOptions.IndentSize

JsonTextWriter에는 다음과 같은 설정이 있으며, Utf8JsonWriter에는 해당 기능이 없습니다.

  • Indentation - 들여쓸 문자 수를 지정합니다. Utf8JsonWriter는 항상 2자 들여쓰기를 합니다.
  • IndentChar - 들여쓰기에 사용할 문자를 지정합니다. Utf8JsonWriter는 항상 공백을 사용합니다.
  • QuoteChar - 문자열 값을 묶는 데 사용할 문자를 지정합니다. Utf8JsonWriter는 항상 큰따옴표를 사용합니다.
  • QuoteName - 속성 이름을 따옴표로 묶을지 여부를 지정합니다. Utf8JsonWriter는 항상 속성 이름을 따옴표로 묶습니다.

이러한 방법으로 Utf8JsonWriter에서 생성된 JSON을 사용자 지정할 수 있는 해결 방법은 없습니다.

Timespan, Uri 또는 char 값 쓰기

JsonTextWriterTimeSpan, Urichar 값에 대한 WriteValue 메서드를 제공합니다. Utf8JsonWriter에는 이에 해당하는 메서드가 없습니다. 그 대신 이러한 값을 문자열로 포맷하고(예를 들어 ToString()을 호출) WriteStringValue를 호출할 수 있습니다.

JSON 작성을 위한 다중 대상

특정 대상 프레임워크에 Newtonsoft.Json을 계속 사용해야 하는 경우 다중 대상을 지정하여 두 가지를 구현할 수 있습니다. 그러나 이 방법은 간단하지 않으며 #ifdefs 및 원본 복제가 필요합니다. 최대한 많은 코드를 공유하는 한 가지 방법은 Utf8JsonWriterNewtonsoft.Json.JsonTextWriter 주위에 래퍼를 만드는 것입니다. 이 래퍼는 동작의 차이를 격리하면서 공개 노출 영역을 통합합니다. 이를 통해 변경 내용을 주로 형식 생성으로 격리할 수 있습니다. Microsoft.Extensions.DependencyModel 라이브러리는 다음 패턴을 따릅니다.

TypeNameHandling.All이 지원되지 않음

System.Text.Json에서 TypeNameHandling.All 동등 기능을 제외한 결정은 의도적인 것이었습니다. JSON 페이로드가 자체 형식 정보를 지정하도록 허용하는 것은 웹 애플리케이션에서 일반적인 취약성 소스입니다. 특히 Newtonsoft.JsonTypeNameHandling.All로 구성하면 원격 클라이언트가 JSON 페이로드 자체에 전체 실행 파일 애플리케이션을 포함할 수 있으므로 역직렬화 중에 웹 애플리케이션이 포함된 코드를 추출하고 실행할 수 있습니다. 자세한 내용은 Friday the 13th JSON 공격 PowerPointFriday the 13th JSON 공격 세부 정보를 참조하세요.

JSON 경로 쿼리가 지원되지 않음

JsonDocument DOM은 JSON 경로를 사용한 쿼리를 지원하지 않습니다.

JsonNode DOM에서 각 JsonNode 인스턴스에는 해당 노드에 대한 경로를 반환하는 GetPath 메서드가 있습니다. 그러나 JSON 경로 쿼리 문자열을 기반으로 쿼리를 처리하는 기본 제공 API는 없습니다.

자세한 내용은 dotnet/runtime #31068 GitHub 이슈를 참조하세요.

일부 제한을 구성할 수 없음

System.Text.Json은 최대 토큰 크기 문자(166MB) 및 base 64(125MB)와 같이 일부 값에 대해 변경할 수 없는 제한을 설정합니다. 자세한 내용은 소스 코드의 JsonConstants 및 GitHub 이슈 dotnet/runtime #39953을 참조하세요.

NaN, Infinity, -Infinity

Newtonsoft는 NaN, Infinity-Infinity JSON 문자열 토큰을 구문 분석합니다. System.Text.Json에서는 JsonNumberHandling.AllowNamedFloatingPointLiterals(을)를 사용합니다. 이 설정을 사용하는 방법에 대한 자세한 내용은 따옴표 안에 숫자 허용 또는 쓰기를 참조하세요.

추가 자료