Lambda+API GatewayのレスポンスをJSONとしてパース出来ない


はじめに

内閣府の祝日CSVをLambda上でJSON形式に加工して、API Gateway経由で使えるようにする」というプログラムを考えたのですが、API GatewayのレスポンスがJSONとして正しくパース出来ないことが分かりました。

Lambda上で動いているRubyプログラムは単体のスクリプトとして問題なく動作することを既に確認していましたが、原因が分かるまで少し時間がかかったので、その記録をまとめてみました。

使用した環境

  • Lambda(Ruby)
  • API Gateway

正しく動作しなかったコード

  • JSON.pretty_generateJSON.generateJSON.dumpなどでシリアライズした結果を返すと、API Gatewayを通して取得したレスポンスの前後に不要なダブルクォート(")が付いてしまい、JSONとしてパース出来ませんでした。
    • その後にさらに調べてみると、API Gatewayを通さずにLambda単体の状態でもレスポンスの前後に不要なダブルクォートが付いていたため、この現象がAPI Gatewayによるものではないと分かりました。
lambda_function.rb
require 'json'
require 'open-uri'

def lambda_handler(event:, context:)
    csv = OpenURI.open_uri('https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv') {|io|
        io.read
    }
    csv = csv.force_encoding("sjis")

    array = []
    csv.split(/\r\n|\r|\n/).each_with_index {|row, i|
        next if (i==0) # ヘッダ行は読み飛ばす
        buffs = row.split(",")
        hash = {"date": buffs[0], "name": buffs[1]}
        array.push(hash)
    }
    return JSON.pretty_generate(array)  # この行をJSON.generate()やJSON.dump()に変えても結果は変わらず...
end
出力結果の例
"[\n  {\n    \"date\": \"1955/1/1\",\n    \"name\": \"元日\"\n  },...,\n  {\n    \"date\": \"2020/11/23\",\n    \"name\": \"勤労感謝の日\"\n  }\n]"

正しく動作したコード

  • こちらのページに書かれているように、シリアライズせずにオブジェクトをそのまま返す形にコードを直したところ、正しいJSONをレスポンスとして受け取れるようになりました。
  • どうやら、 「Lambdaの関数の戻り値として文字列のJSONを指定すると、JSONとして二重にシリアライズされてしまう」 ことが今回の問題の原因のようです。
lambda_function.rb
require 'json'
require 'open-uri'

def lambda_handler(event:, context:)
    csv = OpenURI.open_uri('https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv') {|io|
        io.read
    }
    csv = csv.force_encoding("sjis")

    array = []
    csv.split(/\r\n|\r|\n/).each_with_index {|row, i|
        next if (i==0) # ヘッダ行は読み飛ばす
        buffs = row.split(",")
        hash = {"date": buffs[0], "name": buffs[1]}
        array.push(hash)
    }
    return array  # ArrayやHashなどのオブジェクトをそのまま返せばOK!
end
出力結果の例
[{"date":"1955/1/1","name":"元日"},...,{"date":"2020/11/23","name":"勤労感謝の日"}]

まとめ

  • ある程度調べたところでLambdaの仕様による現象だと分かっていたものの、中々解決策が見つからずに苦労しました。
  • 上記の仕様について、AWS公式ページのどこかに記載されているのかもしれませんが、大事な事なのでもう少し分かりやすく書いて欲しかったです。
    • もしかしたら私が気付かなかっただけで、どこかに明記されていたのかもしれません。
    • あるいは私がLambda初心者で知らなかっただけで、暗黙知だったのかもしれませんが...