【Tips】Lambda(Python)でハンドラーのeventを全て辞書型だと思っていたらハマったこと
森永です。
物凄いハマったんですが、解決してみればそこかよという案件です。
Lambda使いこなしている人だとあるあるなのかもしれません。
問題
イベントソースがSNSで起きた事象です。(他のイベントソースでも起こる可能性はあります。)
SNSからは以下のようなjsonがイベントとして送られてきます。(かなり端折ってます)
いや、送られてくると思ってました。
{ "Records": [ { "Sns": { "Timestamp": "2016-11-17T08:34:04.436Z", "Message": { "Trigger": { "Dimensions": [ { "name": "key", "value": "value" } ] } } } } ] }
Lambdaのハンドラーにeventというパラメータがあります。 イベントソースから送られてきた情報などが記載されているのですが、基本的に辞書型です。
def lambda_handler(event, context): print type(event) => <type 'dict'>
辞書型なので、以下のように下の階層を取り出してみても辞書型になってます。
def lambda_handler(event, context): print type(event['Records'][0]) => <type 'dict'>
もっと深く掘ってみましょう!!
def lambda_handler(event, context): print type(event['Records'][0]['Sns']) print type(event['Records'][0]['Sns']['Message']) => <type 'dict'> => <type 'unicode'>
え?????
「Message」に潜った瞬間に何故かunicode
になりました。
unicode
だと何が問題かというと、「Message」の下の「Trigger」にアクセスしようしても辞書型ではないので、event['Records'][0][Sns][Message][Trigger]
とすることが出来ないということです。
最初はこれをやろうとして、TypeError: string indices must be integers
というエラーが出て詰まっていました。
原因究明
突如としてunicode
になるのは何故か。
Python詳しい人ならすぐに思いつくことかと思いますが、殆ど触っていない私はまぁハマりました。
最終的にeventをprintしたものを隅々まで確認して遂に原因が判明しました。
{ u'Records': [ { u'Sns': { u'Timestamp': u'2016-11-17T08:34:04.436Z', u'Message': u'{ "Trigger": { "Dimensions": [ { "name": "sample", "value": "sample" } ] } }' } } ] }
eventをそのままprintすると各項目がUnicode文字列であるため、u
が項目の前に付与されています。
一見何も問題なさそうですが、よく見ると「Message」のあとの項目にはu
が付いていません。
そして何故か「Message」の後の波括弧の前にUnicode文字列を表すu
が…
そう、「Message」のあとの値(波括弧で囲まれた部分)が全て文字列として扱われていたのです!!!!
つまりはこういうことです。
{ "Records": [ { "Sns": { "Timestamp": "2016-11-17T08:34:04.436Z", "Message": "{ \"Trigger\": { \"Dimensions\": [ { \"name\": \"key\", \"value\": \"value\" } ] } }" } } ] }
これはアカン
解決方法
SNSから送られてくる文字列を変えることは出来ないので、Lambda側で対応しました。
Unicode文字列だと使えないのであれば、使える形の辞書型に変換しちゃえばよいのです。
変換にはjson.loads
を使用しました。
def lambda_handler(event, context): # このままだとUnicode文字列になっちゃう message_unicode = event['Records'][0]['Sns']['Message'] # json.loadsでUnicode→辞書型変換をするといける! message_dist = json.loads(message_unicode) print type(message_unicode) print type(message_dist) # 辞書形になれば以下のように下の階層を参照することも可能です。 print type(message_dist['Trigger']) => <type 'unicode'> => <type 'dict'> => <type 'dict'>
他にいい方法あったら教えてください。
さいごに
Lambdaは触る度にハマって足が遠のくので、ガンガン触ってもっと仲良くなろうと思います。
同じ症状で困っている方のお役に立てれば何よりです。