0

I did check a lot of different related Q'n'A (see linked questions), but none adressed my specific problem, mostly it was about different datatypes or requirements.

The attempt is to use this snippet to convert a nested map to json:

import json
#...
result = json.dumps(table_scan)

I am aware of the problems of this working correctly, but this doesnt seem to be the problem here. The above unfortunately gives me:

[ERROR] TypeError: Object of type Decimal is not JSON serializable
Traceback (most recent call last):
  File "/var/task/lambda-function.py", line 61, in lambda_handler
    result
  File "/var/lang/lib/python3.7/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/var/lang/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/var/lang/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/var/lang/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '

How can I fix this without having to go into the (quite large) map for a 'manual' conversion.

(Application context more precisely: My python AWS lambda function performs scanning a DynamoDB with boto3 which gives me a (multi-layer) dict≈map. My goal is to convert this map to a json in order to return it (outwards) to a AWS API gateway. The items inside the Database come from the event parameters of the (inwards) API gateway.)

Cadoiz
  • 1,446
  • 21
  • 31

2 Answers2

3

A custom encoder for specific json values (including Decimal) can be used as suggested here. In this example, Decimal should presumably be converted to float:

import json
from decimal import Decimal

#Does quasi the same things as json.loads from here: https://pypi.org/project/dynamodb-json/
class JSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Decimal):
            return float(obj)
        return json.JSONEncoder.default(self, obj)

#...
result = json.dumps(table_scan, cls=JSONEncoder)  #Works!
Cadoiz
  • 1,446
  • 21
  • 31
0

Unfortunately, I'm not yet able to post this as a comment.

You can consider monkey-patching the missing feature instead of writing your own JSONEncoder. To do so, you can (for the sake of simplicity) write an own small standalone module, say make_json_serializable.py to apply this monkey patch (what is it?) to the

json module when it's imported so JSONEncoder.default() automatically checks for a special "to_json()" method and uses it to encode the object if found. (Quote from here)

Code:

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder.default  # Save unmodified default.
JSONEncoder.default = _default # Replace it.

Then you can add an to_json method to any class like taken from this answer:

def to_json(self):
  return float(self) # or how you want it to be serialized

Then you also don't have to update every json.dumps() in your project like Onur said here. Applying this monkey-patch should work like this (untested):

from decimal import Decimal
Decimal.to_json = to_json