write json float in scientific notation
Solution 1:
You have to add some special casing for dicts, lists, sets, etc., but by referencing abstract base classes from collections.abc
, you avoid explicitly testing for specific types.
Note that the test for Sequence
has to avoid matching on str
types, since iterating over a str
gives a bunch of 1-character str
s, which are also iterable Sequence
s, and so on until you reach the recursion limit. I could not find an ABC that represents "a sequence container, but not a str".
(I also have to echo Alex Martelli's criticism from a related post, that having to do this much work just to format a particular type speaks to issues in the design of the classes in this module.)
import json
from collections.abc import Mapping, Sequence
a = 0.001234567
class ScientificNotationEncoder(json.JSONEncoder):
def iterencode(self, o, _one_shot=False):
if isinstance(o, float):
return "{:e}".format(o)
elif isinstance(o, Mapping):
return "{{{}}}".format(', '.join('"{}" : {}'.format(str(ok), self.iterencode(ov))
for ok, ov in o.items()))
elif isinstance(o, Sequence) and not isinstance(o, str):
return "[{}]".format(', '.join(map(self.iterencode, o)))
return ', '.join(super().iterencode(o, _one_shot))
aout = json.dumps([a, a, "xyzzy", 42, {'z': a}, (a, a, a),],
cls=ScientificNotationEncoder)
print(aout)
# loading back in seems to still work okay!
print(json.loads(aout))
Prints:
[1.234567e-03, 1.234567e-03, "xyzzy", 42, {"z" : 1.234567e-03}, [1.234567e-03, 1.234567e-03, 1.234567e-03]]
[0.001234567, 0.001234567, 'xyzzy', 42, {'z': 0.001234567}, [0.001234567, 0.001234567, 0.001234567]]