polymorphic type으로 request를 받는 DTO를 어떻게 serialize/deserialize 할 것인가를 찾다가 ChatGPT가 @JsonTypeInfo를 쓰는 것을 보고 이게 뭐지?로부터 시작되었다
우리 시스템을 간단하게 설명해보자면,, 그림판으로 그려봤는데 이런식으로 dto를 주고받고 그럿더랫다..
GET은 무난히 Serialize했는데, POST는 request로부터 어떻게 딱 맞는 Type{N}DTO로 Deserialize하지?
고민 끝에 @JsonTypeInfo로 Deserialize할 수 있었다!!
여기서 사용할 어노테이션은 세 개로 간추려 볼 수 있다.
@JsonTypeInfo – 무슨 타입 정보를 serialization에 포함할 것인지 나타낸다
Id: use
: 클래스/하위 클래스 인스턴스에 대한 타입 정보를 직렬화(serialize)할 때 사용할 타입 메타데이터의 종류, 역직렬화(deserialize) 시 예상되는 내용을 지정As: include
- default값은 JsonTypeInfo.As.PROPERTYString: property
- default값은 ""Class<?> defaultImpl
- default값은 JsonTypeInfo.classBoolean: visible
- default값은 false@JsonSubTypes – Annotation을 붙인 Sub Type들을 나타낸다. 서브클래스들을 등록한다고 생각해도 좋다.
@JsonTypeName – Annotation 클래스에 사용할 타입 이름을 붙인다.
동물원에 있는 동물들 종에 대한 클래스를 만들고, 그 동물들이 상속하는 '동물' class를 만들고자한다.
Animal 클래스에는 @JsonTypeInfo로 Animal을 상속할 동물들을 어떻게 사용할 건지 정의해주었고, @JsonSubTypes로 서브 타입들을 명시해주었다.
Animal 클래스를 상속하는 Dog, Cat 클래스에는 @JsonTypeName으로 property type을 구별할 이름 값을 지정해준다.
public class Zoo {
public Animal animal;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public static class Animal {
public String name;
}
@JsonTypeName("dog")
public static class Dog extends Animal {
public double barkVolume;
}
@JsonTypeName("cat")
public static class Cat extends Animal {
boolean likesCream;
public int lives;
}
}
동물원에 Dog 인스턴스를 하나 생성하고자 한다.
@Test
public void whenSerializingPolymorphic_thenCorrect()
throws JsonProcessingException {
Zoo.Dog dog = new Zoo.Dog("lacy");
Zoo zoo = new Zoo(dog);
String result = new ObjectMapper()
.writeValueAsString(zoo);
assertThat(result, containsString("type"));
assertThat(result, containsString("dog"));
}
아래는 결과! type이 dog으로 Dog 인스턴스가 생긴 것을 알 수 있다.
{
"animal": {
"type": "dog",
"name": "lacy",
"barkVolume": 0
}
}
이젠 json을 input으로 넣겠다.
{
"animal":{
"name":"lacy",
"type":"cat"
}
}
결과! type이 cat임을 알고, Cat 인스턴스를 생성하였다.
@Test
public void whenDeserializingPolymorphic_thenCorrect()
throws IOException {
String json = "{\"animal\":{\"name\":\"lacy\",\"type\":\"cat\"}}";
Zoo zoo = new ObjectMapper()
.readerFor(Zoo.class)
.readValue(json);
assertEquals("lacy", zoo.animal.name);
assertEquals(Zoo.Cat.class, zoo.animal.getClass());
}
-> visible을 true로 바꿔준다. 기본값이 false였기에, TypeDeserializer에서 제거하기 때문에 null이 되어버린다.
stackoverflow 동일 문제
참고 링크:
https://www.baeldung.com/jackson-annotations#jackson-polymorphic-type-handling-annotations더 알고 싶다면..
https://www.baeldung.com/jackson-advanced-annotations#bd-overview