david dereba
04/21/2025, 6:19 AMpackage com.root.plugins
import com.google.gson.*
import com.google.gson.FieldNamingPolicy
import com.google.gson.Strictness
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import com.root.utility.UtilityFunctions
import io.ktor.http.ContentType
import io.ktor.serialization.gson.*
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.util.Encoder
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy
import kotlinx.serialization.modules.SerializersModule
import java.lang.reflect.Type
import java.time.Instant
import kotlinx.datetime.Instant as KtxInstant
@OptIn(ExperimentalSerializationApi::class)
fun Application.configureSerialization() {
// Create the fully configured Gson instance
val gson = GsonBuilder()
.setPrettyPrinting()
.setStrictness(Strictness.LENIENT)
.serializeNulls()
.registerTypeAdapter(UtilityFunctions.ApiResponseGsonSerializer::class.java, UtilityFunctions.ApiResponseGsonSerializer())
.registerTypeAdapter(Instant::class.java, object : JsonSerializer<Instant>, JsonDeserializer<Instant> {
override fun serialize(src: Instant, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
return JsonPrimitive(src.toString())
}
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Instant {
return Instant.parse(json.asString)
}
})
.registerTypeAdapter(Instant::class.java, object : TypeAdapter<Instant>() {
override fun write(out: JsonWriter, value: Instant?) {
if (value == null) {
out.nullValue()
} else {
out.value(value.toString()) // ISO-8601
}
}
override fun read(reader: JsonReader): Instant? {
return if (reader.peek() == JsonToken.NULL) {
reader.nextNull(); null
} else {
Instant.parse(reader.nextString())
}
}
}.nullSafe())
.registerTypeAdapter(KtxInstant::class.java, object : JsonSerializer<KtxInstant>, JsonDeserializer<KtxInstant> {
override fun serialize(src: KtxInstant, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
return JsonPrimitive(src.toString())
}
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): KtxInstant {
return KtxInstant.parse(json.asString)
}
})
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
install(ContentNegotiation) {
// Apply the pre-configured Gson instance
register(ContentType.Application.Json, GsonConverter(gson))
json(Json {
isLenient = true
prettyPrint = true
encodeDefaults = true
ignoreUnknownKeys = true
coerceInputValues = true
namingStrategy = JsonNamingStrategy.SnakeCase
serializersModule = SerializersModule {
contextual(KtxInstant::class) {
KotlinxInstantSerializer
}
}
})
}
routing {
get("/json/kotlinx-serialization") {
call.respond(mapOf("hello" to "world"))
}
get("/json/gson") {
call.respond(mapOf("hello" to "world"))
}
}
}
// And define your serializer
object KotlinxInstantSerializer : KSerializer<KtxInstant> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("KotlinxInstant", PrimitiveKind.STRING)
override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: KtxInstant) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): KtxInstant {
return KtxInstant.parse(decoder.decodeString())
}
}
then this is the full error log
2025-04-21 09:13:55.340 [eventLoopGroupProxy-4-1] ERROR i.k.server.application.Application - Error occurred: JsonIOException || Failed making field 'java.time.Instant#seconds' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type.
See <https://github.com/google/gson/blob/main/Troubleshooting.md#reflection-inaccessible>
com.google.gson.JsonIOException: Failed making field 'java.time.Instant#seconds' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type.
See <https://github.com/google/gson/blob/main/Troubleshooting.md#reflection-inaccessible>
at com.google.gson.internal.reflect.ReflectionHelper.makeAccessible(ReflectionHelper.java:76)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:388)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:161)
at com.google.gson.Gson.getAdapter(Gson.java:628)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:201)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:395)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:161)
at com.google.gson.Gson.getAdapter(Gson.java:628)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:201)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:395)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:161)
at com.google.gson.Gson.getAdapter(Gson.java:628)
at com.google.gson.Gson.toJson(Gson.java:928)
at com.google.gson.Gson.toJsonTree(Gson.java:802)
at com.google.gson.Gson.toJsonTree(Gson.java:779)
at com.root.routing.LeasesRoutesKt.handleLeaseCreation(LeasesRoutes.kt:53)
at com.root.routing.LeasesRoutesKt.access$handleLeaseCreation(LeasesRoutes.kt:1)
at com.root.routing.LeasesRoutesKt$handleLeaseCreation$1.invokeSuspend(LeasesRoutes.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.internal.DispatchedContinuation.resumeWith(DispatchedContinuation.kt:197)
at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:146)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:120)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:11)
at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:70)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final long java.time.Instant.seconds accessible: module java.base does not "opens java.time" to unnamed module @10315254
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
at com.google.gson.internal.reflect.ReflectionHelper.makeAccessible(ReflectionHelper.java:68)
What could be the issue. please assist.hfhbd
04/21/2025, 7:30 AMhfhbd
04/21/2025, 7:34 AMdavid dereba
04/21/2025, 7:40 AMkotlinx-serialization
later on after running into issues with Gson. Thanks for the guidance! I’ll give kotlinx-serialization
a proper try now.Pim
04/21/2025, 8:54 AMdavid dereba
04/23/2025, 9:57 PM