[F] Fix json

pull/131/head
Azalea 2025-03-11 02:48:31 -04:00
parent 6252cbbefe
commit 5375c3c1fa
16 changed files with 137 additions and 276 deletions

View File

@ -62,21 +62,22 @@ class ChusanController(
val token = TokenChecker.getCurrentSession()?.token?.substring(0, 6) ?: "NO-TOKEN"
log.info("Chu3 < $api : ${data.toJson()} : [$token]")
val noop = """{"returnCode":"1","apiName":"$api"}"""
if (api !in noopEndpoint && !handlers.containsKey(api)) {
log.warn("Chu3 > $api not found")
return """{"returnCode":"1","apiName":"$api"}"""
return noop
}
// Only record the counter metrics if the API is known.
Metrics.counter("aquadx_chusan_api_call", "api" to api).increment()
if (api in noopEndpoint) {
log.info("Chu3 > $api no-op")
return """{"returnCode":"1"}"""
return noop
}
return try {
Metrics.timer("aquadx_chusan_api_latency", "api" to api).recordCallable {
serialize(api, handlers[api]!!(ctx)).also {
serialize(api, handlers[api]!!(ctx) ?: noop).also {
if (api !in setOf("GetUserItemApi", "GetGameEventApi"))
log.info("Chu3 > $api : $it")
}

View File

@ -1,6 +1,5 @@
package icu.samnyan.aqua.sega.chusan.handler
import com.fasterxml.jackson.core.type.TypeReference
import ext.*
import icu.samnyan.aqua.sega.chusan.ChusanController
import icu.samnyan.aqua.sega.chusan.model.request.UpsertUserGacha
@ -29,7 +28,7 @@ fun ChusanController.cmApiInit() {
val (gachaId, placeId) = parsing { data["gachaId"]!!.int to data["placeId"]!!.int }
val u = db.userData.findByCard_ExtId(uid)() ?: return@api null
val upsertUserGacha = parsing { mapper.convert(data["cmUpsertUserGacha"], UpsertUserGacha::class.java) }
val upsertUserGacha = parsing { mapper.convert<UpsertUserGacha>(data["cmUpsertUserGacha"]!!) }
upsertUserGacha.gameGachaCardList?.let { lst ->
db.userCardPrintState.saveAll(lst.map {
@ -63,7 +62,7 @@ fun ChusanController.cmApiInit() {
}
"CMUpsertUserPrintCancel" {
val orderIdList: List<Long> = cmMapper.convert(data["orderIdList"], object : TypeReference<List<Long>>() {})
val orderIdList: List<Long> = cmMapper.convert<List<Long>>(parsing { data["orderIdList"]!! })
db.userCardPrintState.saveAll(orderIdList.mapNotNull {
// TODO: The original code by Eori writes findById but I don't think that is correct...
@ -76,8 +75,8 @@ fun ChusanController.cmApiInit() {
}
"CMUpsertUserPrintSubtract" api@ {
val userCardPrintState = cmMapper.convert(data["userCardPrintState"], UserCardPrintState::class.java)
val userItemList = cmMapper.convert(data["userItemList"], object : TypeReference<List<UserItem>>() {})
val userCardPrintState = cmMapper.convert<UserCardPrintState>(parsing { data["userCardPrintState"]!! })
val userItemList = cmMapper.convert<List<UserItem>>(parsing { data["userItemList"]!! })
val u = db.userData.findByCard_ExtId(uid)() ?: return@api null

View File

@ -17,7 +17,7 @@ fun ChusanController.upsertApiInit() {
}
"UpsertUserAll" api@ {
val req = mapper.convert(data["upsertUserAll"], UpsertUserAll::class.java)
val req = parsing { mapper.convert<UpsertUserAll>(data["upsertUserAll"]!!) }
req.run {
// UserData

View File

@ -30,7 +30,7 @@ typealias PagePost = (MutJDict) -> Unit
data class PagedProcessor(val add: JDict?, val fn: PagedHandler, var post: PagePost? = null)
// A very :3 way of declaring APIs
abstract class MeowApi(val serialize: (String, Any?) -> String) {
abstract class MeowApi(val serialize: (String, Any) -> String) {
val initH = mutableMapOf<String, SpecialHandler>()
infix operator fun String.invoke(fn: SpecialHandler) = initH.set("${this}Api", fn)
infix fun String.static(fn: () -> Any) = serialize(this, fn()).let { resp -> this { resp } }

View File

@ -68,9 +68,10 @@ class Maimai2ServletController(
@API("/{api}")
fun handle(@PathVariable api: String, @RequestBody data: Map<String, Any>, req: HttpServletRequest): Any {
logger.info("Mai2 < $api : ${data.toJson()}") // TODO: Optimize logging
val noop = """{"returnCode":1,"apiName":"com.sega.maimai2servlet.api.$api"}"""
if (api !in noopEndpoint && !handlers.containsKey(api)) {
logger.warn("Mai2 > $api not found")
return """{"returnCode":1,"apiName":"com.sega.maimai2servlet.api.$api"}"""
return noop
}
// Only record the counter metrics if the API is known.
@ -78,13 +79,13 @@ class Maimai2ServletController(
if (api in noopEndpoint) {
logger.info("Mai2 > $api no-op")
return """{"returnCode":1,"apiName":"com.sega.maimai2servlet.api.$api"}"""
return noop
}
return try {
Metrics.timer("aquadx_maimai2_api_latency", "api" to api).recordCallable {
val ctx = RequestContext(req, data.mut)
serialize(api, handlers[api]!!(ctx)).also {
serialize(api, handlers[api]!!(ctx) ?: noop).also {
logger.info("Mai2 > $api : ${it.truncate(1000)}")
}
}

View File

@ -2,8 +2,6 @@ package icu.samnyan.aqua.sega.ongeki.model.userdata;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import icu.samnyan.aqua.sega.util.jackson.UserIdSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

View File

@ -1,27 +0,0 @@
package icu.samnyan.aqua.sega.util.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import icu.samnyan.aqua.sega.general.model.Card;
import java.io.IOException;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public class AccessCodeSerializer extends StdSerializer<Card> {
public AccessCodeSerializer() {
this(null);
}
public AccessCodeSerializer(Class<Card> t) {
super(t);
}
@Override
public void serialize(Card value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(value.getLuid());
}
}

View File

@ -1,55 +1,94 @@
package icu.samnyan.aqua.sega.util.jackson
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 ext.jsonArray
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
interface IMapper {
fun write(o: Any?): String
open class IMapper(val mapper: ObjectMapper) {
fun write(o: Any): String = mapper.writeValueAsString(o)
fun <T> convert(map: Any, to: Class<T>) = mapper.convertValue(map, to)
fun <T> convert(map: Any, to: TypeReference<T>): T = mapper.convertValue(map, to)
fun <T> read(json: String, to: Class<T>) = mapper.readValue(json, to)
fun <T> read(json: String, to: TypeReference<T>) = mapper.readValue(json, to)
inline fun <reified T> convert(map: Any): T = convert(map, object : TypeReference<T>() {})
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 {
addSerializer(
LocalDateTime::class.java,
LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"))
)
addDeserializer(
LocalDateTime::class.java,
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"))
)
})
}
@Component
class BasicMapper: IMapper {
companion object {
val BASIC_MAPPER = jacksonObjectMapper().apply {
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, true)
findAndRegisterModules()
registerModule(SimpleModule().apply {
addSerializer(
LocalDateTime::class.java,
LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"))
)
addDeserializer(
LocalDateTime::class.java,
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0"))
)
})
}
class BasicMapper: IMapper(BASIC_MAPPER)
val BOOLEAN_SERIALIZER = object : StdSerializer<Boolean>(Boolean::class.java) {
override fun serialize(v: Boolean, gen: JsonGenerator, p: SerializerProvider) {
gen.writeString(v.toString())
}
override fun write(o: Any?) =
BASIC_MAPPER.writeValueAsString(o)
fun <T> read(jsonStr: String?, toClass: Class<T>?) =
BASIC_MAPPER.readValue(jsonStr, toClass)
fun <T> read(jsonStr: String?, toValueTypeRef: TypeReference<T>?) =
BASIC_MAPPER.readValue(jsonStr, toValueTypeRef)
fun <T> convert(map: Any?, toClass: Class<T>?) =
BASIC_MAPPER.convertValue(map, toClass)
fun <T> convert(map: Any?, toValueTypeRef: TypeReference<T>?) =
BASIC_MAPPER.convertValue(map, toValueTypeRef)
fun toMap(obj: Any?): LinkedHashMap<String, Any?> =
BASIC_MAPPER.convertValue(obj, object : TypeReference<LinkedHashMap<String, Any?>>() {})
}
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 {
addSerializer(
LocalDateTime::class.java,
LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
)
addDeserializer(
LocalDateTime::class.java,
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
)
addSerializer(Boolean::class.javaObjectType, BOOLEAN_SERIALIZER)
addSerializer(Boolean::class.javaPrimitiveType, BOOLEAN_SERIALIZER)
})
}
@Component
class StringMapper: IMapper(STRING_MAPPER)
class A {
var cat = ""
}
fun main(args: Array<String>) {
val json = """{"cat":"meow"}"""
val a = BasicMapper().read(json, A::class.java)
println(a.cat)
val lst = """[{"cat":"meow"}, {"cat":"meow"}]"""
val b = BasicMapper().convert(lst.jsonArray(), object : TypeReference<List<A>>() {})
println(b[0].cat)
println(b.size)
val c = BasicMapper().convert<List<A>>(lst.jsonArray())
println(c[0].cat)
}

View File

@ -1,19 +0,0 @@
package icu.samnyan.aqua.sega.util.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public class BooleanNumberDeserializer extends JsonDeserializer<Boolean> {
@Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return !"0".equals(p.getText());
}
}

View File

@ -1,26 +0,0 @@
package icu.samnyan.aqua.sega.util.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public class BooleanNumberSerializer extends StdSerializer<Boolean> {
public BooleanNumberSerializer() {
this(null);
}
public BooleanNumberSerializer(Class<Boolean> t) {
super(t);
}
@Override
public void serialize(Boolean value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeNumber(value ? 1 : 0);
}
}

View File

@ -1,23 +0,0 @@
package icu.samnyan.aqua.sega.util.jackson
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 ext.int
class BooleanToIntegerDeserializer : JsonDeserializer<Int>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Int {
return when (p.currentToken) {
JsonToken.VALUE_STRING -> when (val str = p.valueAsString.lowercase()) {
"true" -> 1
"false" -> 0
else -> str.int
}
JsonToken.VALUE_NUMBER_INT -> p.intValue
JsonToken.VALUE_TRUE -> 1
JsonToken.VALUE_FALSE -> 0
else -> throw UnsupportedOperationException("Cannot deserialize to boolean int")
}
}
}

View File

@ -1,30 +0,0 @@
package icu.samnyan.aqua.sega.util.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import java.io.IOException;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public class ByteBufSerializer extends StdSerializer<ByteBuf> {
public ByteBufSerializer() {
this(null);
}
public ByteBufSerializer(Class<ByteBuf> t) {
super(t);
}
@Override
public void serialize(ByteBuf value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(ByteBufUtil.hexDump(value));
}
}

View File

@ -0,0 +1,47 @@
package icu.samnyan.aqua.sega.util.jackson
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 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) {
gen.writeString(value.luid)
}
}
class BooleanToIntegerDeserializer : JsonDeserializer<Int>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = when (p.currentToken) {
JsonToken.VALUE_STRING -> when (val str = p.valueAsString.lowercase()) {
"true" -> 1
"false" -> 0
else -> str.int
}
JsonToken.VALUE_NUMBER_INT -> p.intValue
JsonToken.VALUE_TRUE -> 1
JsonToken.VALUE_FALSE -> 0
else -> throw UnsupportedOperationException("Cannot deserialize to boolean int")
}
}
class BooleanNumberSerializer @JvmOverloads constructor(t: Class<Boolean>? = null) : StdSerializer<Boolean>(t) {
override fun serialize(value: Boolean, gen: JsonGenerator, provider: SerializerProvider) {
gen.writeNumber(if (value) 1 else 0)
}
}
class BooleanNumberDeserializer : JsonDeserializer<Boolean>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = "0" != p.text
}
class ZonedDateTimeDeserializer : JsonDeserializer<ZonedDateTime>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = ZonedDateTime.parse(p.text)
}

View File

@ -1,53 +0,0 @@
package icu.samnyan.aqua.sega.util.jackson
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.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 ext.jsonArray
import ext.jsonMap
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@Component
class StringMapper: IMapper {
override fun write(o: Any?) = STRING_MAPPER.writeValueAsString(o)
fun <T> convert(map: Any?, toClass: Class<T>?) = STRING_MAPPER.convertValue(map, toClass)
final inline fun <reified T> convert(map: Any?) = STRING_MAPPER.convertValue(map, object : TypeReference<T>() {})
fun toMap(obj: Any?) = STRING_MAPPER.convertValue(obj, object : TypeReference<LinkedHashMap<String, Any>>() {})
companion object {
val BOOLEAN_SERIALIZER = object : StdSerializer<Boolean>(Boolean::class.java) {
override fun serialize(v: Boolean, gen: JsonGenerator, p: SerializerProvider) {
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 {
addSerializer(
LocalDateTime::class.java,
LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
)
addDeserializer(
LocalDateTime::class.java,
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
)
addSerializer(Boolean::class.javaObjectType, BOOLEAN_SERIALIZER)
addSerializer(Boolean::class.javaPrimitiveType, BOOLEAN_SERIALIZER)
})
}
}
}

View File

@ -1,27 +0,0 @@
package icu.samnyan.aqua.sega.util.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import icu.samnyan.aqua.sega.ongeki.model.userdata.UserData;
import java.io.IOException;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public class UserIdSerializer extends StdSerializer<UserData> {
public UserIdSerializer() {
this(null);
}
public UserIdSerializer(Class<UserData> t) {
super(t);
}
@Override
public void serialize(UserData value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeNumber(value.getCard().getExtId());
}
}

View File

@ -1,19 +0,0 @@
package icu.samnyan.aqua.sega.util.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.time.ZonedDateTime;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public class ZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {
@Override
public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return ZonedDateTime.parse(p.getText());
}
}