.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 공식 문서에 버젓이 명시되어 있는 내용이었다. 진작 문서를 들여다 볼 걸...

>> 참조: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to#serialization-behavior

 

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

>> 참조: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to#deserialization-behavior

 

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)가 지원되지 않는다고 명시되어 있었던 것! ㅆㅂ

(그 밖에도 아주 유용한 정보가 많이 들어 있다. 위 링크 페이지는 책갈피에 꽂아두고 필독!)

 

 

끝.

 

 



Posted by 떼르미
,


자바스크립트를 허용해주세요!
Please Enable JavaScript![ Enable JavaScript ]