GithubHelp home page GithubHelp logo

mverleg / pyjson_tricks Goto Github PK

View Code? Open in Web Editor NEW
154.0 12.0 21.0 393 KB

Extra features for Python's JSON: comments, order, numpy, pandas, datetimes, and many more! Simple but customizable.

License: Other

Python 100.00%
python json numpy datetime comments serialization deserialization

pyjson_tricks's Introduction

JSON tricks (python)

The [pyjson-tricks] package brings several pieces of functionality to python handling of json files:

  1. Store and load numpy arrays in human-readable format.
  2. Store and load class instances both generic and customized.
  3. Store and load date/times as a dictionary (including timezone).
  4. Preserve map order {} using OrderedDict.
  5. Allow for comments in json files by starting lines with #.
  6. Sets, complex numbers, Decimal, Fraction, enums, compression, duplicate keys, pathlib Paths, bytes ...

As well as compression and disallowing duplicate keys.

Several keys of the format __keyname__ have special meanings, and more might be added in future releases.

If you're considering JSON-but-with-comments as a config file format, have a look at HJSON, it might be more appropriate. For other purposes, keep reading!

Thanks for all the Github stars⭐!

Installation and use

You can install using

pip install json-tricks

Decoding of some data types needs the corresponding package to be installed, e.g. numpy for arrays, pandas for dataframes and pytz for timezone-aware datetimes.

You can import the usual json functions dump(s) and load(s), as well as a separate comment removal function, as follows:

from json_tricks import dump, dumps, load, loads, strip_comments

The exact signatures of these and other functions are in the documentation.

Quite some older versions of Python are supported. For an up-to-date list see the automated tests.

Features

Numpy arrays

When not compressed, the array is encoded in sort-of-readable and very flexible and portable format, like so:

arr = arange(0, 10, 1, dtype=uint8).reshape((2, 5))
print(dumps({'mydata': arr}))

this yields:

{
    "mydata": {
        "dtype": "uint8",
        "shape": [2, 5],
        "Corder": true,
        "__ndarray__": [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
    }
}

which will be converted back to a numpy array when using json_tricks.loads. Note that the memory order (Corder) is only stored in v3.1 and later and for arrays with at least 2 dimensions.

As you see, this uses the magic key __ndarray__. Don't use __ndarray__ as a dictionary key unless you're trying to make a numpy array (and know what you're doing).

Numpy scalars are also serialized (v3.5+). They are represented by the closest python primitive type. A special representation was not feasible, because Python's json implementation serializes some numpy types as primitives, without consulting custom encoders. If you want to preserve the exact numpy type, use encode_scalars_inplace.

There is also a compressed format (thanks claydugo for fix). From the next major release, this will be default when using compression. For now, you can use it as:

dumps(data, compression=True, properties={'ndarray_compact': True})

This compressed format encodes the array data in base64, with gzip compression for the array, unless 1) compression has little effect for that array, or 2) the whole file is already compressed. If you only want compact format for large arrays, pass the number of elements to ndarray_compact.

Example:

data = [linspace(0, 10, 9), array([pi, exp(1)])]
dumps(data, compression=False, properties={'ndarray_compact': 8})

[{
   "__ndarray__": "b64.gz:H4sIAAAAAAAC/2NgQAZf7CE0iwOE5oPSIlBaEkrLQegGRShfxQEAz7QFikgAAAA=",
   "dtype": "float64",
   "shape": [9]
 }, {
   "__ndarray__": [3.141592653589793, 2.718281828459045],
   "dtype": "float64",
   "shape": [2]
 }]

Class instances

json_tricks can serialize class instances.

If the class behaves normally (not generated dynamic, no __new__ or __metaclass__ magic, etc) and all it's attributes are serializable, then this should work by default.

# json_tricks/test_class.py
class MyTestCls:
def __init__(self, **kwargs):
    for k, v in kwargs.items():
        setattr(self, k, v)

cls_instance = MyTestCls(s='ub', dct={'7': 7})

json = dumps(cls_instance, indent=4)
cls_instance_again = loads(json)

You'll get your instance back. Here the json looks like this:

{
   	"__instance_type__": [
   		"json_tricks.test_class",
   		"MyTestCls"
   	],
   	"attributes": {
   		"s": "ub",
   		"dct": {
   			"7": 7
   		}
   	}
}

As you can see, this stores the module and class name. The class must be importable from the same module when decoding (and should not have changed). If it isn't, you have to manually provide a dictionary to cls_lookup_map when loading in which the class name can be looked up. Note that if the class is imported, then globals() is such a dictionary (so try loads(json, cls_lookup_map=glboals())). Also note that if the class is defined in the 'top' script (that you're calling directly), then this isn't a module and the import part cannot be extracted. Only the class name will be stored; it can then only be deserialized in the same script, or if you provide cls_lookup_map.

Note that this also works with slots without having to do anything (thanks to koffie and dominicdoty), which encodes like this (custom indentation):

{
    "__instance_type__": ["module.path", "ClassName"],
    "slots": {"slotattr": 37},
    "attributes": {"dictattr": 42}
}

If the instance doesn't serialize automatically, or if you want custom behaviour, then you can implement __json__encode__(self) and __json_decode__(self, **attributes) methods, like so:

class CustomEncodeCls:
def __init__(self):
    self.relevant = 42
    self.irrelevant = 37

    def __json_encode__(self):
    # should return primitive, serializable types like dict, list, int, string, float...
    return {'relevant': self.relevant}

    def __json_decode__(self, **attrs):
    # should initialize all properties; note that __init__ is not called implicitly
    self.relevant = attrs['relevant']
    self.irrelevant = 12

As you've seen, this uses the magic key __instance_type__. Don't use __instance_type__ as a dictionary key unless you know what you're doing.

Date, time, datetime and timedelta

Date, time, datetime and timedelta objects are stored as dictionaries of "day", "hour", "millisecond" etc keys, for each nonzero property.

Timezone name is also stored in case it is set, as is DST (thanks eumir). You'll need to have pytz installed to use timezone-aware date/times, it's not needed for naive date/times.

{
    "__datetime__": null,
    "year": 1988,
    "month": 3,
    "day": 15,
    "hour": 8,
    "minute": 3,
    "second": 59,
    "microsecond": 7,
    "tzinfo": "Europe/Amsterdam"
}

This approach was chosen over timestamps for readability and consistency between date and time, and over a single string to prevent parsing problems and reduce dependencies. Note that if primitives=True, date/times are encoded as ISO 8601, but they won't be restored automatically.

Don't use __date__, __time__, __datetime__, __timedelta__ or __tzinfo__ as dictionary keys unless you know what you're doing, as they have special meaning.

Order

Given an ordered dictionary like this (see the tests for a longer one):

ordered = OrderedDict((
    ('elephant', None),
    ('chicken', None),
    ('tortoise', None),
))

Converting to json and back will preserve the order:

from json_tricks import dumps, loads
json = dumps(ordered)
ordered = loads(json, preserve_order=True)

where preserve_order=True is added for emphasis; it can be left out since it's the default.

As a note on performance, both dicts and OrderedDicts have the same scaling for getting and setting items (O(1)). In Python versions before 3.5, OrderedDicts were implemented in Python rather than C, so were somewhat slower; since Python 3.5 both are implemented in C. In summary, you should have no scaling problems and probably no performance problems at all, especially in Python 3. Python 3.6+ preserves order of dictionaries by default making this redundant, but this is an implementation detail that should not be relied on.

Comments

Warning: in the next major version, comment parsing will be opt-in, not default anymore (for performance reasons). Update your code now to pass ignore_comments=True explicitly if you want comment parsing.

This package uses # and // for comments, which seem to be the most common conventions, though only the latter is valid javascript.

For example, you could call loads on the following string:

{ # "comment 1 "hello": "Wor#d", "Bye": ""M#rk"", "yes\"": 5,# comment" 2 "quote": ""th#t's" what she said", // comment "3" "list": [1, 1, "#", """, "", 8], "dict": {"q": 7} #" comment 4 with quotes } // comment 5

And it would return the de-commented version:

{
    "hello": "Wor#d", "Bye": ""M#rk"", "yes\\"": 5,
    "quote": ""th#t's" what she said",
    "list": [1, 1, "#", """, "\", 8], "dict": {"q": 7}
}

Since comments aren't stored in the Python representation of the data, loading and then saving a json file will remove the comments (it also likely changes the indentation).

The implementation of comments is a bit crude, which means that there are some exceptional cases that aren't handled correctly (#57).

It is also not very fast. For that reason, if ignore_comments wasn't explicitly set to True, then json-tricks first tries to parge without ignoring comments. If that fails, then it will automatically re-try with comment handling. This makes the no-comment case faster at the cost of the comment case, so if you are expecting comments make sure to set ignore_comments to True.

Other features

  • Special floats like NaN, Infinity and -0 using the allow_nan=True argument (non-standard json, may not decode in other implementations).
  • Sets are serializable and can be loaded. By default the set json representation is sorted, to have a consistent representation.
  • Save and load complex numbers (py3) with 1+2j serializing as {'__complex__': [1, 2]}.
  • Save and load Decimal and Fraction (including NaN, infinity, -0 for Decimal).
  • Save and load Enum (thanks to Jenselme), either built-in in python3.4+, or with the enum34 package in earlier versions. IntEnum needs encode_intenums_inplace.
  • json_tricks allows for gzip compression using the compression=True argument (off by default).
  • json_tricks can check for duplicate keys in maps by setting allow_duplicates to False. These are kind of allowed, but are handled inconsistently between json implementations. In Python, for dict and OrderedDict, duplicate keys are silently overwritten.
  • Save and load pathlib.Path objects (e.g., the current path, Path('.'), serializes as {"__pathlib__": "."}) (thanks to bburan).
  • Save and load bytes (python 3+ only), which will be encoded as utf8 if that is valid, or as base64 otherwise. Base64 is always used if primitives are requested. Serialized as [{"__bytes_b64__": "aGVsbG8="}] vs [{"__bytes_utf8__": "hello"}].
  • Save and load slices (thanks to claydugo).

Preserve type vs use primitive

By default, types are encoded such that they can be restored to their original type when loaded with json-tricks. Example encodings in this documentation refer to that format.

You can also choose to store things as their closest primitive type (e.g. arrays and sets as lists, decimals as floats). This may be desirable if you don't care about the exact type, or you are loading the json in another language (which doesn't restore python types). It's also smaller.

To forego meta data and store primitives instead, pass primitives to dump(s). This is available in version 3.8 and later. Example:

data = [
    arange(0, 10, 1, dtype=int).reshape((2, 5)),
    datetime(year=2017, month=1, day=19, hour=23, minute=00, second=00),
    1 + 2j,
    Decimal(42),
    Fraction(1, 3),
    MyTestCls(s='ub', dct={'7': 7}),  # see later
    set(range(7)),
]
# Encode with metadata to preserve types when decoding
print(dumps(data))
// (comments added and indenting changed)
[
    // numpy array
    {
        "__ndarray__": [
            [0, 1, 2, 3, 4],
            [5, 6, 7, 8, 9]],
        "dtype": "int64",
        "shape": [2, 5],
        "Corder": true
    },
    // datetime (naive)
    {
        "__datetime__": null,
        "year": 2017,
        "month": 1,
        "day": 19,
        "hour": 23
    },
    // complex number
    {
        "__complex__": [1.0, 2.0]
    },
    // decimal & fraction
    {
        "__decimal__": "42"
    },
    {
        "__fraction__": true
        "numerator": 1,
        "denominator": 3,
    },
    // class instance
    {
        "__instance_type__": [
          "tests.test_class",
          "MyTestCls"
        ],
        "attributes": {
          "s": "ub",
          "dct": {"7": 7}
        }
    },
    // set
    {
        "__set__": [0, 1, 2, 3, 4, 5, 6]
    }
]
# Encode as primitive types; more simple but loses type information
print(dumps(data, primitives=True))
// (comments added and indentation changed)
[
    // numpy array
    [[0, 1, 2, 3, 4],
    [5, 6, 7, 8, 9]],
    // datetime (naive)
    "2017-01-19T23:00:00",
    // complex number
    [1.0, 2.0],
    // decimal & fraction
    42.0,
    0.3333333333333333,
    // class instance
    {
        "s": "ub",
        "dct": {"7": 7}
    },
    // set
    [0, 1, 2, 3, 4, 5, 6]
]

Note that valid json is produced either way: json-tricks stores meta data as normal json, but other packages probably won't interpret it.

Note that valid json is produced either way: json-tricks stores meta data as normal json, but other packages probably won't interpret it.

Usage & contributions

Code is under Revised BSD License so you can use it for most purposes including commercially.

Contributions are very welcome! Bug reports, feature suggestions and code contributions help this project become more useful for everyone! There is a short contribution guide.

Contributors not yet mentioned: janLo (performance boost).

Tests

Tests are run automatically for commits to the repository for all supported versions. This is the status:

image

To run the tests manually for your version, see this guide.

pyjson_tricks's People

Contributors

bburan avatar claydugo avatar erwanp avatar hmaarrfk avatar janlo avatar koffie avatar mverleg avatar yarikoptic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyjson_tricks's Issues

not pip installable

Unfortunately the fix for #36 in 180efc5
resulted in it to become non installable (see below), and as I have thought, it now installs 'tests' as an independent python module (shouldn't):

$> virtualenv venv                       
Running virtualenv with interpreter /usr/bin/python2
New python executable in /tmp/venv/bin/python2
Also creating executable in /tmp/venv/bin/python
Installing setuptools, pkg_resources, pip, wheel...done.

$> venv/bin/pip install json_tricks
Collecting json_tricks
  Downloading json_tricks-3.11.1.tar.gz
Building wheels for collected packages: json-tricks
  Running setup.py bdist_wheel for json-tricks ... error
  Complete output from command /tmp/venv/bin/python2 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-bdY5b3/json-tricks/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" bdist_wheel -d /home/yoh/.tmp/tmpCIK_H8pip-wheel- --python-tag cp27:
  running bdist_wheel
  running build
  running build_py
  creating build
  creating build/lib.linux-x86_64-2.7
  creating build/lib.linux-x86_64-2.7/json_tricks
  copying json_tricks/np.py -> build/lib.linux-x86_64-2.7/json_tricks
  copying json_tricks/comment.py -> build/lib.linux-x86_64-2.7/json_tricks
  copying json_tricks/nonp.py -> build/lib.linux-x86_64-2.7/json_tricks
  copying json_tricks/decoders.py -> build/lib.linux-x86_64-2.7/json_tricks
  copying json_tricks/utils.py -> build/lib.linux-x86_64-2.7/json_tricks
  copying json_tricks/encoders.py -> build/lib.linux-x86_64-2.7/json_tricks
  copying json_tricks/np_utils.py -> build/lib.linux-x86_64-2.7/json_tricks
  copying json_tricks/__init__.py -> build/lib.linux-x86_64-2.7/json_tricks
  creating build/lib.linux-x86_64-2.7/tests
  copying tests/test_bare.py -> build/lib.linux-x86_64-2.7/tests
  copying tests/test_enum.py -> build/lib.linux-x86_64-2.7/tests
  copying tests/test_tz.py -> build/lib.linux-x86_64-2.7/tests
  copying tests/test_class.py -> build/lib.linux-x86_64-2.7/tests
  copying tests/test_pandas.py -> build/lib.linux-x86_64-2.7/tests
  copying tests/__init__.py -> build/lib.linux-x86_64-2.7/tests
  copying tests/test_utils.py -> build/lib.linux-x86_64-2.7/tests
  copying tests/test_np.py -> build/lib.linux-x86_64-2.7/tests
  running egg_info
  writing json_tricks.egg-info/PKG-INFO
  writing top-level names to json_tricks.egg-info/top_level.txt
  writing dependency_links to json_tricks.egg-info/dependency_links.txt
  reading manifest file 'json_tricks.egg-info/SOURCES.txt'
  writing manifest file 'json_tricks.egg-info/SOURCES.txt'
  installing to build/bdist.linux-x86_64/wheel
  running install
  running install_lib
  creating build/bdist.linux-x86_64
  creating build/bdist.linux-x86_64/wheel
  creating build/bdist.linux-x86_64/wheel/json_tricks
  copying build/lib.linux-x86_64-2.7/json_tricks/np.py -> build/bdist.linux-x86_64/wheel/json_tricks
  copying build/lib.linux-x86_64-2.7/json_tricks/comment.py -> build/bdist.linux-x86_64/wheel/json_tricks
  copying build/lib.linux-x86_64-2.7/json_tricks/nonp.py -> build/bdist.linux-x86_64/wheel/json_tricks
  copying build/lib.linux-x86_64-2.7/json_tricks/decoders.py -> build/bdist.linux-x86_64/wheel/json_tricks
  copying build/lib.linux-x86_64-2.7/json_tricks/utils.py -> build/bdist.linux-x86_64/wheel/json_tricks
  copying build/lib.linux-x86_64-2.7/json_tricks/encoders.py -> build/bdist.linux-x86_64/wheel/json_tricks
  copying build/lib.linux-x86_64-2.7/json_tricks/np_utils.py -> build/bdist.linux-x86_64/wheel/json_tricks
  copying build/lib.linux-x86_64-2.7/json_tricks/__init__.py -> build/bdist.linux-x86_64/wheel/json_tricks
  creating build/bdist.linux-x86_64/wheel/tests
  copying build/lib.linux-x86_64-2.7/tests/test_bare.py -> build/bdist.linux-x86_64/wheel/tests
  copying build/lib.linux-x86_64-2.7/tests/test_enum.py -> build/bdist.linux-x86_64/wheel/tests
  copying build/lib.linux-x86_64-2.7/tests/test_tz.py -> build/bdist.linux-x86_64/wheel/tests
  copying build/lib.linux-x86_64-2.7/tests/test_class.py -> build/bdist.linux-x86_64/wheel/tests
  copying build/lib.linux-x86_64-2.7/tests/test_pandas.py -> build/bdist.linux-x86_64/wheel/tests
  copying build/lib.linux-x86_64-2.7/tests/__init__.py -> build/bdist.linux-x86_64/wheel/tests
  copying build/lib.linux-x86_64-2.7/tests/test_utils.py -> build/bdist.linux-x86_64/wheel/tests
  copying build/lib.linux-x86_64-2.7/tests/test_np.py -> build/bdist.linux-x86_64/wheel/tests
  running install_data
  creating build/bdist.linux-x86_64/wheel/json_tricks-3.11.1.data
  creating build/bdist.linux-x86_64/wheel/json_tricks-3.11.1.data/data
  copying LICENSE.txt -> build/bdist.linux-x86_64/wheel/json_tricks-3.11.1.data/data/
  error: can't copy 'README.txt': doesn't exist or not a regular file
  
  ----------------------------------------
  Failed building wheel for json-tricks
  Running setup.py clean for json-tricks
Failed to build json-tricks
Installing collected packages: json-tricks
  Running setup.py install for json-tricks ... error
    Complete output from command /tmp/venv/bin/python2 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-bdY5b3/json-tricks/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /home/yoh/.tmp/pip-kiI8oF-record/install-record.txt --single-version-externally-managed --compile --install-headers /tmp/venv/include/site/python2.7/json-tricks:
    running install
    running build
    running build_py
    creating build
    creating build/lib.linux-x86_64-2.7
    creating build/lib.linux-x86_64-2.7/json_tricks
    copying json_tricks/np.py -> build/lib.linux-x86_64-2.7/json_tricks
    copying json_tricks/comment.py -> build/lib.linux-x86_64-2.7/json_tricks
    copying json_tricks/nonp.py -> build/lib.linux-x86_64-2.7/json_tricks
    copying json_tricks/decoders.py -> build/lib.linux-x86_64-2.7/json_tricks
    copying json_tricks/utils.py -> build/lib.linux-x86_64-2.7/json_tricks
    copying json_tricks/encoders.py -> build/lib.linux-x86_64-2.7/json_tricks
    copying json_tricks/np_utils.py -> build/lib.linux-x86_64-2.7/json_tricks
    copying json_tricks/__init__.py -> build/lib.linux-x86_64-2.7/json_tricks
    creating build/lib.linux-x86_64-2.7/tests
    copying tests/test_bare.py -> build/lib.linux-x86_64-2.7/tests
    copying tests/test_enum.py -> build/lib.linux-x86_64-2.7/tests
    copying tests/test_tz.py -> build/lib.linux-x86_64-2.7/tests
    copying tests/test_class.py -> build/lib.linux-x86_64-2.7/tests
    copying tests/test_pandas.py -> build/lib.linux-x86_64-2.7/tests
    copying tests/__init__.py -> build/lib.linux-x86_64-2.7/tests
    copying tests/test_utils.py -> build/lib.linux-x86_64-2.7/tests
    copying tests/test_np.py -> build/lib.linux-x86_64-2.7/tests
    running egg_info
    writing json_tricks.egg-info/PKG-INFO
    writing top-level names to json_tricks.egg-info/top_level.txt
    writing dependency_links to json_tricks.egg-info/dependency_links.txt
    reading manifest file 'json_tricks.egg-info/SOURCES.txt'
    writing manifest file 'json_tricks.egg-info/SOURCES.txt'
    running install_lib
    creating /tmp/venv/lib/python2.7/site-packages/json_tricks
    copying build/lib.linux-x86_64-2.7/json_tricks/np.py -> /tmp/venv/lib/python2.7/site-packages/json_tricks
    copying build/lib.linux-x86_64-2.7/json_tricks/comment.py -> /tmp/venv/lib/python2.7/site-packages/json_tricks
    copying build/lib.linux-x86_64-2.7/json_tricks/nonp.py -> /tmp/venv/lib/python2.7/site-packages/json_tricks
    copying build/lib.linux-x86_64-2.7/json_tricks/decoders.py -> /tmp/venv/lib/python2.7/site-packages/json_tricks
    copying build/lib.linux-x86_64-2.7/json_tricks/utils.py -> /tmp/venv/lib/python2.7/site-packages/json_tricks
    copying build/lib.linux-x86_64-2.7/json_tricks/encoders.py -> /tmp/venv/lib/python2.7/site-packages/json_tricks
    copying build/lib.linux-x86_64-2.7/json_tricks/np_utils.py -> /tmp/venv/lib/python2.7/site-packages/json_tricks
    copying build/lib.linux-x86_64-2.7/json_tricks/__init__.py -> /tmp/venv/lib/python2.7/site-packages/json_tricks
    creating /tmp/venv/lib/python2.7/site-packages/tests
    copying build/lib.linux-x86_64-2.7/tests/test_bare.py -> /tmp/venv/lib/python2.7/site-packages/tests
    copying build/lib.linux-x86_64-2.7/tests/test_enum.py -> /tmp/venv/lib/python2.7/site-packages/tests
    copying build/lib.linux-x86_64-2.7/tests/test_tz.py -> /tmp/venv/lib/python2.7/site-packages/tests
    copying build/lib.linux-x86_64-2.7/tests/test_class.py -> /tmp/venv/lib/python2.7/site-packages/tests
    copying build/lib.linux-x86_64-2.7/tests/test_pandas.py -> /tmp/venv/lib/python2.7/site-packages/tests
    copying build/lib.linux-x86_64-2.7/tests/__init__.py -> /tmp/venv/lib/python2.7/site-packages/tests
    copying build/lib.linux-x86_64-2.7/tests/test_utils.py -> /tmp/venv/lib/python2.7/site-packages/tests
    copying build/lib.linux-x86_64-2.7/tests/test_np.py -> /tmp/venv/lib/python2.7/site-packages/tests
    byte-compiling /tmp/venv/lib/python2.7/site-packages/json_tricks/np.py to np.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/json_tricks/comment.py to comment.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/json_tricks/nonp.py to nonp.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/json_tricks/decoders.py to decoders.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/json_tricks/utils.py to utils.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/json_tricks/encoders.py to encoders.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/json_tricks/np_utils.py to np_utils.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/json_tricks/__init__.py to __init__.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/tests/test_bare.py to test_bare.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/tests/test_enum.py to test_enum.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/tests/test_tz.py to test_tz.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/tests/test_class.py to test_class.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/tests/test_pandas.py to test_pandas.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/tests/__init__.py to __init__.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/tests/test_utils.py to test_utils.pyc
    byte-compiling /tmp/venv/lib/python2.7/site-packages/tests/test_np.py to test_np.pyc
    running install_data
    copying LICENSE.txt -> /tmp/venv/
    error: can't copy 'README.txt': doesn't exist or not a regular file
    
    ----------------------------------------
Command "/tmp/venv/bin/python2 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-bdY5b3/json-tricks/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /home/yoh/.tmp/pip-kiI8oF-record/install-record.txt --single-version-externally-managed --compile --install-headers /tmp/venv/include/site/python2.7/json-tricks" failed with error code 1 in /tmp/pip-build-bdY5b3/json-tricks/

$> find venv/ -iname tests
venv/lib/python2.7/site-packages/tests

FWIW -- the LICENSE file is indeed now shipped (but again -- that wasn't correct fix):

$> tar -tzvf <(wget -q -O- 'https://pypi.python.org/packages/1e/a7/e5a1be23db2664b77423ee90317dbe88146ee42456e3aafcdad8fafa3e51/json_tricks-3.11.1.tar.gz#md5=e50bef5981e0b8b1808750c57df06f30')

Redirecting output to ‘wget-log’.
drwxr-s--- mark/mark         0 2018-01-05 02:49 json_tricks-3.11.1/
drwxr-s--- mark/mark         0 2018-01-05 02:49 json_tricks-3.11.1/json_tricks/
-rw-r----- mark/mark      9981 2017-12-03 10:04 json_tricks-3.11.1/json_tricks/nonp.py
-rw-r----- mark/mark       347 2017-12-01 14:22 json_tricks-3.11.1/json_tricks/np_utils.py
-rw-r----- mark/mark      1195 2017-12-03 09:03 json_tricks-3.11.1/json_tricks/np.py
-rw-r----- mark/mark      5350 2017-12-03 13:17 json_tricks-3.11.1/json_tricks/utils.py
-rw-r----- mark/mark      1403 2017-12-01 14:48 json_tricks-3.11.1/json_tricks/__init__.py
-rw-r----- mark/mark     12394 2017-12-03 10:07 json_tricks-3.11.1/json_tricks/encoders.py
-rw-r----- mark/mark      8603 2017-12-03 10:01 json_tricks-3.11.1/json_tricks/decoders.py
-rw-r----- mark/mark       833 2017-12-01 14:22 json_tricks-3.11.1/json_tricks/comment.py
-rw-r----- mark/mark     18488 2018-01-05 02:49 json_tricks-3.11.1/PKG-INFO
-rw-r----- mark/mark      1540 2017-12-01 14:22 json_tricks-3.11.1/LICENSE.txt
-rw-r----- mark/mark      1318 2017-12-03 08:37 json_tricks-3.11.1/tox.ini
-rw-r----- mark/mark     14244 2017-12-03 09:34 json_tricks-3.11.1/README.rst
-rwx------ mark/mark       109 2018-01-05 02:49 json_tricks-3.11.1/setup.cfg
drwxr-s--- mark/mark         0 2018-01-05 02:49 json_tricks-3.11.1/tests/
-rw-r----- mark/mark      2102 2017-12-01 14:22 json_tricks-3.11.1/tests/test_tz.py
-rw-r----- mark/mark      2095 2017-12-03 06:16 json_tricks-3.11.1/tests/test_pandas.py
-rw-r----- mark/mark      4586 2017-12-03 09:29 json_tricks-3.11.1/tests/test_np.py
-rw-r----- mark/mark       434 2017-12-01 14:22 json_tricks-3.11.1/tests/test_utils.py
-rw-r----- mark/mark         0 2017-12-01 14:22 json_tricks-3.11.1/tests/__init__.py
-rw-r----- mark/mark     13090 2017-12-03 13:10 json_tricks-3.11.1/tests/test_bare.py
-rw-r----- mark/mark      1678 2017-12-03 06:16 json_tricks-3.11.1/tests/test_class.py
-rw-r----- mark/mark      2869 2017-12-03 13:28 json_tricks-3.11.1/tests/test_enum.py
drwxr-s--- mark/mark         0 2018-01-05 02:49 json_tricks-3.11.1/json_tricks.egg-info/
-rw-r----- mark/mark       552 2018-01-05 02:49 json_tricks-3.11.1/json_tricks.egg-info/SOURCES.txt
-rw-r----- mark/mark     18488 2018-01-05 02:49 json_tricks-3.11.1/json_tricks.egg-info/PKG-INFO
-rw-r----- mark/mark         1 2018-01-05 02:49 json_tricks-3.11.1/json_tricks.egg-info/dependency_links.txt
-rw-r----- mark/mark         1 2017-12-03 05:41 json_tricks-3.11.1/json_tricks.egg-info/not-zip-safe
-rw-r----- mark/mark        18 2018-01-05 02:49 json_tricks-3.11.1/json_tricks.egg-info/top_level.txt
-rw-r----- mark/mark      2330 2018-01-05 02:47 json_tricks-3.11.1/setup.py

Most probably need to carry MANIFEST.in listing those instead ... I am just not clear why it worked for me before using sdist and how distutils-vs-setuptools difference mentioned in #63 was in effect since setup.py explicitly imports setuptools

Support pandas dataframes

Of course optionally, similar to numpy arrays.

There are several formats, though I feel that column-based vectors would make most sense...

Unterminated string exception in object containing empty string

Setup

JSON data containing an empty string, e.g.:

import json_tricks

json_data = u'{"foo": "", "bar": "http://google.com"}'
data = json_tricks.loads(json_data)

Expected behaviour

Data is parsed successfully.

Observed behaviour

The following exception occurs:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-50-b6ad4747ea59> in <module>()
      2
      3 json_data = u'{"foo": "", "bar": "http://google.com"}'
----> 4 data = json_tricks.loads(json_data)

C:\Users\chris\Miniconda2-x86\envs\db-dev-copy\lib\site-packages\json_tricks\nonp.pyc in loads(string, preserve_order, ignore_comments, decompression, obj_pairs_hooks, extra_obj_pairs_hooks, cls_lookup_map, allow_duplicates, conv_str_byte, **jsonkwargs)
    199         hooks = tuple(extra_obj_pairs_hooks) + obj_pairs_hooks
    200         hook = TricksPairHook(ordered=preserve_order, obj_pairs_hooks=hooks, allow_duplicates=allow_duplicates)
--> 201         return json_loads(string, object_pairs_hook=hook, **jsonkwargs)
    202
    203

C:\Users\chris\Miniconda2-x86\envs\db-dev-copy\lib\json\__init__.pyc in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    350     if parse_constant is not None:
    351         kw['parse_constant'] = parse_constant
--> 352     return cls(encoding=encoding, **kw).decode(s)

C:\Users\chris\Miniconda2-x86\envs\db-dev-copy\lib\json\decoder.pyc in decode(self, s, _w)
    362
    363         """
--> 364         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    365         end = _w(s, end).end()
    366         if end != len(s):

C:\Users\chris\Miniconda2-x86\envs\db-dev-copy\lib\json\decoder.pyc in raw_decode(self, s, idx)
    378         """
    379         try:
--> 380             obj, end = self.scan_once(s, idx)
    381         except StopIteration:
    382             raise ValueError("No JSON object could be decoded")

ValueError: Unterminated string starting at: line 1 column 20 (char 19)

Note that this works as expected using json.loads(...).

Workaround

If the data is split onto separate lines, the issue seems to go away, e.g.:

import json
import json_tricks

json_data = u'{"foo": "", "bar": "http://google.com"}'
# Reformat JSON data onto separate lines.
json_data = json.dumps(json.loads(json_data), indent=4)
print(json_data)
# The following works now.
json_tricks.loads(json_data)

Easily combine encoders

Like hooks, these should be easy to chain.

Possibly for version 3.0 since it'll change the interface somewhat

Improved compression using binary format

Some work is already underway for an (optional) binary serialization as part of this package. I think this will help compression quite a bit. I have no idea if/when it will be finished.

Enum support by default

Hi,

Since Python 3.4 we can create enum natively with the enum package. This feature is also available with enum34 for previous versions of python. I think this can be useful to include enum support directly in json-tricks.

Since I had to write an implementation for that today, I'll gladely contribute if you want.

Binary mode

Check if files really need to be opened in binary mode or that I can make it work otherwise too. (This is a problem in Python3 only). Preferably I'd like to do it without assuming an encoding/decoding.

I already added clear error messages so this is only an inconvenience, but I'd prefer to have it just work, since it seems a useless restriction.

tarball distributed from PyPI lacks tests and LICENSE.txt

Not sure how that tarball was produced but it seems to be quite "distilled":

$> tar -tzvf <(wget -q -O- 'https://pypi.python.org/packages/90/b2/37141b404698de9f8fd44ba691c23e0533aa88b3b911b44fc26edeb3afd0/json_tricks-3.11.0.tar.gz#md5=a6e3cdff859c38ca6eecdd8b62498dcf') 

Redirecting output to ‘wget-log’.
drwxr-s--- mark/mark         0 2017-12-03 13:33 json_tricks-3.11.0/
drwxr-s--- mark/mark         0 2017-12-03 13:33 json_tricks-3.11.0/json_tricks/
-rw-r----- mark/mark      9981 2017-12-03 10:04 json_tricks-3.11.0/json_tricks/nonp.py
-rw-r----- mark/mark       347 2017-12-01 14:22 json_tricks-3.11.0/json_tricks/np_utils.py
-rw-r----- mark/mark      1195 2017-12-03 09:03 json_tricks-3.11.0/json_tricks/np.py
-rw-r----- mark/mark      5350 2017-12-03 13:17 json_tricks-3.11.0/json_tricks/utils.py
-rw-r----- mark/mark      1403 2017-12-01 14:48 json_tricks-3.11.0/json_tricks/__init__.py
-rw-r----- mark/mark     12394 2017-12-03 10:07 json_tricks-3.11.0/json_tricks/encoders.py
-rw-r----- mark/mark      8603 2017-12-03 10:01 json_tricks-3.11.0/json_tricks/decoders.py
-rw-r----- mark/mark       833 2017-12-01 14:22 json_tricks-3.11.0/json_tricks/comment.py
-rw-r----- mark/mark     18488 2017-12-03 13:33 json_tricks-3.11.0/PKG-INFO
-rw-r----- mark/mark     14244 2017-12-03 09:34 json_tricks-3.11.0/README.rst
-rwx------ mark/mark       109 2017-12-03 13:33 json_tricks-3.11.0/setup.cfg
drwxr-s--- mark/mark         0 2017-12-03 13:33 json_tricks-3.11.0/json_tricks.egg-info/
-rw-r----- mark/mark       381 2017-12-03 13:33 json_tricks-3.11.0/json_tricks.egg-info/SOURCES.txt
-rw-r----- mark/mark     18488 2017-12-03 13:33 json_tricks-3.11.0/json_tricks.egg-info/PKG-INFO
-rw-r----- mark/mark         1 2017-12-03 13:33 json_tricks-3.11.0/json_tricks.egg-info/dependency_links.txt
-rw-r----- mark/mark         1 2017-12-03 05:41 json_tricks-3.11.0/json_tricks.egg-info/not-zip-safe
-rw-r----- mark/mark        12 2017-12-03 13:33 json_tricks-3.11.0/json_tricks.egg-info/top_level.txt
-rw-r----- mark/mark      2241 2017-12-03 12:58 json_tricks-3.11.0/setup.py

whenever using standard "setup.py sdist" seems to do the right thing and ship tests (needed to verify correct functioning locally) and LICENSE.txt (referenced in setup.py):

(git)hopa:~exppsy/json_tricks[tags/v3.11.0]git
$> python setup.py sdist
running sdist      
running egg_info
creating json_tricks.egg-info
writing json_tricks.egg-info/PKG-INFO
writing top-level names to json_tricks.egg-info/top_level.txt
writing dependency_links to json_tricks.egg-info/dependency_links.txt
writing manifest file 'json_tricks.egg-info/SOURCES.txt'
writing manifest file 'json_tricks.egg-info/SOURCES.txt'
running check
creating json_tricks-3.11.0
creating json_tricks-3.11.0/docs
creating json_tricks-3.11.0/json_tricks
creating json_tricks-3.11.0/json_tricks.egg-info
creating json_tricks-3.11.0/tests
copying files to json_tricks-3.11.0...
copying .gitignore -> json_tricks-3.11.0
copying .travis.yml -> json_tricks-3.11.0
copying LICENSE.txt -> json_tricks-3.11.0
copying README.rst -> json_tricks-3.11.0
copying setup.cfg -> json_tricks-3.11.0
copying setup.py -> json_tricks-3.11.0
copying tox.ini -> json_tricks-3.11.0
copying docs/.gitignore -> json_tricks-3.11.0/docs
copying docs/Makefile -> json_tricks-3.11.0/docs
copying docs/conf.py -> json_tricks-3.11.0/docs
copying docs/index.rst -> json_tricks-3.11.0/docs
copying json_tricks/__init__.py -> json_tricks-3.11.0/json_tricks
copying json_tricks/comment.py -> json_tricks-3.11.0/json_tricks
copying json_tricks/decoders.py -> json_tricks-3.11.0/json_tricks
copying json_tricks/encoders.py -> json_tricks-3.11.0/json_tricks
copying json_tricks/nonp.py -> json_tricks-3.11.0/json_tricks
copying json_tricks/np.py -> json_tricks-3.11.0/json_tricks
copying json_tricks/np_utils.py -> json_tricks-3.11.0/json_tricks
copying json_tricks/utils.py -> json_tricks-3.11.0/json_tricks
copying json_tricks.egg-info/PKG-INFO -> json_tricks-3.11.0/json_tricks.egg-info
copying json_tricks.egg-info/SOURCES.txt -> json_tricks-3.11.0/json_tricks.egg-info
copying json_tricks.egg-info/dependency_links.txt -> json_tricks-3.11.0/json_tricks.egg-info
copying json_tricks.egg-info/not-zip-safe -> json_tricks-3.11.0/json_tricks.egg-info
copying json_tricks.egg-info/top_level.txt -> json_tricks-3.11.0/json_tricks.egg-info
copying tests/__init__.py -> json_tricks-3.11.0/tests
copying tests/test_bare.py -> json_tricks-3.11.0/tests
copying tests/test_class.py -> json_tricks-3.11.0/tests
copying tests/test_enum.py -> json_tricks-3.11.0/tests
copying tests/test_np.py -> json_tricks-3.11.0/tests
copying tests/test_pandas.py -> json_tricks-3.11.0/tests
copying tests/test_tz.py -> json_tricks-3.11.0/tests
copying tests/test_utils.py -> json_tricks-3.11.0/tests
Writing json_tricks-3.11.0/setup.cfg
creating dist
Creating tar archive
removing 'json_tricks-3.11.0' (and everything under it)
2 16590 [1].....................................:Thu 04 Jan 2018 08:37:26 PM EST:.
(git)hopa:~exppsy/json_tricks[tags/v3.11.0]git
$> tar -tzvf dist/json_tricks-3.11.0.tar.gz 
drwx--S--- yoh/yoh           0 2018-01-04 20:37 json_tricks-3.11.0/
drwx--S--- yoh/yoh           0 2018-01-04 20:37 json_tricks-3.11.0/tests/
-rw------- yoh/yoh        2102 2018-01-04 20:36 json_tricks-3.11.0/tests/test_tz.py
-rw------- yoh/yoh       13090 2018-01-04 20:36 json_tricks-3.11.0/tests/test_bare.py
-rw------- yoh/yoh        2095 2018-01-04 20:36 json_tricks-3.11.0/tests/test_pandas.py
-rw------- yoh/yoh         434 2018-01-04 20:36 json_tricks-3.11.0/tests/test_utils.py
-rw------- yoh/yoh           0 2018-01-04 20:36 json_tricks-3.11.0/tests/__init__.py
-rw------- yoh/yoh        1678 2018-01-04 20:36 json_tricks-3.11.0/tests/test_class.py
-rw------- yoh/yoh        4586 2018-01-04 20:36 json_tricks-3.11.0/tests/test_np.py
-rw------- yoh/yoh        2869 2018-01-04 20:36 json_tricks-3.11.0/tests/test_enum.py
-rw------- yoh/yoh         121 2018-01-04 20:36 json_tricks-3.11.0/.travis.yml
drwx--S--- yoh/yoh           0 2018-01-04 20:37 json_tricks-3.11.0/json_tricks.egg-info/
-rw------- yoh/yoh          12 2018-01-04 20:37 json_tricks-3.11.0/json_tricks.egg-info/top_level.txt
-rw------- yoh/yoh       18522 2018-01-04 20:37 json_tricks-3.11.0/json_tricks.egg-info/PKG-INFO
-rw------- yoh/yoh           1 2018-01-04 20:37 json_tricks-3.11.0/json_tricks.egg-info/not-zip-safe
-rw------- yoh/yoh         633 2018-01-04 20:37 json_tricks-3.11.0/json_tricks.egg-info/SOURCES.txt
-rw------- yoh/yoh           1 2018-01-04 20:37 json_tricks-3.11.0/json_tricks.egg-info/dependency_links.txt
drwx--S--- yoh/yoh           0 2018-01-04 20:37 json_tricks-3.11.0/docs/
-rw------- yoh/yoh        5061 2018-01-04 20:36 json_tricks-3.11.0/docs/index.rst
-rw------- yoh/yoh        7429 2018-01-04 20:36 json_tricks-3.11.0/docs/Makefile
-rw------- yoh/yoh          30 2018-01-04 20:36 json_tricks-3.11.0/docs/.gitignore
-rw------- yoh/yoh        9182 2018-01-04 20:36 json_tricks-3.11.0/docs/conf.py
-rw------- yoh/yoh       18522 2018-01-04 20:37 json_tricks-3.11.0/PKG-INFO
-rw------- yoh/yoh        1318 2018-01-04 20:36 json_tricks-3.11.0/tox.ini
-rw------- yoh/yoh         109 2018-01-04 20:37 json_tricks-3.11.0/setup.cfg
-rw------- yoh/yoh       14244 2018-01-04 19:12 json_tricks-3.11.0/README.rst
-rw------- yoh/yoh         125 2018-01-04 20:36 json_tricks-3.11.0/.gitignore
-rw------- yoh/yoh        1540 2018-01-04 20:36 json_tricks-3.11.0/LICENSE.txt
drwx--S--- yoh/yoh           0 2018-01-04 20:37 json_tricks-3.11.0/json_tricks/
-rw------- yoh/yoh        8603 2018-01-04 19:12 json_tricks-3.11.0/json_tricks/decoders.py
-rw------- yoh/yoh       12394 2018-01-04 19:12 json_tricks-3.11.0/json_tricks/encoders.py
-rw------- yoh/yoh         347 2018-01-04 19:12 json_tricks-3.11.0/json_tricks/np_utils.py
-rw------- yoh/yoh         833 2018-01-04 19:12 json_tricks-3.11.0/json_tricks/comment.py
-rw------- yoh/yoh        9981 2018-01-04 19:12 json_tricks-3.11.0/json_tricks/nonp.py
-rw------- yoh/yoh        1403 2018-01-04 19:12 json_tricks-3.11.0/json_tricks/__init__.py
-rw------- yoh/yoh        5350 2018-01-04 19:12 json_tricks-3.11.0/json_tricks/utils.py
-rw------- yoh/yoh        1195 2018-01-04 19:12 json_tricks-3.11.0/json_tricks/np.py
-rw------- yoh/yoh        2241 2018-01-04 19:12 json_tricks-3.11.0/setup.py

Dump numpy data with optional precision

Hei,

Thanks for this great library, the ease of dumping numpy data is really impressive. I am wondering, could it be possible to add one more param to control the saving precision of numpy data? Since in most cases, one float of three decimal place is enough (0.123 is okay, 0.129999.. is not necessary).

Any ideas?

does not preserve object array shapes when they contain numpy arrays

import numpy as np
a = np.zeros((2, 2), dtype=object)
for i in np.ndindex(a.shape): 
    a[i] = np.array([1, 2, 3])

import json_tricks
b = json_tricks.loads(json_tricks.dumps(a))

print(b.shape)
print(a.shape)

assert b.shape == a.shape
(2, 2, 3)
(2, 2)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-1-f976420f7432> in <module>
     10 print(a.shape)
     11 
---> 12 assert b.shape == a.shape

AssertionError:

Install information

json_tricks               3.12.2                     py_0    conda-forge
numpy                     1.16.0          py37_blas_openblash1522bff_1000  [blas_openblas]  conda-forge
python                    3.7.2                h0371630_0  

pretty sure it happened on windows too

cc: @EricThomson

Python 3.7 support

Pyjson-tricks should support Python 3.7. I've no reason to expect it not to work, but the tests should run. I tried in 76d09fc but it didn't run on Travis, and Python3.7 fails to install locally.

Include JSONDecodeError in json_tricks module

Could you please include the standard JSONDecodeError class in the json_tricks package as a convenience?
I often find myself having to import that class from the standard json module.

Discard metadata and store as approximate Python type

Add one option to make all the serializes produce output that is as close to primitive types as possible. This will:

  • Not restore the same object type when encoded and decoded (using json-tricks).
  • Be easy for other json libraries to load something comparable.

For example:

  • Numpy arrays as nested lists (without meta data).
  • Fraction, Decimal, complex and numpy scalars as floats and ints.
  • Datetimes possibly as string representations.
  • Sets as lists.

I'm not sure how to handle class instances.

Handle encoding for numpy array scalars

The json_tricks library will serialize instances of numpy.ndarray but if you pass it an array scalar (e.g., a single element of an array, which have datatypes like numpy.int64, numpy.float64, etc.)

For example:

>>> z = np.array([1,2,3])  
>>> json_tricks.dumps(z)
'{"dtype": "int64", "shape": [3], "__ndarray__": [1, 2, 3]}'
>>> json_tricks.dumps(z[0])
TypeError: Object of type <class 'numpy.int64'> could not be encoded by TricksEncoder using encoders [<function json_date_time_encode at 0x7f38028fea60>, <function class_instance_encode at 0x7f38028febf8>, <function json_complex_encode at 0x7f38028fec80>, <function numpy_encode at 0x7f3802410400>]

My present workaround is to ensure that all array scalars are converted to numpy arrays before attempting to serialize (json_tricks.dumps(np.array(z[0])) in the above) but it would be easier if this was handled automatically.

Ignoring errors during complex object encoding

I'm serializing a complex object that has several children that I can safely ignore (RLocks, XMLElements, etc). I don't need these objects to serialize, but I do need the rest of the encoding to continue.

Prior to using silence_typeerrors on the default encoder, it fails on the Rlock below:

Traceback (most recent call last):
File "test.py", line 10, in
json = jt.dumps(s, silence_typeerror=True)
File "/usr/local/lib/python3.6/site-packages/json_tricks/nonp.py", line 90, in dumps
primitives=primitives, **jsonkwargs).encode(obj)
File "/usr/local/lib/python3.6/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/local/lib/python3.6/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/usr/local/lib/python3.6/site-packages/json_tricks/encoders.py", line 58, in default
type(obj), self.class.name, ', '.join(str(encoder) for encoder in self.obj_encoders)))
TypeError: Object of type <class '_thread.RLock'> could not be encoded by TricksEncoder using encoders [<function nopandas_encode at 0x7fc9f798cd90>, <function nonumpy_encode at 0x7fc9f798cea0>, <function enum_instance_encode at 0x7fc9f798c8c8>, <function json_date_time_encode at 0x7fc9f79c26a8>, <function json_complex_encode at 0x7fc9f798ca60>, <function json_set_encode at 0x7fc9f798cb70>, <function numeric_types_encode at 0x7fc9f798cae8>, <function class_instance_encode at 0x7fc9f798c9d8>]

After passing silence_typeerrors kwargs, it gets past the RLock, but fails on an XMLElement with a TypeError.

Traceback (most recent call last):
File "test.py", line 12, in
json = jt.dumps(s, kwargs)
File "/usr/local/lib/python3.6/site-packages/json_tricks/nonp.py", line 90, in dumps
primitives=primitives, **jsonkwargs).encode(obj)
File "/usr/local/lib/python3.6/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/local/lib/python3.6/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/usr/local/lib/python3.6/site-packages/json_tricks/encoders.py", line 58, in default
type(obj), self.class.name, ', '.join(str(encoder) for encoder in self.obj_encoders)))
TypeError: Object of type <class 'xml.etree.ElementTree.Element'> could not be encoded by TricksEncoder using encoders [<function nopandas_encode at 0x7f95faba0d90>, <function nonumpy_encode at 0x7f95faba0ea0>, <function enum_instance_encode at 0x7f95faba08c8>, <function json_date_time_encode at 0x7f95fabd76a8>, <function json_complex_encode at 0x7f95faba0a60>, <function json_set_encode at 0x7f95faba0b70>, <function numeric_types_encode at 0x7f95faba0ae8>, <function class_instance_encode at 0x7f95faba09d8>]

Is there another way to ignore unserializable objects, or am I using something incorrectly?

Bug: JSONDecodeError with valid string

@mverleg Hi Mark, nice to meet you, first of all, thanks for creating this little library, it's quite handy one... today I've found a little bug.

Could you please take a look & advice?

>>> import json
>>> json.loads(json.dumps('a.b("\\\\", "/")\nc = \'"{}"\'.d(e)\nf.g("#")\n'))
'a.b("\\\\", "/")\nc = \'"{}"\'.d(e)\nf.g("#")\n'

>>> import json_tricks
>>> json_tricks.loads(json_tricks.dumps('a.b("\\\\", "/")\nc = \'"{}"\'.d(e)\nf.g("#")\n'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\virtual_envs\py364_32\lib\site-packages\json_tricks\nonp.py", line 213, in loads
    return json_loads(string, object_pairs_hook=hook, **jsonkwargs)
  File "d:\software\python364_32\Lib\json\__init__.py", line 368, in loads
    return cls(**kw).decode(s)
  File "d:\software\python364_32\Lib\json\decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "d:\software\python364_32\Lib\json\decoder.py", line 355, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 1 (char 0)

Using json-tricks==3.13.1 + python 3.6.2 + win7 over here.

Ps. you can see the same data is encoded/decoded perfectly using json but it crashes when using json_tricks

Add support for Decimal type

Decimals are not supported, but it would be a nice addition:

>> import json_tricks.nonp as json
>>> from decimal import *
>>> d = Decimal(1) / Decimal(7)
>>> d
Decimal('0.1428571428571428571428571429')
>>> json.dumps(d)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    json.dumps(d)
  File "/Users/regreinh/.virtualenvs/jsontricks/lib/python3.6/site-packages/json_tricks/nonp.py", line 79, in dumps
    string = cls(sort_keys=sort_keys, obj_encoders=encoders, **jsonkwargs).encode(obj)
  File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/Users/regreinh/.virtualenvs/jsontricks/lib/python3.6/site-packages/json_tricks/encoders.py", line 54, in default
    type(obj), self.__class__.__name__, ', '.join(str(encoder) for encoder in self.obj_encoders)))
TypeError: Object of type <class 'decimal.Decimal'> could not be encoded by TricksEncoder using encoders [<function json_date_time_encode at 0x101e79d90>, <function class_instance_encode at 0x101f2ad08
>, <function json_complex_encode at 0x101f2ad90>, <function json_set_encode at 0x101f2ae18>, <function nonumpy_encode at 0x101fbf510>]
>>>

Invalid datetime decoding

Do not use tzinfo in datetime/time constuctor with pytz. This will produce datetime/time objects with incorrect timezone in most cases (from pytz documentation):

Unfortunately using the tzinfo argument of the standard datetime constructors ”does not work” with pytz for many timezones.

>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt)
'2002-10-27 12:00:00 LMT+0020'
It is safe for timezones without daylight saving transitions though, such as UTC:

>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=pytz.utc).strftime(fmt)
'2002-10-27 12:00:00 UTC+0000'

so, the proper way of constructing datetime/time objects with timezone is to create naive object, then use localize method of pytz timezone class. like this:

>>> amsterdam.localize(datetime(2002, 10, 27, 12, 0, 0)).strftime(fmt)
'2002-10-27 12:00:00 CET+0100'

Other languages

Which other languages need support for reading and writing json_tricks types? Which languages does Python often exchange arrays, datetimes, etc with? (Class instances aren't going to work of course.)

travis-ci timeout (but works locally)

Unit tests work for me locally, but on Travis the Python3.4 version times out (all others including 3.5 work).

(It doesn't seem like a real problem for the code, so I don't want to spend much time fixing it...)

Add support for <type 'time.struct_time'>

I was hoping that this library would be able to automatically serialize <type 'time.struct_time'> (Python 9-tuples) into something like RFC 2822. It doesn't yet, but it could! :)

Add an alternative, smaller storage for numpy arrays

Some notes:

  1. Would be ideal if it is extensible to other 'raw' data (like images).
  2. Using more than one save file would be a disadvantage as it's easy to lose parts.
  3. Mixing formats in the same file (e.g. json followed by raw binary) might be difficult for humans and other implementations to read.
  4. Base64 encoding loses efficiency (33% and not great for zipping) - or base85, which may be worse.
  5. Using a python specific format (like .npz) would be disadvantage.
  6. Reading and writing efficiency is important (reading is already really slow).

I can't think of a way that satisfies all these preferences, so I'll leave it open for now.

Create conda recipe to distribute on conda-forge

I'm using JSON tricks (v13.11.0) in LIVVkit and would like to distribute LIVVkit via conda-forge.

However, my build recipe is failing because conda-forge requires all dependencies to be in conda-forge. Therefore, I'd like to build a conda-forge recipe for JSON tricks (for at least version 13.11.0).

@mverleg, would you mind if I created a conda-forge recipe?

I'm happy to volunteer as the maintainer of the conda-forge recipe and I can do it:

  1. out of a branch from my LIVVkit/staged-recipes fork, or
  2. you could fork conda-forge/staged-recipes and I can contribute/help there.

I assume you'd want to be listed as a maintainer as well in the recipe? Note: this will create some spam for you.

Run unit tests automatically for every version

Several goals:

  • Test all Python interpreters/versions that are supported (using tox).
  • Test with and without numpy and pytz (also using tox).
  • Make tests run automatically on commit / pull request (possibly using travis).

Class instance encoder ignores type if class was derived from dict or list

Currently, encoders.class_instance_encode() contains the following lines:

	if isinstance(obj, list) or isinstance(obj, dict):
		return obj

However, this makes it impossible to successfully serialize any class instance that has inherited from either list or dict:

import json_tricks

class Foo(dict):
    def __init__(self):
        self.x = 2

if  __name__ == '__main__':
    f = Foo()
    print(json_tricks.dumps(f))

Output:

{}

The attribute was entirely lost, and so was the class name.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.