.NET Framework에서는 NuGet으로 받아야만 쓸 수 있는 3rd party 프레임워크에 불과한 Newtonsoft.Json이 워낙 이런저런 패키지들에 깊숙히 침투해서 중구난방으로 사용되고 있었기 때문에 .NET Core로 마이그레이션할 때 System.Text.Json으로 바꾸고 Newtonsoft의 잔재를 걷어내는 일은 참으로 지난한 일이 되었다...
즐겨 사용하는 패키지인 PushSharp을 역시 .NET Core에서도 사용하고 있는데, 내부적으로 Newtonsoft를 쓰고 있어서 하는 수 없이 최신 소스코드를 GitHub에서 내려 받은 다음 일일이 System.Text.Json으로 수정해서 써야 했고...
PushSharp에서 아직까지 제대로 지원하지 않는 APNS P8 토큰 기반 HTTP/2 통신 관련 기능을 직접 구현할 때 필요한 패키지인 Jose.JWT도 역시 내부적으로 Newtonsoft를 쓰고 있어서 마찬가지로 소스코드 째로 받아 수정해야 했으며...
기타 등등.
수정하고 테스트하면서 알게 된 것들 몇 가지를 적어 본다.
1. ASP.NET Core 프로젝트의 Startup.cs에서 전역 설정한 JsonOptions는... JsonSerializer를 직접 사용하는 경우에는 적용되지 않는다. 전역 설정이라서 당연히 여기저기 다 적용되겠거니 싶었지만, 아니었다. 해당 전역 설정은 Controller로 요청하거나 응답하는 경우에만 적용되는 설정이다! (Controller 응답을 클래스 객체로 리턴할 때, 그리고 요청을 Json 객체로 받을 때 내부적으로 자동 Serialize/Deserialize되는데 이 때에만 적용된다.)
services.AddControllersWithViews()
.AddJsonOptions(options =>
{
// Json 날짜 변환 형식 추가
options.JsonSerializerOptions.Converters.Add(new DateTimeConverterEx());
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
// Json 속성이 소문자로 강제 변환되는 것을 방지(클래스 정의대로)
options.JsonSerializerOptions.PropertyNamingPolicy = null;
// null Property는 생략함
options.JsonSerializerOptions.IgnoreNullValues = true;
// 한글이 인코딩(깨짐)되지 않게
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
});
보통 이런 식으로 전역 설정을 해놓고 쓰는데...
MyClass cls = ...;
String objString = JsonSerializer.Serialize(cls);
MyClass cls2 = JsonSerializer.Deserialize<MyClass>(objString);
이렇게 코드에서 직접 JsonSerializer를 사용할 때는 위 JsonOptions 전역 설정이 적용되지 않는다. 왜냐하면, JsonSerializer.Serialize()와 JsonSerializer.Deserialize() 함수를 잘 들여다 보면 알 수 있지만 두 번째 인수인 JsonSerializerOptions 값이 생략되면 그 기본값은 null이 된다! ㄷㄷㄷ 프레임워크 기본값으로 초기화되는 것.
즉, Startup.cs의 전역 설정 외에 따로 기본값을 정의해서 매번 JsonSerializer 코드를 쓸 때마다 적용하지 않으면 프레임워크 초기값이 적용되는 것이다. 중복이지만... 매번 코드를 작성해서 적용해야 한다. 어쩔 수 없다.
나는 고민 끝에 가장 간단한 방법으로 JsonSerializerOptions에서 끝에 s만 뺀 정적static 클래스를 만들고 정적static 읽기 전용 속성Property인 Default를 하나 정의해서 사용하는 방법을 썼다. (JsonSerializerOption이 .NET Core 내장 클래스인 것처럼 속이기? ㅎㅎ)
public static class JsonSerializerOption
{
public static JsonSerializerOptions Default
{
get
{
return new JsonSerializerOptions
{
// Json 속성이 소문자로 강제 변환되는 것을 방지(클래스 정의대로)
PropertyNamingPolicy = null,
// null Property는 생략함
IgnoreNullValues = true,
// 한글이 인코딩(깨짐)되지 않게
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
}
}
}
JsonSerializerOptions 객체는 Singleton으로 쓸 수 없고 Serialize/Deserialize 할 때마다 매번 new 해서 새로 만들어야 한다. 이 또한 어쩔 수 없다...
전역 설정에서의 중복을 피하기 위해 Startup.cs 코드는 아래와 같이 바뀌었다.
services.AddControllersWithViews()
.AddJsonOptions(options =>
{
// Json 날짜 변환 형식 추가
options.JsonSerializerOptions.Converters.Add(new DateTimeConverterEx());
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
// JsonSerializerOptions 기본값 사용자 정의
var serializerOptions = JsonSerializerOption.Default;
options.JsonSerializerOptions.PropertyNamingPolicy = serializerOptions.PropertyNamingPolicy;
options.JsonSerializerOptions.IgnoreNullValues = serializerOptions.IgnoreNullValues;
options.JsonSerializerOptions.Encoder = serializerOptions.Encoder;
});
2. Newtonsoft와 달리 System.Text.Json으로 Serialize/Deserialize할 때는 클래스 멤버 변수(Field)가 아닌 속성(Property)을 사용해야 한다. 즉, get; set;이 없는 클래스 멤버 변수(Field)는 비록 public이라 하더라도 지원되지 않는다.
public class MyClass
{
public string id;
public string name;
}
이렇게 변수(Field)로 되어 있으면 전혀 Serialize/Deserialize되지 않는다. 그냥 텅 빈 값만 덩그러니...
public class MyClass
{
public string id { get; set; }
public string name { get; set; }
}
이렇게 속성(Property)으로 수정해서 써야 한다. 처음엔 이 내용을 몰라서... 왜 정상적으로 동작하지 않는지 디버깅하느라 눈 빠질 뻔 했다. 알고보니 Microsoft 공식 문서에 버젓이 명시되어 있는 내용이었다. 진작 문서를 들여다 볼 걸...
How to serialize and deserialize JSON using C# - .NET
This article shows you how to use the System.Text.Json namespace to serialize to and deserialize from JSON in .NET. It includes sample code.
docs.microsoft.com
How to serialize and deserialize JSON using C# - .NET
This article shows you how to use the System.Text.Json namespace to serialize to and deserialize from JSON in .NET. It includes sample code.
docs.microsoft.com
위 두 부분에서 모두 클래스 멤버 변수(Field)가 지원되지 않는다고 명시되어 있었던 것! ㅆㅂ
(그 밖에도 아주 유용한 정보가 많이 들어 있다. 위 링크 페이지는 책갈피에 꽂아두고 필독!)
끝.
'Tech: > Server·IIS' 카테고리의 다른 글
[docker] MariaDB 10.4 my.cnf 인식 오류 (0) | 2022.08.03 |
---|---|
ARR: URL 재작성(rewrite) 팁 (0) | 2020.12.15 |
Docker 컨테이너 실행순서 조정하기 (0) | 2020.08.12 |
Docker 컨테이너 시간을 UTC+09:00로 설정하기 (0) | 2020.08.11 |
Windows Server Core 유용한 팁 (0) | 2020.07.09 |