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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With