Creating a Custom JSONEncoder

I am running Python 2.7 and I am trying to create my own subclass of FloatEncoder JSONEncoder. I have followed many examples, such as this , but none of them work. Here is my FloatEncoder class:

class FloatEncoder(JSONEncoder): def _iterencode(self, obj, markers=None): if isinstance(obj, float): return (str(obj) for obj in [obj]) return super(FloatEncoder, self)._iterencode(obj, markers) 

And this is where I call json.dumps:

 with patch("utils.fileio.FloatEncoder") as float_patch: for val,res in ((.00123456,'0.0012'),(.00009,'0.0001'),(0.99999,'1.0000'),({'hello':1.00001,'world':[True,1.00009]},'{"world": [true, 1.0001], "hello": 1.0000}')): untrusted = dumps(val, cls=FloatEncoder) self.assertTrue(float_patch._iterencode.called) self.assertEqual(untrusted, res) 

The first statement fails, which means that _iterencode is not executed. After reading the JSON documentation, I tried to override the default () method, but it was also not called.

+7
source share
2 answers

It seems you are trying to round float values ​​to 4 decimal points when generating JSON (based on test cases).

JSONEncoder sending with Python 2.7 does not have a _iterencode method, so why not get a call. Also, a quick look at json/encoder.py suggests that this class is written in a way that makes it difficult to change the behavior of float coding. It might be better to separate issues around floats before doing JSON serialization.

EDIT : Alex Martelli also delivers a monkey protection solution in a related answer. The problem with this approach is that you introduce a global modification of the behavior of the json library, which could inadvertently affect some other piece of code in your application that was written with the assumption that the floats are encoded without rounding.

Try the following:

 from collections import Mapping, Sequence from unittest import TestCase, main from json import dumps def round_floats(o): if isinstance(o, float): return round(o, 4) elif isinstance(o, basestring): return o elif isinstance(o, Sequence): return [round_floats(item) for item in o] elif isinstance(o, Mapping): return dict((key, round_floats(value)) for key, value in o.iteritems()) else: return o class TestFoo(TestCase): def test_it(self): for val, res in ((.00123456, '0.0012'), (.00009, '0.0001'), (0.99999, '1.0'), ({'hello': 1.00001, 'world': [True, 1.00009]}, '{"world": [true, 1.0001], "hello": 1.0}')): untrusted = dumps(round_floats(val)) self.assertEqual(untrusted, res) if __name__ == '__main__': main() 
+2
source

Do not define _iterencode , define default , as shown in the third answer on this page.

-one
source

All Articles