- 投稿日:2019-04-30T21:10:46+09:00
Kotlin Serialization + Jersey で RESTful API
はじめに
この記事はなんか書く太郎三日目の記事です
二日目 - 四日目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 から使ってみるやつやりたいお気持ち
- 投稿日:2019-04-30T00:41:04+09:00
[Android] �2つの日付の差分を簡単に計算する
概要
例えば 2008/9/23 〜 今日 の間って, 何日間?何ヶ月間?何年間?
みたいなことをJava/KotlinのDateとかCalenderを使ってやろうとすると,日付をUnixTimeに直して引き算してさらにパースし直して・・・,となります.
そんな時に活躍するのが
LocalDateなのですが,これがなんとminSdk26以上と手厳しい.
これが今回解決する課題です.
(ちなみに 2008/9/23 はAndroid1.0が出た日なのです)
解決策
なんとあのJakeWharton神が,このAndroid用のバックポートライブラリを作ってくれているのです.
ThreeTen Android Backport
https://github.com/JakeWharton/ThreeTenABPこれを使うことで,minSdkを気にせずLocalDateを使用することができます.
使い方
まずGradleに下記を記述します.
app/build.gradledependencies { ... implementation 'com.jakewharton.threetenabp:threetenabp:1.2.0' }これにより,
java.time.LocalDate
に存在したLocalDateに加え,
org.threeten.bp.LocalDate
が使用可能になりました.こちらを使います.たとえば 2008/9/23 〜 今日(執筆:2019/4/30) 間の差分日数・月数・年数を調べてみましょう.
val androidFirstRelease = LocalDate.of(2008, Month.SEPTEMBER, 23) val today = LocalDate.now() // 2019/4/30 val period = Period.between(androidFirstRelease, today) > period.days -> 7 > period.months -> 7 > period.years -> 10 // java.time.Month ではなく org.threeten.bp.Month を使うことに注意となり,Androidのファーストリリースから10年7ヶ月7日経っていることがわかりました.
LocalDateの使い方は基本的にJava8に乗っているデフォルトのLocalDateと同じですので,そちらを見ると良いでしょう.
これでUnixTimeに戻して・・・なんてやらなくても大丈夫ですね!
お疲れ様でした.
