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?
When importing the standard json module, it is enough to change the default encoder FLOAT_REPR. There isn't really the need to import or create Encoder instances.
Sometimes is also very useful to output as json the best representation python can guess with str. This will make sure signifficant digits are not ignored.
If you're stuck with Python 2.5 or earlier versions: The monkey-patch trick does not seem to work with the original simplejson module if the C speedups are installed:
If you're using Python 2.7, a simple solution is to simply round your floats explicitly to the desired precision.
This works because Python 2.7 made float rounding more consistent. Unfortunately this does not work in Python 2.6:
The solutions mentioned above are workarounds for 2.6, but none are entirely adequate. Monkey patching json.encoder.FLOAT_REPR does not work if your Python runtime uses a C version of the JSON module. The PrettyFloat class in Tom Wuttke's answer works, but only if %g encoding works globally for your application. The %.15g is a bit magic, it works because float precision is 17 significant digits and %g does not print trailing zeroes.
I spent some time trying to make a PrettyFloat that allowed customization of precision for each number. Ie, a syntax like
It's not easy to get this right. Inheriting from float is awkward. Inheriting from Object and using a JSONEncoder subclass with its own default() method should work, except the json module seems to assume all custom types should be serialized as strings. Ie: you end up with the Javascript string "0.33" in the output, not the number 0.33. There may be a way yet to make this work, but it's harder than it looks.
Pros:
Cons:
Quadratic complexity.
Really unfortunate that
dumps
doesn't allow you to do anything to floats. Howeverloads
does. So if you don't mind the extra CPU load, you could throw it through the encoder/decoder/encoder and get the right result:You can do what you need to do, but it isn't documented: