skip to Main Content

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?

2

Answers


  1. 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)
    
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search