mirror of
https://github.com/hykilpikonna/AquaDX.git
synced 2026-09-12 19:35:43 -05:00
[F] Fix Jackson 3 export isSomething serialization bug
This commit is contained in:
@@ -97,11 +97,8 @@ dependencies {
|
||||
// Content validation
|
||||
implementation("org.apache.tika:tika-core:3.3.1")
|
||||
|
||||
// Import: DateTime Parsing
|
||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.22.1")
|
||||
|
||||
// Serialization
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.22.1")
|
||||
// Serialization (Jackson 3; JSR-310 support is built into jackson-databind 3.x)
|
||||
implementation("tools.jackson.module:jackson-module-kotlin:3.1.4")
|
||||
|
||||
// Testing
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:6.2.2")
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
package ext
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.core.JsonToken
|
||||
import com.fasterxml.jackson.databind.*
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonNamingStrategy
|
||||
import tools.jackson.core.JsonParser
|
||||
import tools.jackson.core.JsonToken
|
||||
import tools.jackson.databind.DeserializationContext
|
||||
import tools.jackson.databind.DeserializationFeature
|
||||
import tools.jackson.databind.ObjectMapper
|
||||
import tools.jackson.databind.PropertyNamingStrategies
|
||||
import tools.jackson.databind.ValueDeserializer
|
||||
import tools.jackson.databind.ext.javatime.ser.LocalDateTimeSerializer
|
||||
import tools.jackson.databind.json.JsonMapper
|
||||
import tools.jackson.databind.module.SimpleModule
|
||||
import tools.jackson.module.kotlin.KotlinModule
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
// Jackson
|
||||
val ACCEPTABLE_FALSE = setOf("0", "false", "no", "off", "False", "None", "null")
|
||||
val ACCEPTABLE_TRUE = setOf("1", "true", "yes", "on", "True")
|
||||
val JSON_FUZZY_BOOLEAN = SimpleModule().addDeserializer(Boolean::class.java, object : JsonDeserializer<Boolean>() {
|
||||
val JSON_FUZZY_BOOLEAN = SimpleModule().addDeserializer(Boolean::class.java, object : ValueDeserializer<Boolean>() {
|
||||
override fun deserialize(parser: JsonParser, context: DeserializationContext) = when(parser.text) {
|
||||
in ACCEPTABLE_FALSE -> false
|
||||
in ACCEPTABLE_TRUE -> true
|
||||
@@ -25,7 +30,7 @@ val JSON_FUZZY_BOOLEAN = SimpleModule().addDeserializer(Boolean::class.java, obj
|
||||
})
|
||||
val JSON_DATETIME = SimpleModule()
|
||||
.addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
|
||||
.addDeserializer(LocalDateTime::class.java, object : JsonDeserializer<LocalDateTime>() {
|
||||
.addDeserializer(LocalDateTime::class.java, object : ValueDeserializer<LocalDateTime>() {
|
||||
override fun deserialize(parser: JsonParser, context: DeserializationContext): LocalDateTime? {
|
||||
parser.readLocalDateTimeArray(context)?.let { return it }
|
||||
|
||||
@@ -45,8 +50,7 @@ fun JsonParser.readLocalDateTimeArray(context: DeserializationContext): LocalDat
|
||||
val parts = mutableListOf<Int>()
|
||||
while (nextToken() != JsonToken.END_ARRAY) {
|
||||
if (currentToken() != JsonToken.VALUE_NUMBER_INT) {
|
||||
throw context.weirdStringException(
|
||||
parts.toString(),
|
||||
return context.reportInputMismatch(
|
||||
LocalDateTime::class.java,
|
||||
"Invalid legacy date-time array",
|
||||
)
|
||||
@@ -55,8 +59,7 @@ fun JsonParser.readLocalDateTimeArray(context: DeserializationContext): LocalDat
|
||||
}
|
||||
|
||||
if (parts.size !in 5..7) {
|
||||
throw context.weirdStringException(
|
||||
parts.toString(),
|
||||
return context.reportInputMismatch(
|
||||
LocalDateTime::class.java,
|
||||
"Invalid legacy date-time array; expected 5 to 7 integer components",
|
||||
)
|
||||
@@ -68,21 +71,25 @@ fun JsonParser.readLocalDateTimeArray(context: DeserializationContext): LocalDat
|
||||
parts.getOrElse(5) { 0 }, parts.getOrElse(6) { 0 },
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
throw context.weirdStringException(
|
||||
parts.toString(),
|
||||
context.reportInputMismatch(
|
||||
LocalDateTime::class.java,
|
||||
"Invalid legacy date-time array: ${e.message}",
|
||||
)
|
||||
}
|
||||
}
|
||||
val JACKSON = jacksonObjectMapper().apply {
|
||||
setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL)
|
||||
findAndRegisterModules()
|
||||
registerModule(JSON_FUZZY_BOOLEAN)
|
||||
registerModule(JSON_DATETIME)
|
||||
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
propertyNamingStrategy = PropertyNamingStrategies.LOWER_CAMEL_CASE;
|
||||
}
|
||||
|
||||
val JACKSON: JsonMapper = JsonMapper.builder()
|
||||
.addModule(KotlinModule.Builder().build())
|
||||
.findAndAddModules()
|
||||
.addModule(JSON_FUZZY_BOOLEAN)
|
||||
.addModule(JSON_DATETIME)
|
||||
.changeDefaultPropertyInclusion {
|
||||
JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL)
|
||||
}
|
||||
.propertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE)
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.build()
|
||||
|
||||
inline fun <reified T> ObjectMapper.parse(str: Str) = readValue(str, T::class.java)
|
||||
inline fun <reified T> ObjectMapper.parse(map: Map<*, *>) = convertValue(map, T::class.java)
|
||||
// TODO: https://stackoverflow.com/q/78197784/7346633
|
||||
@@ -117,11 +124,18 @@ val JSON = Json {
|
||||
coerceInputValues = true
|
||||
}
|
||||
|
||||
// Bean for default jackson object mapper
|
||||
//@Configuration
|
||||
//class JacksonConfig {
|
||||
// @Bean
|
||||
// fun objectMapper(): ObjectMapper {
|
||||
// return JACKSON
|
||||
// }
|
||||
//}
|
||||
/**
|
||||
* Expose the shared Jackson 3 mapper as the application JsonMapper.
|
||||
*
|
||||
* Spring Boot 4 auto-configures a tools.jackson JsonMapper WITHOUT the Kotlin module
|
||||
* (Jackson2ObjectMapperBuilder's well-known-module registration no longer exists),
|
||||
* which silently renamed all `is*` boolean fields in @RestController responses
|
||||
* (e.g. isClear -> clear) and ignored Jackson 2 databind annotations.
|
||||
* Providing our own JsonMapper bean makes Boot's JacksonAutoConfiguration back off
|
||||
* (@ConditionalOnMissingBean), so the HTTP message converters use this mapper.
|
||||
*/
|
||||
@org.springframework.context.annotation.Configuration
|
||||
class JacksonConfig {
|
||||
@org.springframework.context.annotation.Bean
|
||||
fun jsonMapper(): JsonMapper = JACKSON
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package icu.samnyan.aqua.net.games
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException
|
||||
import tools.jackson.core.JacksonException
|
||||
import ext.*
|
||||
import icu.samnyan.aqua.net.Fedy
|
||||
import icu.samnyan.aqua.net.db.AquaNetUser
|
||||
@@ -124,7 +124,7 @@ abstract class ImportController<ExportModel: IExportClass<UserModel>, UserModel:
|
||||
json.parseJackson(exportClass.java)
|
||||
} catch (e: Exception) {
|
||||
val jsonError = generateSequence<Throwable>(e) { it.cause }
|
||||
.filterIsInstance<JsonProcessingException>()
|
||||
.filterIsInstance<JacksonException>()
|
||||
.firstOrNull()
|
||||
?: throw e
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package icu.samnyan.aqua.net.games
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.databind.DeserializationContext
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import tools.jackson.core.JsonParser
|
||||
import tools.jackson.databind.DeserializationContext
|
||||
import tools.jackson.databind.ValueDeserializer
|
||||
import tools.jackson.databind.module.SimpleModule
|
||||
import ext.JACKSON
|
||||
import ext.minus
|
||||
|
||||
@@ -31,7 +31,7 @@ fun String.asSqlInsert(): SqlInsert {
|
||||
}
|
||||
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST")
|
||||
val JSON_INT_LIST_STR = SimpleModule().addDeserializer(List::class.java, object : JsonDeserializer<List<Integer>>() {
|
||||
val JSON_INT_LIST_STR = SimpleModule().addDeserializer(List::class.java, object : ValueDeserializer<List<Integer>>() {
|
||||
override fun deserialize(parser: JsonParser, context: DeserializationContext) =
|
||||
try {
|
||||
val text = parser.text.trim('[', ']')
|
||||
@@ -41,6 +41,4 @@ val JSON_INT_LIST_STR = SimpleModule().addDeserializer(List::class.java, object
|
||||
400 - "Invalid list value ${parser.text}: $e" }
|
||||
})
|
||||
|
||||
val JACKSON_ARTEMIS = JACKSON.copy().apply {
|
||||
registerModule(JSON_INT_LIST_STR)
|
||||
}
|
||||
val JACKSON_ARTEMIS = JACKSON.rebuild().addModule(JSON_INT_LIST_STR).build()
|
||||
@@ -1,7 +1,7 @@
|
||||
package icu.samnyan.aqua.sega.cardmaker
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import tools.jackson.core.type.TypeReference
|
||||
import tools.jackson.databind.json.JsonMapper
|
||||
import ext.logger
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import org.springframework.web.bind.annotation.ModelAttribute
|
||||
@@ -14,7 +14,7 @@ import java.nio.charset.StandardCharsets
|
||||
@RestControllerAdvice(basePackages = ["icu.samnyan.aqua.sega.cardmaker"])
|
||||
class CardMakerControllerAdvice {
|
||||
val logger = logger()
|
||||
val mapper = ObjectMapper()
|
||||
val mapper = JsonMapper()
|
||||
|
||||
/**
|
||||
* Get the map object from json string
|
||||
|
||||
@@ -3,11 +3,11 @@ package icu.samnyan.aqua.sega.chusan.model.userdata
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.databind.DeserializationContext
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
import tools.jackson.core.JsonParser
|
||||
import tools.jackson.databind.DeserializationContext
|
||||
import tools.jackson.databind.ValueDeserializer
|
||||
import tools.jackson.databind.annotation.JsonDeserialize
|
||||
import tools.jackson.databind.annotation.JsonSerialize
|
||||
import ext.readLocalDateTimeArray
|
||||
import icu.samnyan.aqua.net.games.BaseEntity
|
||||
import icu.samnyan.aqua.net.games.IUserData
|
||||
@@ -22,7 +22,7 @@ import java.time.format.DateTimeFormatterBuilder
|
||||
import java.time.format.ResolverStyle
|
||||
import java.time.temporal.ChronoField
|
||||
|
||||
class FlexibleDateTimeDeserializer : JsonDeserializer<LocalDateTime?>() {
|
||||
class FlexibleDateTimeDeserializer : ValueDeserializer<LocalDateTime?>() {
|
||||
@Throws(IOException::class)
|
||||
public override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||
p.readLocalDateTimeArray(ctxt)?.let { return it }
|
||||
@@ -30,10 +30,10 @@ class FlexibleDateTimeDeserializer : JsonDeserializer<LocalDateTime?>() {
|
||||
val value = p.text
|
||||
return FORMATTERS.firstNotNullOfOrNull { formatter ->
|
||||
runCatching { LocalDateTime.parse(value, formatter) }.getOrNull()
|
||||
} ?: throw ctxt.weirdStringException(
|
||||
value,
|
||||
} ?: ctxt.reportInputMismatch<LocalDateTime>(
|
||||
LocalDateTime::class.java,
|
||||
"Invalid date-time; expected yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd HH:mm:ss with an optional fractional second",
|
||||
"Invalid date-time '%s'; expected yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd HH:mm:ss with an optional fractional second",
|
||||
value,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package icu.samnyan.aqua.sega.chusan.model.userdata
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import tools.jackson.databind.annotation.JsonDeserialize
|
||||
import icu.samnyan.aqua.net.games.IGenericUserMusic
|
||||
import icu.samnyan.aqua.sega.util.BooleanToIntegerDeserializer
|
||||
import jakarta.persistence.Entity
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package icu.samnyan.aqua.sega.diva.util
|
||||
|
||||
import com.fasterxml.jackson.core.json.JsonWriteFeature
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer
|
||||
import tools.jackson.core.json.JsonWriteFeature
|
||||
import tools.jackson.core.type.TypeReference
|
||||
import tools.jackson.databind.DeserializationFeature
|
||||
import tools.jackson.databind.ObjectMapper
|
||||
import tools.jackson.databind.ext.javatime.deser.LocalDateTimeDeserializer
|
||||
import tools.jackson.databind.json.JsonMapper
|
||||
import tools.jackson.databind.module.SimpleModule
|
||||
import ext.JDict
|
||||
import icu.samnyan.aqua.sega.util.BooleanNumberDeserializer
|
||||
import icu.samnyan.aqua.sega.util.BooleanNumberSerializer
|
||||
@@ -18,19 +18,19 @@ import java.time.format.DateTimeFormatter
|
||||
|
||||
@Component
|
||||
class DivaMapper {
|
||||
private val mapper: ObjectMapper = JsonMapper.builder().enable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS)
|
||||
private val mapper: ObjectMapper = JsonMapper.builder()
|
||||
.addModule(SimpleModule().apply {
|
||||
addSerializer(LocalDateTime::class.java, DivaDateTimeSerializer())
|
||||
addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0")))
|
||||
addDeserializer(ZonedDateTime::class.java, ZonedDateTimeDeserializer())
|
||||
addSerializer(Boolean::class.java, BooleanNumberSerializer())
|
||||
addSerializer(Boolean::class.javaPrimitiveType, BooleanNumberSerializer())
|
||||
addDeserializer(Boolean::class.java, BooleanNumberDeserializer())
|
||||
addDeserializer(Boolean::class.javaPrimitiveType, BooleanNumberDeserializer())
|
||||
})
|
||||
.enable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.build().apply {
|
||||
registerModule(SimpleModule().apply {
|
||||
addSerializer(LocalDateTime::class.java, DivaDateTimeSerializer())
|
||||
addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0")))
|
||||
addDeserializer(ZonedDateTime::class.java, ZonedDateTimeDeserializer())
|
||||
addSerializer(Boolean::class.java, BooleanNumberSerializer())
|
||||
addSerializer(Boolean::class.javaPrimitiveType, BooleanNumberSerializer())
|
||||
addDeserializer(Boolean::class.java, BooleanNumberDeserializer())
|
||||
addDeserializer(Boolean::class.javaPrimitiveType, BooleanNumberDeserializer())
|
||||
})
|
||||
}
|
||||
.build()
|
||||
|
||||
fun write(o: Any) = mapper.writeValueAsString(o)
|
||||
fun <T> convert(map: JDict, toClass: Class<T>) = mapper.convertValue<T>(map, toClass)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package icu.samnyan.aqua.sega.diva.util
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.databind.SerializerProvider
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer
|
||||
import tools.jackson.core.JsonGenerator
|
||||
import tools.jackson.databind.SerializationContext
|
||||
import tools.jackson.databind.ser.std.StdSerializer
|
||||
import icu.samnyan.aqua.sega.diva.PlayerPvRecordRepository
|
||||
import icu.samnyan.aqua.sega.diva.model.common.Edition
|
||||
import icu.samnyan.aqua.sega.diva.model.common.LevelInfo
|
||||
@@ -30,7 +30,7 @@ object DivaTime {
|
||||
}
|
||||
|
||||
class DivaDateTimeSerializer(t: Class<LocalDateTime>? = null) : StdSerializer<LocalDateTime>(t) {
|
||||
override fun serialize(value: LocalDateTime, gen: JsonGenerator, provider: SerializerProvider) {
|
||||
override fun serialize(value: LocalDateTime, gen: JsonGenerator, provider: SerializationContext) {
|
||||
gen.writeString(DivaTime.getString(value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package icu.samnyan.aqua.sega.general
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException
|
||||
import tools.jackson.core.JacksonException
|
||||
import ext.*
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import org.springframework.scheduling.annotation.Scheduled
|
||||
|
||||
|
||||
fun interface BaseHandler {
|
||||
@Throws(JsonProcessingException::class)
|
||||
@Throws(JacksonException::class)
|
||||
fun handle(request: Map<String, Any>): Any?
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package icu.samnyan.aqua.sega.maimai2.handler
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException
|
||||
import tools.jackson.core.JacksonException
|
||||
import ext.mapApply
|
||||
import ext.minus
|
||||
import ext.unique
|
||||
@@ -28,7 +28,7 @@ class UpsertUserAllHandler(
|
||||
) : BaseHandler {
|
||||
fun String.isValidUsername() = isNotBlank() && length <= 8
|
||||
|
||||
@Throws(JsonProcessingException::class)
|
||||
@Throws(JacksonException::class)
|
||||
override fun handle(request: Map<String, Any>): Any? {
|
||||
val upsertUserAll = mapper.convert(request, Mai2UpsertUserAll::class.java)
|
||||
val userId = upsertUserAll.userId
|
||||
|
||||
@@ -3,7 +3,7 @@ package icu.samnyan.aqua.sega.maimai2.model.userdata
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
import tools.jackson.databind.annotation.JsonSerialize
|
||||
import icu.samnyan.aqua.net.games.BaseEntity
|
||||
import icu.samnyan.aqua.net.games.IUserData
|
||||
import icu.samnyan.aqua.sega.general.IntegerListConverter
|
||||
|
||||
@@ -6,10 +6,10 @@ import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.databind.JsonSerializer
|
||||
import com.fasterxml.jackson.databind.SerializerProvider
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
import tools.jackson.core.JsonGenerator
|
||||
import tools.jackson.databind.SerializationContext
|
||||
import tools.jackson.databind.ValueSerializer
|
||||
import tools.jackson.databind.annotation.JsonSerialize
|
||||
import ext.toJson
|
||||
import icu.samnyan.aqua.net.games.BaseEntity
|
||||
import icu.samnyan.aqua.net.games.IGenericGamePlaylog
|
||||
@@ -563,8 +563,8 @@ class UserRegions : Mai2UserEntity() {
|
||||
}
|
||||
|
||||
val MAIMAI_DATETIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0")
|
||||
class MaimaiDateSerializer : JsonSerializer<LocalDateTime>() {
|
||||
override fun serialize(v: LocalDateTime, j: JsonGenerator, s: SerializerProvider) {
|
||||
class MaimaiDateSerializer : ValueSerializer<LocalDateTime>() {
|
||||
override fun serialize(v: LocalDateTime, j: JsonGenerator, s: SerializationContext) {
|
||||
j.writeString(v.format(MAIMAI_DATETIME))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package icu.samnyan.aqua.sega.ongeki.model
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
import tools.jackson.databind.annotation.JsonSerialize
|
||||
import icu.samnyan.aqua.net.games.*
|
||||
import icu.samnyan.aqua.sega.general.model.Card
|
||||
import icu.samnyan.aqua.sega.util.AccessCodeSerializer
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package icu.samnyan.aqua.sega.util
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.core.json.JsonWriteFeature
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import com.fasterxml.jackson.databind.SerializerProvider
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import tools.jackson.core.JsonGenerator
|
||||
import tools.jackson.core.json.JsonWriteFeature
|
||||
import tools.jackson.core.type.TypeReference
|
||||
import tools.jackson.databind.DeserializationFeature
|
||||
import tools.jackson.databind.ObjectMapper
|
||||
import tools.jackson.databind.cfg.EnumFeature
|
||||
import tools.jackson.databind.SerializationContext
|
||||
import tools.jackson.databind.ext.javatime.deser.LocalDateTimeDeserializer
|
||||
import tools.jackson.databind.ext.javatime.ser.LocalDateTimeSerializer
|
||||
import tools.jackson.databind.json.JsonMapper
|
||||
import tools.jackson.databind.module.SimpleModule
|
||||
import tools.jackson.databind.ser.std.StdSerializer
|
||||
import tools.jackson.module.kotlin.KotlinModule
|
||||
import ext.jsonArray
|
||||
import org.springframework.stereotype.Component
|
||||
import java.time.LocalDateTime
|
||||
@@ -28,11 +29,10 @@ open class IMapper(val mapper: ObjectMapper) {
|
||||
inline fun <reified T> read(json: String): T = read(json, object : TypeReference<T>() {})
|
||||
}
|
||||
|
||||
val BASIC_MAPPER = jacksonObjectMapper().apply {
|
||||
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, true)
|
||||
findAndRegisterModules()
|
||||
registerModule(SimpleModule().apply {
|
||||
val BASIC_MAPPER: ObjectMapper = JsonMapper.builder()
|
||||
.addModule(KotlinModule.Builder().build())
|
||||
.findAndAddModules()
|
||||
.addModule(SimpleModule().apply {
|
||||
addSerializer(
|
||||
LocalDateTime::class.java,
|
||||
LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"))
|
||||
@@ -42,7 +42,9 @@ val BASIC_MAPPER = jacksonObjectMapper().apply {
|
||||
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"))
|
||||
)
|
||||
})
|
||||
}
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.configure(EnumFeature.WRITE_ENUMS_USING_INDEX, true)
|
||||
.build()
|
||||
|
||||
@Component
|
||||
class BasicMapper: IMapper(BASIC_MAPPER)
|
||||
@@ -50,17 +52,15 @@ class BasicMapper: IMapper(BASIC_MAPPER)
|
||||
|
||||
|
||||
val BOOLEAN_SERIALIZER = object : StdSerializer<Boolean>(Boolean::class.java) {
|
||||
override fun serialize(v: Boolean, gen: JsonGenerator, p: SerializerProvider) {
|
||||
override fun serialize(v: Boolean, gen: JsonGenerator, p: SerializationContext) {
|
||||
gen.writeString(v.toString())
|
||||
}
|
||||
}
|
||||
|
||||
var STRING_MAPPER = jacksonObjectMapper().apply {
|
||||
enable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS.mappedFeature())
|
||||
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, true)
|
||||
findAndRegisterModules()
|
||||
registerModule(SimpleModule().apply {
|
||||
var STRING_MAPPER: ObjectMapper = JsonMapper.builder()
|
||||
.addModule(KotlinModule.Builder().build())
|
||||
.findAndAddModules()
|
||||
.addModule(SimpleModule().apply {
|
||||
addSerializer(
|
||||
LocalDateTime::class.java,
|
||||
LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
@@ -72,7 +72,10 @@ var STRING_MAPPER = jacksonObjectMapper().apply {
|
||||
addSerializer(Boolean::class.javaObjectType, BOOLEAN_SERIALIZER)
|
||||
addSerializer(Boolean::class.javaPrimitiveType, BOOLEAN_SERIALIZER)
|
||||
})
|
||||
}
|
||||
.enable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.configure(EnumFeature.WRITE_ENUMS_USING_INDEX, true)
|
||||
.build()
|
||||
|
||||
@Component
|
||||
class StringMapper: IMapper(STRING_MAPPER)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package icu.samnyan.aqua.sega.util
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import tools.jackson.core.type.TypeReference
|
||||
import ext.logger
|
||||
import ext.toJson
|
||||
import icu.samnyan.aqua.sega.general.model.GameEncryptionKey
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package icu.samnyan.aqua.sega.util
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.core.JsonToken
|
||||
import com.fasterxml.jackson.databind.DeserializationContext
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import com.fasterxml.jackson.databind.SerializerProvider
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer
|
||||
import tools.jackson.core.JsonGenerator
|
||||
import tools.jackson.core.JsonParser
|
||||
import tools.jackson.core.JsonToken
|
||||
import tools.jackson.databind.DeserializationContext
|
||||
import tools.jackson.databind.SerializationContext
|
||||
import tools.jackson.databind.ValueDeserializer
|
||||
import tools.jackson.databind.ser.std.StdSerializer
|
||||
import ext.int
|
||||
import icu.samnyan.aqua.sega.general.model.Card
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
class AccessCodeSerializer @JvmOverloads constructor(t: Class<Card>? = null) : StdSerializer<Card>(t) {
|
||||
override fun serialize(value: Card, gen: JsonGenerator, provider: SerializerProvider) {
|
||||
override fun serialize(value: Card, gen: JsonGenerator, provider: SerializationContext) {
|
||||
gen.writeString(value.luid)
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanToIntegerDeserializer : JsonDeserializer<Int>() {
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = when (p.currentToken) {
|
||||
class BooleanToIntegerDeserializer : ValueDeserializer<Int>() {
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = when (p.currentToken()) {
|
||||
JsonToken.VALUE_STRING -> when (val str = p.valueAsString.lowercase()) {
|
||||
"true" -> 1
|
||||
"false" -> 0
|
||||
@@ -32,16 +32,16 @@ class BooleanToIntegerDeserializer : JsonDeserializer<Int>() {
|
||||
}
|
||||
|
||||
class BooleanNumberSerializer @JvmOverloads constructor(t: Class<Boolean>? = null) : StdSerializer<Boolean>(t) {
|
||||
override fun serialize(value: Boolean, gen: JsonGenerator, provider: SerializerProvider) {
|
||||
override fun serialize(value: Boolean, gen: JsonGenerator, provider: SerializationContext) {
|
||||
gen.writeNumber(if (value) 1 else 0)
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanNumberDeserializer : JsonDeserializer<Boolean>() {
|
||||
class BooleanNumberDeserializer : ValueDeserializer<Boolean>() {
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = "0" != p.text
|
||||
}
|
||||
|
||||
class ZonedDateTimeDeserializer : JsonDeserializer<ZonedDateTime>() {
|
||||
class ZonedDateTimeDeserializer : ValueDeserializer<ZonedDateTime>() {
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = ZonedDateTime.parse(p.text)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package test
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonMappingException
|
||||
import tools.jackson.databind.DatabindException
|
||||
import ext.JACKSON
|
||||
import icu.samnyan.aqua.sega.chusan.model.userdata.Chu3UserData
|
||||
import icu.samnyan.aqua.sega.chusan.model.userdata.UserCharge
|
||||
@@ -66,7 +66,7 @@ class Chu3ImportJsonTest : StringSpec({
|
||||
}
|
||||
|
||||
"invalid CHUNITHM dates are rejected with a useful message" {
|
||||
val error = shouldThrow<JsonMappingException> {
|
||||
val error = shouldThrow<DatabindException> {
|
||||
JACKSON.readValue(
|
||||
"""{"firstPlayDate":"573-573-573T22:49:23"}""",
|
||||
Chu3UserData::class.java,
|
||||
@@ -74,6 +74,6 @@ class Chu3ImportJsonTest : StringSpec({
|
||||
}
|
||||
|
||||
error.originalMessage shouldBe
|
||||
"Cannot deserialize value of type `java.time.LocalDateTime` from String \"573-573-573T22:49:23\": Invalid date-time; expected yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd HH:mm:ss with an optional fractional second"
|
||||
"Invalid date-time '573-573-573T22:49:23'; expected yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd HH:mm:ss with an optional fractional second"
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user