Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

Scalaでネストしたオブジェクトの型はワンライナーで定義できる(JSONに対応する型を宣言的に定義する)

追記

具体的な型が分かるわけではないので、この方法だとうまくいかないことがわかった。詳しく調べている。

本編

わけあって(わけは後で話す)、以下のようなJSONに対応する型エイリアスを定義しなければならなくなった。

{
  "deviceEvent": {
    "buttonClicked": {
      "clickType": "SINGLE",
      "reportedTime": "2018-05-04T23:26:33.747Z"
    }
  }
}

以下のように書くことができる。ダブルクオートを剥がしてイチゴのトッピングをつけたら完成という感じで、かなり楽。

type OneClickEvent = {
  val deviceEvent: {
    val buttonClicked: {
      val clickType: String
      val reportedTime: DateTime
    }
  }
}

type定義の右辺でも、{}を使って部分的構造型のリテラルを直接記述できるので、かなり元のJSONと対応した記述ができる。

当初自分は各フィールドごとの型エイリアスを分けて書かなければならないと思っていた。これは型を書くところに{}を書けるとは知らなかったため、全てtypeで切り出そうとしていたからだ。

// こう書かなきゃイカン! と思っていた:
type ButtonClicked =  {
  val clickType: String
  val reportedTime: DateTime
}
type DeviceEvent = { val buttonClicked: ButtonClicked }
type OneClickEvent = { val deviceEvent: DeviceEvent }

この書き方だとどうしても冗長でワケがわからなくなってしまうので、一気呵成に書けるのは助かる。

贅沢を言うと、valも省略できればありがたいが、Scalaは今のところそういう言語ではないのでvalは書かなければならない。Odersky先生にお願いしたら作ってくれるかもしれない。

また、構造的部分型なのでリフレクション的仕組みを使わなければフィールドにはアクセスできないようで、Scala 3ではimport reflect.Selectable.reflectiveSelectableが必要であった。

import reflect.Selectable.reflectiveSelectable

val event: OneClickEvent = ...
val clickType = event.deviceEvent.buttonClicked.clickType

背景

どうしてこういうことがやりたかったかというと、AWS LambdaでIoT 1-clickのイベントを受信したかったからである。先日の記事でLambda上でScalaを走らせることができたので、今度は具体的なイベントを受信したかったのだ。

blog.3qe.us

ところで、AWS LambdaのJava版ライブラリは、"com.amazonaws" % "aws-lambda-java-events" % "3.11.0"で参照されるパッケージに各種イベントを取り揃えていて、ハンドラの引数にこの型を指定しておくと勝手に型変換もやってくれるという嬉しい仕様になっている。

github.com

このパッケージにIoTButtonEventといういかにもIoT 1-Clickで使えそうな型があるのだが、なんとこれは使えない(1-Clickではない他のIoTのボタンクリックイベントに割当てられていて、構造が違う)!!

IoT 1-Clickが返すJSONは以下に定義されているので、このページを読みつつ、使いそうな場所だけつまみ食いした型エイリアスを作る必要が発生してしまった。

bokete.jp

もしめちゃくちゃ手間がかかったら、悪態をついてやめようと思っていたのだが、簡単にできてよかった。Scalaありがとう。

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?