I am using the standard json module in python 2.6 to serialize a list of floats. However, I'm getting results like this:
>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
I want the floats to be formated with only two decimal digits. The output should look like this:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
I have tried defining my own JSON Encoder class:
class MyEncoder(json.JSONEncoder):
def encode(self, obj):
if isinstance(obj, float):
return format(obj, '.2f')
return json.JSONEncoder.encode(self, obj)
This works for a sole float object:
>>> json.dumps(23.67, cls=MyEncoder)
'23.67'
But fails for nested objects:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
I don't want to have external dependencies, so I prefer to stick with the standard json module.
How can I achieve this?
emits
No monkeypatching necessary.
Alex Martelli's solution will work for single threaded apps, but may not work for multi-threaded apps that need to control the number of decimal places per thread. Here is a solution that should work in multi threaded apps:
You can merely set encoder.thread_local.decimal_places to the number of decimal places you want, and the next call to json.dumps() in that thread will use that number of decimal places
I agree with @Nelson that inheriting from float is awkward, but perhaps a solution that only touches the
__repr__
function might be forgiveable. I ended up using thedecimal
package for this to reformat floats when needed. The upside is that this works in all contexts whererepr()
is being called, so also when simply printing lists to stdout for example. Also, the precision is runtime configurable, after the data has been created. Downside is of course that your data needs to be converted to this special float class (as unfortunately you cannot seem to monkey patchfloat.__repr__
). For that I provide a brief conversion function.The code:
Usage example:
Here's a solution that worked for me in Python 3 and does not require monkey patching:
Output is:
It copies the data but with rounded floats.
If you need to do this in python 2.7 without overriding the global json.encoder.FLOAT_REPR, here's one way.
Then, in python 2.7:
In python 2.6, it doesn't quite work as Matthew Schinckel points out below:
Unfortunately, I believe you have to do this by monkey-patching (which, to my opinion, indicates a design defect in the standard library
json
package). E.g., this code:emits:
as you desire. Obviously, there should be an architected way to override
FLOAT_REPR
so that EVERY representation of a float is under your control if you wish it to be; but unfortunately that's not how thejson
package was designed:-(.