Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 753
Dust Avatar asked Sep 21 '13 19:09

Dust


2 Answers

I can't find an answer to avoid the problem that converts 0.00001 to 1e-5,so I wrote a pretty_float_json_dumps function. It works fine in my project! Hope it can help someone!!

def pretty_float_json_dumps(json_obj):
    dumps_str = ""

    if isinstance(json_obj, dict): 
        dumps_str += "{"
        for k,v in json_obj.items():
            dumps_str += json.dumps(k)+":"
            if isinstance(v, float): 
                float_tmp_str = ("%.16f" % v).rstrip("0")
                dumps_str += (float_tmp_str+'0' if float_tmp_str.endswith('.') else float_tmp_str) + ','
            elif isinstance(v, list) or isinstance(v, dict): 
                dumps_str += pretty_float_json_dumps(v)+','
            else:
                dumps_str += pretty_float_json_dumps(v)+','
        if dumps_str.endswith(','):
            dumps_str = dumps_str[:-1]
        dumps_str += "}"
    elif isinstance(json_obj, list): 
        dumps_str += "["
        for v in json_obj:
            if isinstance(v, float): 
                float_tmp_str = ("%.16f" % v).rstrip("0")
                dumps_str += (float_tmp_str+'0' if float_tmp_str.endswith('.') else float_tmp_str) + ','
            elif isinstance(v, list) or isinstance(v, dict): 
                dumps_str += pretty_float_json_dumps(v)+','
            else:
                dumps_str += pretty_float_json_dumps(v)+','
        if dumps_str.endswith(','):
            dumps_str = dumps_str[:-1]
        dumps_str += "]"
    else:
        dumps_str += json.dumps(json_obj)
    return dumps_str
like image 42
lee ray Avatar answered Sep 16 '22 21:09

lee ray


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.

like image 175
Veedrac Avatar answered Sep 19 '22 21:09

Veedrac