Search code examples
pythonjsonscientific-notation

Disable scientific notation in python json.dumps output


json.dumps outputs small float or decimal values using scientific notation, which is unacceptable to the json-rpc application this output is sent to.

>>> import json
>>> json.dumps({"x": 0.0000001})
'{"x": 1e-07}'

I want this output instead:

'{"x": 0.0000001}'

It would be ideal to avoid introducing additional dependencies.


Solution

  • One way to format

    evil = {"x": 0.00000000001}
    

    is to steal Decimal's "f" formatter. It's the only easy way I've found that avoids both cropping problems and exponents, but it's not space efficient.

    class FancyFloat(float):
        def __repr__(self):
            return format(Decimal(self), "f")
    

    To use it you can make an encoder that "decimalize"s the input

    class JsonRpcEncoder(json.JSONEncoder):
        def decimalize(self, val):
            if isinstance(val, dict):
                return {k:self.decimalize(v) for k,v in val.items()}
    
            if isinstance(val, (list, tuple)):
                return type(val)(self.decimalize(v) for v in val)
    
            if isinstance(val, float):
                return FancyFloat(val)
    
            return val
    
        def encode(self, val):
            return super().encode(self.decimalize(val))
    
    JsonRpcEncoder().encode(evil)
    #>>> '{"x": 0.00000000000999999999999999939496969281939810930172340963650867706746794283390045166015625}'
    

    or, of course, you could move the decimalization out into a function and call that before json.dumps.

    That's how I would do it, even if it's a lame method.


    Update

    Sam Mason suggests format(Decimal(str(self)), "f") instead, which should still always round-trip, but also produces shorter outputs.