Kotlin Serialization + Jersey で RESTful API

# はじめに
この記事は[なんか書く太郎](https://gw-advent.9wick.com/calendars/31)三日目の記事です
[二日目](https://ryonkmr.com/155630428) - [四日目](https://qiita.com/yskkuwahara/items/6c394e597e8e4a8be98c)

Kotlin Serialization 使えば Kotlin/JS で作ったフロントエンドでも同じ型を使い回せて便利そうだなと思って軽い気持ちで試してみたので雑にまとめときます

# MessageBodyWriter の実装を書く
Jersey の探索パッケージの下に， Content-Type が application/json のときは `kotlinx.serialization.json.Json` をつかってハンドラの返し値を JSON にシリアライズするやつをおきます

```kotlin
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 に

```groovy
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
    kotlinOptions.freeCompilerArgs += ["-Xuse-experimental=kotlin.Experimental"]
}
```
って書いておきます

# 送る型を書く
今回はとりあえずこんな感じにしておきます

```kotlin
@Serializable
data class Hoge(
        val id: Int,
        val fugas: List<Fuga>
)

@Serializable
data class Fuga(
        val id: Int,
        val hello: String = "hello"
)
```

# 送るメソッドを書く
今回はこんな感じ

```kotlin
@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 から使ってみるやつやりたいお気持ち