116 lines
4.1 KiB
Kotlin

package roomescape.common.config
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer
import com.fasterxml.jackson.module.kotlin.kotlinModule
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import roomescape.common.exception.CommonErrorCode
import roomescape.common.exception.RoomescapeException
import java.time.*
import java.time.format.DateTimeFormatter
@Configuration
class JacksonConfig {
companion object {
private val ISO_OFFSET_DATE_TIME_FORMATTER: DateTimeFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX")
}
@Bean
fun objectMapper(): ObjectMapper = ObjectMapper()
.registerModule(javaTimeModule())
.registerModule(dateTimeModule())
.registerModule(kotlinModule())
.registerModule(longIdModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
private fun javaTimeModule(): JavaTimeModule = JavaTimeModule()
.addSerializer(
LocalDate::class.java,
LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE)
)
.addDeserializer(
LocalDate::class.java,
LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE)
)
.addSerializer(
LocalTime::class.java,
LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm"))
)
.addDeserializer(
LocalTime::class.java,
LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm"))
) as JavaTimeModule
private fun longIdModule(): SimpleModule {
val simpleModule = SimpleModule()
simpleModule.addSerializer(Long::class.java, LongToStringSerializer())
simpleModule.addDeserializer(Long::class.java, StringToLongDeserializer())
return simpleModule
}
private fun dateTimeModule(): SimpleModule {
val simpleModule = SimpleModule()
simpleModule.addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer())
simpleModule.addSerializer(OffsetDateTime::class.java, OffsetDateTimeSerializer())
return simpleModule
}
class LongToStringSerializer : JsonSerializer<Long>() {
override fun serialize(value: Long?, gen: JsonGenerator, serializers: SerializerProvider) {
if (value == null) {
gen.writeNull()
} else {
gen.writeString(value.toString())
}
}
}
class StringToLongDeserializer : JsonDeserializer<Long>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Long? {
val text = p.text
if (text.isNullOrBlank()) {
return null
}
return try {
text.toLong()
} catch (_: NumberFormatException) {
throw RoomescapeException(CommonErrorCode.INVALID_INPUT_VALUE)
}
}
}
class LocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
override fun serialize(
value: LocalDateTime,
gen: JsonGenerator,
serializers: SerializerProvider
) {
value.atZone(ZoneId.systemDefault())
.toOffsetDateTime()
.also {
gen.writeString(it.format(ISO_OFFSET_DATE_TIME_FORMATTER))
}
}
}
class OffsetDateTimeSerializer : JsonSerializer<OffsetDateTime>() {
override fun serialize(
value: OffsetDateTime,
gen: JsonGenerator,
serializers: SerializerProvider
) {
gen.writeString(value.format(ISO_OFFSET_DATE_TIME_FORMATTER))
}
}
}