Skip to content

WIP: Add custom json encoder and decoder #533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions zarr/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ def encode_fill_value(v, dtype):
if v is None:
return v
if dtype.kind == 'f':
# these cases are handled in the ZarrJsonEncoder now,
# but there may be cases where the metadata was not parsed
# with this encoder (?), so better to leave it here
if np.isnan(v):
return 'NaN'
elif np.isposinf(v):
Expand Down
13 changes: 13 additions & 0 deletions zarr/tests/test_attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest

import numpy as np
from zarr.attrs import Attributes
from zarr.tests.util import CountingDict

Expand Down Expand Up @@ -218,3 +219,15 @@ def test_caching_off(self):
assert 'spam' not in a
assert 10 == store.counter['__getitem__', 'attrs']
assert 3 == store.counter['__setitem__', 'attrs']

def test_special_values(self):
a = self.init_attributes(dict())

a['nan'] = np.nan
assert np.isnan(a['nan'])

a['pinf'] = np.PINF
assert np.isposinf(a['pinf'])

a['ninf'] = np.NINF
assert np.isneginf(a['ninf'])
33 changes: 31 additions & 2 deletions zarr/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,44 @@
}


class ZarrJsonEncoder(json.JSONEncoder):
"""Encode json input
"""
def default(self, obj):
if np.isnan(obj):
return "NaN"
elif np.isposinf(obj):
return "Infinity"
elif np.isneginf(obj):
return "-Infinity"
# we could also allow for passing numpy dtypes:
# if isinstance(obj, np.dtype):
# return obj.item()
return super().default(obj)


class ZarrJsonDecoder(json.JSONDecoder):
"""Decode json input
"""
def default(self, obj):
if obj == "NaN":
return np.nan
elif obj == "Infinity":
return np.PINF
elif obj == "-Infinity":
return np.NINF
return super().default(obj)


def json_dumps(o):
"""Write JSON in a consistent, human-readable way."""
return json.dumps(o, indent=4, sort_keys=True, ensure_ascii=True,
separators=(',', ': ')).encode('ascii')
separators=(',', ': '), cls=ZarrJsonEncoder).encode('ascii')


def json_loads(s):
"""Read JSON in a consistent way."""
return json.loads(ensure_text(s, 'ascii'))
return json.loads(ensure_text(s, 'ascii'), cls=ZarrJsonDecoder)


def normalize_shape(shape):
Expand Down