Reputation: 431
Due to suspected platform differences between developers, json.dump
is formatting scientific notations in different ways (one person's machine, it formats to 1e-6
, and on others it formats to 1e-06
). The files are committed to our git history, so having to constantly revert the changes is annoying.
Is there a way to control how scientific numbers are formatted?
Upvotes: 0
Views: 69
Reputation: 4976
Do you need to use the built-in JSON module? yyjson has more control over number parsing, including support for Decimal objects which will preserve this regardless of platform. Plus, it's just much faster.
If you're reading huge floats/doubles or require perfect precision, you can tell yyjson to read all numbers as Decimals:
>>> from yyjson import Document, ReaderFlags
>>> float('1.7976931348623157e+310')
inf
>>> doc = Document(
... '{"huge": 1.7976931348623157e+310}',
... flags=ReaderFlags.NUMBERS_AS_DECIMAL
... )
>>> print(doc.get_pointer('/huge'))
1.7976931348623157E+310
Or use ReaderFlags.BIG_NUMBERS_AS_DECIMAL
to only read numbers that are
too large for Python's float type as Decimals:
>>> from yyjson import Document, ReaderFlags
>>> doc = Document(
'{"huge": 1.7976931348623157e+310, "small": 1.0}',
flags=ReaderFlags.BIG_NUMBERS_AS_DECIMAL
)
>>> type(doc.get_pointer('/huge'))
<class 'decimal.Decimal'>
>>> type(doc.get_pointer('/small'))
<class 'float'>
This will round-trip regardless of platform.
Upvotes: 0
Reputation: 9
import json
class CustomJSONEncoder(json.JSONEncoder):
def encode(self, obj):
return super().encode(self._convert(obj))
def _convert(self, obj):
if isinstance(obj, float):
return format(obj, '.6e') # Consistent scientific notation
elif isinstance(obj, dict):
return {k: self._convert(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [self._convert(v) for v in obj]
return obj
data = {
"value1": 0.000001,
"value2": 1e-6,
"nested": {"value3": 1e-6}
}
with open("output.json", "w") as f:
json.dump(data, f, cls=CustomJSONEncoder)
Upvotes: -1