Enum and Annotation
enum class MaskType {
NAME, CONTACT
}
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class JsonMask(val type: MaskType)
data class UserInfo(
@JsonMask(MaskType.NAME)
val name: String,
@JsonMask(MaskType.CONTACT)
val phoneNumber: String
)
Custom Serializers
class NameMaskingSerializer : JsonSerializer<Any>() {
override fun serialize(value: Any?, gen: JsonGenerator, serializers: SerializerProvider) {
val maskedValue = (value as? String)?.let {
if (it.length > 1) it.first() + "*".repeat(it.length - 1) else it
} ?: ""
gen.writeString(maskedValue)
}
}
class ContactMaskingSerializer : JsonSerializer<Any>() {
override fun serialize(value: Any?, gen: JsonGenerator, serializers: SerializerProvider) {
val maskedValue = (value as? String)?.let {
it.take(it.length - 4) + "*".repeat(4)
} ?: ""
gen.writeString(maskedValue)
}
}
Serializer Module
Jackson의 Module 클래스를 상속하여 만들어진 클래스이며 해당 모듈은 커스텀한 serializers와 deserializers을 등록할 수 있게 한다.
Jackson에서 제공하는 추상 클래스이며 Bean의 직렬화 과정에서 커스텀할 수 있는 API를 정의한다.
class MaskingModule : SimpleModule() {
init {
setSerializerModifier(object : BeanSerializerModifier() {
override fun changeProperties(
config: com.fasterxml.jackson.databind.SerializationConfig?,
beanDesc: com.fasterxml.jackson.databind.BeanDescription?,
beanProperties: MutableList<BeanPropertyWriter>
): MutableList<BeanPropertyWriter> {
beanProperties.forEach { writer ->
val field: Field? = when (val member = writer.member) {
is AnnotatedField -> member.annotated
is AnnotatedMethod -> member.declaringClass.getDeclaredField(writer.name)
else -> null
}
field?.getAnnotation(JsonMask::class.java)?.let { annotation ->
val serializer: JsonSerializer<Any> = when (annotation.type) {
MaskType.NAME -> NameMaskingSerializer()
MaskType.CONTACT -> ContactMaskingSerializer()
}
writer.assignSerializer(serializer)
}
}
return beanProperties
}
})
}
}
Test
fun main() {
val objectMapper = ObjectMapper().apply {
registerKotlinModule()
registerModule(MaskingModule())
}
val userInfo = UserInfo(
name = "홍길동",
"010-1234-5678",
)
val result = objectMapper.writeValueAsString(userInfo)
println(result) // {"name":"홍**","phoneNumber":"010-1234-****"}
}