はじめに
この記事はなんか書く太郎三日目の記事です
二日目 - 四日目
Kotlin Serialization 使えば Kotlin/JS で作ったフロントエンドでも同じ型を使い回せて便利そうだなと思って軽い気持ちで試してみたので雑にまとめときます
MessageBodyWriter の実装を書く
Jersey の探索パッケージの下に, Content-Type が application/json のときは kotlinx.serialization.json.Json
をつかってハンドラの返し値を JSON にシリアライズするやつをおきます
private infix fun Class<*>.isA(superClazz: Class<*>): Boolean =
superClazz.isAssignableFrom(this)
@Provider
@Produces("application/json")
class KotlinSerializableWriter : MessageBodyWriter<Any> {
override fun isWriteable(
type: Class<*>?, genericType: Type?, annotations: Array<out Annotation>?,
mediaType: MediaType?
): Boolean {
return genericType?.let { this.canSerialize(it) } ?: false
}
@UseExperimental(ImplicitReflectionSerializer::class)
private fun canSerialize(type: Type): Boolean = when (type) {
is GenericArrayType -> this.canSerialize(type.genericComponentType)
is Class<*> -> type.isArray || type.kotlin.let {
it.compiledSerializer() ?: it.defaultSerializer()
} != null
is ParameterizedType -> {
val clazz = type.rawType as Class<*>
when {
clazz isA List::class.java || clazz isA Set::class.java ->
this.canSerialize(type.actualTypeArguments[0])
clazz isA Map::class.java -> type.actualTypeArguments.let {
this.canSerialize(it[0]) && this.canSerialize(it[1])
}
else -> false
}
}
else -> false
}
@UseExperimental(ImplicitReflectionSerializer::class)
override fun writeTo(
t: Any?, type: Class<*>?, genericType: Type?, annotations: Array<out Annotation>?,
mediaType: MediaType?, httpHeaders: MultivaluedMap<String, Any>?,
entityStream: OutputStream?
) {
PrintWriter(entityStream).use {
t ?: return it.print("null")
val serializer = type?.kotlin?.serializer() as? KSerializer<Any>
?: throw IllegalArgumentException()
it.print(Json.stringify(serializer, t))
}
}
}
Dependency に書いとくといい感じにしてくれるやつあるかもって思ったけど Google 力低くて見つけれなかった
UseExperimentalしているので build.gradle に
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions.freeCompilerArgs += ["-Xuse-experimental=kotlin.Experimental"]
}
って書いておきます
送る型を書く
今回はとりあえずこんな感じにしておきます
@Serializable
data class Hoge(
val id: Int,
val fugas: List<Fuga>
)
@Serializable
data class Fuga(
val id: Int,
val hello: String = "hello"
)
送るメソッドを書く
今回はこんな感じ
@Path("/hello")
class HelloResource {
@Path("/")
@Produces("application/json")
@GET
fun hello(): Hoge = Hoge(2, listOf(
Fuga(3),
Fuga(5),
Fuga(7)
))
}
叩いてみる
% curl localhost:8080/backend/hello -s | jq '.'
{
"id": 2,
"fugas": [
{
"id": 3,
"hello": "hello"
},
{
"id": 5,
"hello": "hello"
},
{
"id": 7,
"hello": "hello"
}
]
}
まとめ
そのうち Kotlin/JS から使ってみるやつやりたいお気持ち