20190430のKotlinに関する記事は2件です。

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android] �2つの日付の差分を簡単に計算する

概要

例えば 2008/9/23今日 の間って, 何日間?何ヶ月間?何年間?

みたいなことをJava/KotlinのDateとかCalenderを使ってやろうとすると,日付をUnixTimeに直して引き算してさらにパースし直して・・・,となります.

そんな時に活躍するのが LocalDate なのですが,これがなんとminSdk26以上と手厳しい.
これが今回解決する課題です.
スクリーンショット 2019-04-30 0.13.03.png

(ちなみに 2008/9/23 はAndroid1.0が出た日なのです)

解決策

なんとあのJakeWharton神が,このAndroid用のバックポートライブラリを作ってくれているのです.

ThreeTen Android Backport
https://github.com/JakeWharton/ThreeTenABP

これを使うことで,minSdkを気にせずLocalDateを使用することができます.

使い方

まずGradleに下記を記述します.

app/build.gradle
dependencies {
    ...
    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と同じですので,そちらを見ると良いでしょう.

覚えたら書く - java.time.LocalDate

これでUnixTimeに戻して・・・なんてやらなくても大丈夫ですね!
お疲れ様でした.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む