追記
具体的な型が分かるわけではないので、この方法だとうまくいかないことがわかった。詳しく調べている。
本編
わけあって(わけは後で話す)、以下のような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を走らせることができたので、今度は具体的なイベントを受信したかったのだ。
ところで、AWS LambdaのJava版ライブラリは、"com.amazonaws" % "aws-lambda-java-events" % "3.11.0"
で参照されるパッケージに各種イベントを取り揃えていて、ハンドラの引数にこの型を指定しておくと勝手に型変換もやってくれるという嬉しい仕様になっている。
このパッケージにIoTButtonEvent
といういかにもIoT 1-Clickで使えそうな型があるのだが、なんとこれは使えない(1-Clickではない他のIoTのボタンクリックイベントに割当てられていて、構造が違う)!!
IoT 1-Clickが返すJSONは以下に定義されているので、このページを読みつつ、使いそうな場所だけつまみ食いした型エイリアスを作る必要が発生してしまった。
もしめちゃくちゃ手間がかかったら、悪態をついてやめようと思っていたのだが、簡単にできてよかった。Scalaありがとう。