I develop a AWS Lambda in Python. I read data in dynamoDB but after I want return json data to APIGateway.
print(dynamodbTableQueryData)
#{'year': Decimal('2015'), 'info': {'rating': Decimal('0'), 'plot': 'Nothing happens at all.'}, 'title': 'The Big New Movie'}
return {
'statusCode': 200,
'body': json.dumps(dynamodbTableQueryData)
}
if I write this, the title
is returned on APIGateway:
return {
'statusCode': 200,
'body': json.dumps(dynamodbTableQueryData['title'])
}
if I write this, the year
is not returned on APIGateway and I have an error:
return {
'statusCode': 200,
'body': json.dumps(dynamodbTableQueryData['year'])
}
this error:
[ERROR] TypeError: Object of type Decimal is not JSON serializable Traceback (most recent call last)
I try this but do not work:
import boto3
from boto3.dynamodb.types import TypeSerializer, TypeDeserializer
ts= TypeSerializer()
td = TypeDeserializer()
deserialized_data= td.deserialize(dynamodbTableQueryData)
print(deserialized_data)
CodePudding user response:
It is because JSON object must only contain specific types such as list, dict, str, int, float, etc. Thus for the types that are not supported such as Decimal
, datetime
, etc., we should explicitly define how should they be parsed by defining a custom serializer. For example, should Decimal('2015')
be a string "Decimal(2015)"
or just the number part "2015"
or should it be an int 2015
or float 2015.0
or something else.
Here is a solution that also works recursively if the Decimal
object is in an inner nest. We will get the string value of the number e.g. "2015"
(I chose str type because returning float might be inconsistent depending on the tech stack e.g. 1.2
might be interpreted as 1.20000001
). This will be implemented via the default
and cls
argument to json.dumps() which as documented:
If specified, default should be a function that gets called for objects that can’t otherwise be serialized. It should return a JSON encodable version of the object or raise a TypeError. If not specified, TypeError is raised.
from decimal import Decimal
import json
dynamodbTableQueryData = {'year': Decimal('2015'), 'info': {'rating': Decimal('0'), 'plot': 'Nothing happens at all.'}, 'title': 'The Big New Movie'}
# Solution 1
def to_serializable(val):
if isinstance(val, Decimal):
return str(val)
return val
print("Solution 1")
result = json.dumps(dynamodbTableQueryData['info'], default=to_serializable)
print(result)
result = json.dumps(dynamodbTableQueryData['title'], default=to_serializable)
print(result)
result = json.dumps(dynamodbTableQueryData['year'], default=to_serializable)
print(result)
# Solution 2
class MyJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return str(obj)
else:
return super().default(obj)
print("Solution 2")
result = json.dumps(dynamodbTableQueryData['info'], cls=MyJSONEncoder)
print(result)
result = json.dumps(dynamodbTableQueryData['title'], cls=MyJSONEncoder)
print(result)
result = json.dumps(dynamodbTableQueryData['year'], cls=MyJSONEncoder)
print(result)
Output
Solution 1
{"rating": "0", "plot": "Nothing happens at all."}
"The Big New Movie"
"2015"
Solution 2
{"rating": "0", "plot": "Nothing happens at all."}
"The Big New Movie"
"2015"