mitchute
mitchute

Reputation: 431

Python json.dump scientific notation formatting

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

Answers (2)

TkTech
TkTech

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

Abhinav Jha
Abhinav Jha

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

Related Questions