Python JSON serialize a Decimal object

后端 未结 17 1385
星月不相逢
星月不相逢 2020-11-22 08:27

I have a Decimal(\'3.9\') as part of an object, and wish to encode this to a JSON string which should look like {\'x\': 3.9}. I don\'t care about p

17条回答
  •  庸人自扰
    2020-11-22 09:11

    For those who don't want to use a third-party library... An issue with Elias Zamaria's answer is that it converts to float, which can run into problems. For example:

    >>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
    '{"x": 1e-07}'
    >>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
    '{"x": 100000000000.01733}'
    

    The JSONEncoder.encode() method lets you return the literal json content, unlike JSONEncoder.default(), which has you return a json compatible type (like float) that then gets encoded in the normal way. The problem with encode() is that it (normally) only works at the top level. But it's still usable, with a little extra work (python 3.x):

    import json
    from collections.abc import Mapping, Iterable
    from decimal import Decimal
    
    class DecimalEncoder(json.JSONEncoder):
        def encode(self, obj):
            if isinstance(obj, Mapping):
                return '{' + ', '.join(f'{self.encode(k)}: {self.encode(v)}' for (k, v) in obj.items()) + '}'
            if isinstance(obj, Iterable) and (not isinstance(obj, str)):
                return '[' + ', '.join(map(self.encode, obj)) + ']'
            if isinstance(obj, Decimal):
                return f'{obj.normalize():f}'  # using normalize() gets rid of trailing 0s, using ':f' prevents scientific notation
            return super().encode(obj)
    

    Which gives you:

    >>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
    '{"x": 0.0000001}'
    >>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
    '{"x": 100000000000.01734}'
    

提交回复
热议问题