feat : 어노테이션 기반의 직렬화 예제 코드 추가

This commit is contained in:
banjjoknim
2022-03-13 20:41:52 +09:00
parent abcf7da69e
commit 9f9a5644fc
6 changed files with 122 additions and 17 deletions

View File

@@ -1,7 +0,0 @@
package com.banjjoknim.playground.jackson.common
data class Car(
val name: String,
val price: Int = 0,
val owner: Owner
)

View File

@@ -0,0 +1,33 @@
package com.banjjoknim.playground.jackson.common
import com.banjjoknim.playground.jackson.jsonserialize.UsingJsonSerializeAnnotationCarSerializer
import com.fasterxml.jackson.databind.annotation.JsonSerialize
data class Car(
val name: String,
val price: Int = 10000000,
val owner: Owner = Owner()
)
data class CarUsingNoAnnotation(
val name: String = "banjjoknim",
val secret: String = "secret",
val price: Int = 10000000,
val owner: Owner = Owner()
)
data class CarUsingJsonSerializeAnnotation(
val name: String = "banjjoknim",
@JsonSerialize(using = UsingJsonSerializeAnnotationCarSerializer::class)
val secret: String = "secret",
val price: Int = 10000000,
val owner: Owner = Owner()
)
data class CarUsingSecretAnnotation(
val name: String = "banjjoknim",
@field:Secret
val secret: String = "secret",
val price: Int = 10000000,
val owner: Owner = Owner()
)

View File

@@ -1,3 +1,3 @@
package com.banjjoknim.playground.jackson.common
data class Owner(val name: String, val age: Int)
data class Owner(val name: String = "ban", val age: Int = 30)

View File

@@ -9,6 +9,6 @@ import com.fasterxml.jackson.annotation.JacksonAnnotation
* @see com.fasterxml.jackson.annotation.JacksonAnnotationsInside
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY)
@JacksonAnnotation // Jackson 에서 이 어노테이션을 인식할 수 있게 만들어준다.
@Target(AnnotationTarget.FIELD) // 현재 상황에서는 PROPERTY 로 적용할 경우 제대로 적용되지 않는다. 아마 어노테이션 자체가 자바 기반으로 사용되어 PROPERTY 를 인식하지 못하는 것 같다(자바에서는 PROPERTY 타입을 사용할 수 없음).
@JacksonAnnotation // NOTE: important; MUST be considered a 'Jackson' annotation to be seen(or recognized otherwise via AnnotationIntrospect.isHandled())
annotation class Secret

View File

@@ -1,8 +1,12 @@
package com.banjjoknim.playground.jackson.jsonserialize
import com.banjjoknim.playground.jackson.common.Car
import com.banjjoknim.playground.jackson.common.Secret
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.introspect.Annotated
import com.fasterxml.jackson.databind.introspect.AnnotatedMember
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector
import com.fasterxml.jackson.databind.ser.std.StdSerializer
/**
@@ -16,9 +20,12 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer
*
* JsonSerializer<T> 만 확장할 경우엔 애노테이션 정보를 얻을 수 없다. 추가적으로 ContextualSerializer 인터페이스를 구현해주면 createContextual() 메서드를 구현해줘야 하는데 두번째 인자로 넘어오는 BeanProperty 를 이용해 애노테이션 정보를 구할 수 있다.
*
* ContexutalSerializer 를 사용하는 방법은 [TestContextualSerialization](https://github.com/FasterXML/jackson-databind/blob/master/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualSerialization.java) 참고하도록 한다.
*
* Custom Serializer 가 JsonSerializer<T> 와 ContextualSerialier 를 모두 구현할 경우 createContextual() 함수가 먼저 호출된다.
*
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer
* @see com.fasterxml.jackson.databind.ser.std.StringSerializer
* @see com.fasterxml.jackson.databind.ser.ContextualSerializer
*/
class CarSerializer : StdSerializer<Car>(Car::class.java) {
@@ -64,3 +71,40 @@ class CarNameOwnerNameSerializer : StdSerializer<Car>(Car::class.java) {
gen.writeEndObject()
}
}
class UsingJsonSerializeAnnotationCarSerializer : StdSerializer<String>(String::class.java) {
override fun serialize(value: String, gen: JsonGenerator, provider: SerializerProvider) {
gen.writeString("****")
}
}
class SecretAnnotationSerializer : StdSerializer<String>(String::class.java) {
override fun serialize(value: String, gen: JsonGenerator, provider: SerializerProvider) {
gen.writeString("****")
}
}
class SecretAnnotationIntrospector : JacksonAnnotationIntrospector() {
/**
*
* `@JsonIgnore` 를 적용했을 때 무시할지 여부를 판단하는 함수이다.
*
* 따라서 직렬화 / 역직렬화시 무시하고 싶은 프로퍼티가 있다면 이 함수를 override 하면 된다.
*/
override fun hasIgnoreMarker(m: AnnotatedMember): Boolean {
return super.hasIgnoreMarker(m)
}
/**
* 특정 프로퍼티에 대해 어떤 Serializer 를 사용할 것인지 결정하는 함수이다.
*
* 따라서 특정 조건에 따라 직렬화를 하고싶다면 이 함수를 override 하면 된다.
*/
override fun findSerializer(a: Annotated): Any? {
val annotation = a.getAnnotation(Secret::class.java)
if (annotation != null) {
return SecretAnnotationSerializer()
}
return super.findSerializer(a) // 기존 JacksonAnnotationIntrospector 의 것을 사용한다.
}
}

View File

@@ -1,11 +1,9 @@
package com.banjjoknim.playground.jsonserialize
package com.banjjoknim.playground.jackson.jsonserialize
import com.banjjoknim.playground.jackson.jsonserialize.CarNameOwnerNameSerializer
import com.banjjoknim.playground.jackson.jsonserialize.CarNameOwnerSerializer
import com.banjjoknim.playground.jackson.jsonserialize.CarNameSerializer
import com.banjjoknim.playground.jackson.jsonserialize.CarPriceSerializer
import com.banjjoknim.playground.jackson.jsonserialize.CarSerializer
import com.banjjoknim.playground.jackson.common.Car
import com.banjjoknim.playground.jackson.common.CarUsingJsonSerializeAnnotation
import com.banjjoknim.playground.jackson.common.CarUsingNoAnnotation
import com.banjjoknim.playground.jackson.common.CarUsingSecretAnnotation
import com.banjjoknim.playground.jackson.common.Owner
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
@@ -25,13 +23,16 @@ import org.junit.jupiter.api.Test
* @see com.fasterxml.jackson.databind.ser.std.BeanSerializerBase
* @see com.fasterxml.jackson.databind.ser.BeanPropertyWriter
*/
class CarSerializerTest {
class CarSerializersTest {
private lateinit var mapper: ObjectMapper
companion object {
private val owner = Owner("ban", 30)
private val car = Car("banjjoknim", 10_000_000, owner)
private val carUsingNoAnnotation = CarUsingNoAnnotation()
private val carUsingJsonSerializeAnnotation = CarUsingJsonSerializeAnnotation()
private val carUsingSecretAnnotation = CarUsingSecretAnnotation()
}
@BeforeEach
@@ -122,5 +123,39 @@ class CarSerializerTest {
// then
assertThat(result).isEqualTo("""{"name":"banjjoknim","owner":{"name":"ban"}}""")
}
@Test
fun `아무 어노테이션도 적용하지 않고 직렬화한다`() {
// given
// when
val actual = mapper.writeValueAsString(carUsingNoAnnotation)
// then
assertThat(actual).isEqualTo("""{"name":"banjjoknim","secret":"secret","price":10000000,"owner":{"name":"ban","age":30}}""")
}
@Test
fun `@JsonSerialize 어노테이션을 적용하여 직렬화한다`() {
// given
// when
val actual = mapper.writeValueAsString(carUsingJsonSerializeAnnotation)
// then
assertThat(actual).isEqualTo("""{"name":"banjjoknim","secret":"****","price":10000000,"owner":{"name":"ban","age":30}}""")
}
@Test
fun `@Secret 어노테이션을 적용하여 직렬화한다`() {
// given
mapper.setAnnotationIntrospector(SecretAnnotationIntrospector())
// when
val actual = mapper.writeValueAsString(carUsingSecretAnnotation)
// then
assertThat(actual).isEqualTo("""{"name":"banjjoknim","secret":"****","price":10000000,"owner":{"name":"ban","age":30}}""")
}
}
}