Lambdaカクテル

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

Invite link for Scalaわいわいランド

AWS LambdaでScalaを動かし、JSONを入出力する

先日AWS LambdaでScala 3を走らせる方法を解説した。

blog.3qe.us

解説したというか、何事もなく動いたという感じだ。

今回は安定してJSONを入出力するテクがわかってきたので解説する。

完成品はこんな感じになる:

import java.io.InputStream
import java.io.OutputStream
import com.amazonaws.services.lambda.runtime.Context
import scala.io.Source

case class Input(foo: Int, bar: String, buzz: Seq[Double])

def handler(in: InputStream, out: OutputStream, ctx: Context): Unit = {
  val jsonString = Source.fromInputStream(in).mkString
  val parsedInput: Either[io.circe.Error, Input] = decode[Input](jsonString)

  println(parsedInput)

  in.close()
  out.flush()
  out.close()
}

ハンドラ

AWS LambdaのJavaランタイムは、自動的に入力のJSONをPOJOに変換する機能が付いてくるのだが、この動作の振る舞いがとても微妙で、Scalaのcase classにうまく変換できない上に、文字列として受け取りたくても勝手に変換しようとするのでたいしてアテにならない。

ありがたいことに、ハンドラのシグネチャを特定の形式にするとJSON変換を通さずにそのまま入力を返してくれるようになる。

import java.io.InputStream
import java.io.OutputStream
import com.amazonaws.services.lambda.runtime.Context

def handler(in: InputStream, out: OutputStream, ctx: Context): Unit = ???

InputStreamOutputStream、最後にContextを受け取る形にするのがミソ。こうすることで、Lambdaランタイムは入出力ストリームとしてそのまま手つかずのバイトストリームを扱えるようになる。

もちろん最後にはストリームを閉じなければならない:

in.close()
out.flush()
out.close()

入力のJSON変換

今回はCirceを使うが、まぁ好みのものがあればそれを使えばよい。scala.io.Sourceを使うとjava.io.InputStreamをよしなに文字列に変換できるので、これをcirceのdecodeに放り込むとデコードが完了する。

import scala.io.Source

case class Input(foo: Int, bar: String, buzz: Seq[Double])

val jsonString = Source.fromInputStream(in).mkString
val parsedInput: Either[io.circe.Error, Input] = decode[Input](jsonString)

出力

出力も要するにjava.io.OutputStreamに書き込むだけなので難しいことはない。バイトストリームなのでgetBytes()してエンコードしてやる必要があることだけ注意。

out.write("hello, world".getBytes())

Circeから直接OutputStreamに書き込む方法があるかな、と思ったけど見付けられなかった。

まとめ

  • LambdaのJavaランタイムはJava向けの構成になっているのでScalaだと使いづらいことがある
  • JSONの入力をしたいときはInputStreamを使って直接バイトストリームを得るのがおすすめ
★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?