Reputation: 1195
I am trying to write a serverless back-end for an application with AWS Lambda, and am running into the error in the title. The error occurs when testing with API Gateway proxy integration, but the function works fine when tested in the Lambda console.
Here is the error:
{
"errorMessage":"string indices must be integers",
"errorType":"TypeError",
"stackTrace":[
[
"/var/task/auth_login.py",
17,
"lambda_handler",
"response = get_user(payload)"
],
[
"/var/task/shifty_utils/__init__.py",
22,
"get_user",
"table = dynamo.Table(user['company'] + '_users')"
]
]
}
Here is context for where it occurs:
def lambda_handler(event, context):
payload = event['body']
response = get_user(payload)
def get_user(user):
try:
table = dynamo.Table(user['company'] + '_users')
response = table.get_item(
Key={
'userId': user['userId'],
'position': user['position']
}
)
except ClientError as e:
print(e.response['Error']['Message'])
return {'message': e.response['Error']['Message']}
else:
return response
Basically proxy integration seems to be reading in the event object as a JSON formatted string, as opposed to a dict, but here's what happens if I adjust my code for that:
{
"errorMessage":"the JSON object must be str, bytes or bytearray, not 'dict'",
"errorType":"TypeError",
"stackTrace":[
[
"/var/task/auth_login.py",
15,
"lambda_handler",
"payload = json.loads(event)"
],
[
"/var/lang/lib/python3.6/json/__init__.py",
348,
"loads",
"'not {!r}'.format(s.__class__.__name__))"
]
]
}
I can't win. Any help is appreciated.
Upvotes: 6
Views: 14904
Reputation: 99
This is because event['body']
is not a dict
but a str
. (I ran into this problem when decoding an SQS triggered event)
In case if anyone ran into a problem when a value of json.loads(event['body'])
is again not dict
but str
, here is a solution that decodes str to dict recursively.
import json
def to_dict(obj : object) -> dict:
""" Serialize Object to Dictionary Recursively
Arguments:
obj {object} -- string, list, or dictionary to be serialize
Returns:
dict -- Serialized Dictionary
"""
if isinstance(obj, dict):
data = {}
for k, v in obj.items():
data[k] = to_dict(v)
return data
elif hasattr(obj, "_ast"):
return to_dict(obj._ast())
elif hasattr(obj, "__iter__") and not isinstance(obj, str):
return [to_dict(v) for v in obj]
elif hasattr(obj, "__dict__"):
data = {key : to_dict(value) for key, value in obj.__dict__.items() if
not callable(value) and not key.startswith('_')}
elif isinstance(obj, str):
try:
data = {}
obj = json.loads(obj)
for k, v in obj.items():
data[k] = to_dict(v)
return data
except:
return obj
else:
return obj
Example Usage:
test = {'Records': ['{"s3": "{\\"bucket\\": \\"bucketname\\"}"}', '{"s3": "{\\"bucket\\": \\"bucketname\\"}"}']}
print(to_dict(test)['Records'][0]['s3']['bucket'])
This should print "bucketname".
Upvotes: 6
Reputation: 177
When dealing with json, python provides 2 std functions:
https://docs.python.org/3/library/json.html#json.dumps
Serialize obj to a JSON formatted str using this conversion table. The arguments have the same meaning as in dump().
https://docs.python.org/3/library/json.html#json.loads
Deserialize s (a str, bytes or bytearray instance containing a JSON document) to a Python object using this conversion table.
What you need here is the latest:
import json
payload = json.loads(event['body']
event['body']
is probably a json str so for accessing it´s values you fist will need to convert it to a python obj via `json.loads
Upvotes: 3
Reputation: 38982
You've identified the issue. However you're trying to convert a dict
to dict
.
This is what you have:
json.loads(event) # event is a dict
The body part as you have rightly identified is what is getting in as str
.
This is what you should have:
json.loads(event['body'])
One more step is to make it client-agnostic.
if isinstance(event['body'], (unicode, str)):
body = json.loads(event['body'])
Upvotes: 20