Disable scientific notation in python json.dumps output

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.


Update

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