この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
森永です。
物凄いハマったんですが、解決してみればそこかよという案件です。
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は触る度にハマって足が遠のくので、ガンガン触ってもっと仲良くなろうと思います。
同じ症状で困っている方のお役に立てれば何よりです。