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.)

2 Answers 2

4

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!
Sign up to request clarification or add additional context in comments.

1 Comment

Note that as others have pointed out so well on stackoverflow.com/questions/43678946/… there are inaccuracies that can arise from converting a decimal to a float. They suggest using str() rather than float() to avoid losing any precision or creating any rounding errors, which was presumably a reason for using Decimal in the first place.
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.