From 9f9a5644fce7243cc249410ec3e0f1f8652dcedf Mon Sep 17 00:00:00 2001 From: banjjoknim Date: Sun, 13 Mar 2022 20:41:52 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EA=B8=B0=EB=B0=98=EC=9D=98=20=EC=A7=81=EB=A0=AC?= =?UTF-8?q?=ED=99=94=20=EC=98=88=EC=A0=9C=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/banjjoknim/playground/jackson/common/Car.kt | 7 --- .../com/banjjoknim/playground/jackson/common/Cars.kt | 33 +++++++++++++ .../com/banjjoknim/playground/jackson/common/Owner.kt | 2 +- .../com/banjjoknim/playground/jackson/common/Secret.kt | 4 +- .../playground/jackson/jsonserialize/CarSerializers.kt | 44 +++++++++++++++++ .../jsonserialize/CarSerializersTest.kt} | 49 ++++++++++++++++--- 6 files changed, 122 insertions(+), 17 deletions(-) delete mode 100644 놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Car.kt create mode 100644 놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Cars.kt rename 놀이터(예제 코드 작성)/jackson/src/test/kotlin/com/banjjoknim/playground/{jsonserialize/CarSerializerTest.kt => jackson/jsonserialize/CarSerializersTest.kt} (69%) diff --git a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Car.kt b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Car.kt deleted file mode 100644 index 8bcb4be..0000000 --- a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Car.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.banjjoknim.playground.jackson.common - -data class Car( - val name: String, - val price: Int = 0, - val owner: Owner -) diff --git a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Cars.kt b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Cars.kt new file mode 100644 index 0000000..bcde090 --- /dev/null +++ b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Cars.kt @@ -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() +) diff --git a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Owner.kt b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Owner.kt index 58a152e..699107d 100644 --- a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Owner.kt +++ b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Owner.kt @@ -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) diff --git a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Secret.kt b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Secret.kt index 0b55fd6..0c9dcc2 100644 --- a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Secret.kt +++ b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/common/Secret.kt @@ -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 diff --git a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/jsonserialize/CarSerializers.kt b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/jsonserialize/CarSerializers.kt index 8b3f4fe..ed02fcb 100644 --- a/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/jsonserialize/CarSerializers.kt +++ b/놀이터(예제 코드 작성)/jackson/src/main/kotlin/com/banjjoknim/playground/jackson/jsonserialize/CarSerializers.kt @@ -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 만 확장할 경우엔 애노테이션 정보를 얻을 수 없다. 추가적으로 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 와 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::class.java) { @@ -64,3 +71,40 @@ class CarNameOwnerNameSerializer : StdSerializer(Car::class.java) { gen.writeEndObject() } } + +class UsingJsonSerializeAnnotationCarSerializer : StdSerializer(String::class.java) { + override fun serialize(value: String, gen: JsonGenerator, provider: SerializerProvider) { + gen.writeString("****") + } +} + +class SecretAnnotationSerializer : StdSerializer(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 의 것을 사용한다. + } +} diff --git a/놀이터(예제 코드 작성)/jackson/src/test/kotlin/com/banjjoknim/playground/jsonserialize/CarSerializerTest.kt b/놀이터(예제 코드 작성)/jackson/src/test/kotlin/com/banjjoknim/playground/jackson/jsonserialize/CarSerializersTest.kt similarity index 69% rename from 놀이터(예제 코드 작성)/jackson/src/test/kotlin/com/banjjoknim/playground/jsonserialize/CarSerializerTest.kt rename to 놀이터(예제 코드 작성)/jackson/src/test/kotlin/com/banjjoknim/playground/jackson/jsonserialize/CarSerializersTest.kt index a5641f0..8872faa 100644 --- a/놀이터(예제 코드 작성)/jackson/src/test/kotlin/com/banjjoknim/playground/jsonserialize/CarSerializerTest.kt +++ b/놀이터(예제 코드 작성)/jackson/src/test/kotlin/com/banjjoknim/playground/jackson/jsonserialize/CarSerializersTest.kt @@ -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}}""") + } } }