접속 속성을 가진 객체를 System.Text.Json으로 역직렬화 하기

접속 속성을 가진 객체를 System.Text.Json으로 역직렬화 하기

이철우

객체지향 언어 C++의 헤더 파일이 중복이라는 생각도 C#을 시작하게 된 계기였다. 구현은 추상에 의존해야 한다는 의존성 역전의 원칙에 따라 프로그래밍하니, 접속(interface)을 많이 작성하게 된다. 한편으로 접속은 C++의 헤더 파일을 떠올리게 한다.

접속 속성을 가진 객체를 System.Text.Json으로 역직렬화 하면 예외가 발생한다. 이를 해결하는 방법을 [참고 1]에서 찾았고 이를 소개한다.

이름과 생일을 속성으로 가진 클래스 사람(Person)을 만들자. 속성 이름의 데이터 유형은 접속 IName이다. IName을 상속받아 클래스 PersonName을 만들자.

    internal interface IName
    {
        string Name { get; }
    }

    internal class PersonName : IName
    {
        public PersonName(string name)
        {
            Name = name;
        }
        public string Name { get; init; }

        public override string ToString()
        {
            return $"{Name}";
        }
    }

    internal class Person
    {
        public Person() { }
        public Person(IName name, DateOnly birthday)
        {
            Name = name;
            Birthday = birthday;
        }
        public IName Name { get; init; }
        public DateOnly Birthday { get; init; }

        public override string ToString()
        {
            return $"{Name} {Birthday}";
        }
    }

이를 system.text.json을 이용하여 직열화, 역직열화 해보자.

var person = new Person { Name = new PersonName("Hong Gil Dong"), Birthday = new DateOnly(2024, 11, 24) };

var jsonString = JsonSerializer.Serialize(person);
Console.WriteLine(jsonString);

var jsonObject = JsonSerializer.Deserialize<Person>(jsonString);
Console.WriteLine($"{jsonObject}");

역직열화에서 다음과 같은 예외가 발생한다.

Deserialization of interface or abstract types is not supported.

이를 해결하기 위해 [참고 1]의 클래스를 추가하자.

public class TypeMappingConverter<TType, TImplementation> : JsonConverter<TType>
      where TImplementation : TType
    {
        [return: MaybeNull]
        public override TType Read(
          ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
            JsonSerializer.Deserialize<TImplementation>(ref reader, options);

        public override void Write(
          Utf8JsonWriter writer, TType value, JsonSerializerOptions options) =>
            JsonSerializer.Serialize(writer, (TImplementation)value!, options);
    }

그리고 역직열화 부분을 아래와 같이 고치자.

var options = new JsonSerializerOptions
{
    Converters =
        {
            new TypeMappingConverter<IName, PersonName>()
        }
};
var jsonObject = JsonSerializer.Deserialize<Person>(jsonString, options);
Console.WriteLine($"{jsonObject}");

역직렬화가 잘 됨을 알 수 있다.

[참고 1] Serialize objects implementing interface with System.Text.Json

6 Likes