From 60032c0f0a2a5c957067b054788bd3b69c6a3dbb Mon Sep 17 00:00:00 2001 From: Fabian Zills <46721498+PythonFZ@users.noreply.github.com> Date: Fri, 11 Mar 2022 10:16:29 +0100 Subject: [PATCH] add `datetime` example (#13) * Update setup.py * Update __init__.py * add `datetime` example * add keywords * test not serializable --- README.md | 52 +++++++ example/datetime_converter.ipynb | 194 ++++++++++++++++++++++++ setup.py | 4 +- tests/converter/test_class_converter.py | 13 +- znjson/__init__.py | 2 +- 5 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 example/datetime_converter.ipynb diff --git a/README.md b/README.md index 6e67679..be86f2e 100644 --- a/README.md +++ b/README.md @@ -48,4 +48,56 @@ The resulting ``*.json`` file is partially readable and looks like this: 4 ] } +```` + +# Custom Converter + +ZnJSON allows you to easily add custom converters. +Let's write a serializer for ``datetime.datetime``. + +````python +from znjson import ConverterBase +from datetime import datetime + +class DatetimeConverter(ConverterBase): + """Encode/Decode datetime objects + + Attributes + ---------- + level: int + Priority of this converter over others. + A higher level will be used first, if there + are multiple converters available + representation: str + An unique identifier for this converter. + instance: + Used to select the correct converter. + This should fulfill isinstance(other, self.instance) + or __eq__ should be overwritten. + """ + level = 100 + representation = "datetime" + instance = datetime + + def _encode(self, obj: datetime) -> str: + """Convert the datetime object to str / isoformat""" + return obj.isoformat() + def _decode(self, value: str) -> datetime: + """Create datetime object from str / isoformat""" + return datetime.fromisoformat(value) +```` + +This allows us to use this new serializer: +````python +znjson.register(DatetimeConverter) # we need to register the new converter first +json_string = json.dumps(dt, cls=znjson.ZnEncoder, indent=4) +json.loads(json_string, cls=znjson.ZnDecoder) +```` + +and will result in +````json +{ + "_type": "datetime", + "value": "2022-03-11T09:47:35.280331" +} ```` \ No newline at end of file diff --git a/example/datetime_converter.ipynb b/example/datetime_converter.ipynb new file mode 100644 index 0000000..2c0aa3b --- /dev/null +++ b/example/datetime_converter.ipynb @@ -0,0 +1,194 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Write a custom Converter\n", + "\n", + "In this Example we will write a custom converter for `datetime` objects." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 1, + "outputs": [], + "source": [ + "import json\n", + "\n", + "from znjson import ConverterBase\n", + "from datetime import datetime\n", + "import znjson" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2022-03-11 10:02:18.048121\n" + ] + } + ], + "source": [ + "dt = datetime.now()\n", + "print(dt)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [], + "source": [ + "class DatetimeConverter(ConverterBase):\n", + " \"\"\"Encode/Decode datetime objects\n", + "\n", + " Attributes\n", + " ----------\n", + " level: int\n", + " Priority of this converter over others.\n", + " A higher level will be used first, if there\n", + " are multiple converters available\n", + " representation: str\n", + " An unique identifier for this converter.\n", + " instance:\n", + " Used to select the correct converter.\n", + " This should fulfill isinstance(other, self.instance)\n", + " or __eq__ should be overwritten.\n", + " \"\"\"\n", + " level = 100\n", + " representation = \"datetime\"\n", + " instance = datetime\n", + "\n", + " def _encode(self, obj: datetime) -> str:\n", + " \"\"\"Convert the datetime object to str / isoformat\"\"\"\n", + " return obj.isoformat()\n", + " def _decode(self, value: str) -> datetime:\n", + " \"\"\"Create datetime object from str / isoformat\"\"\"\n", + " return datetime.fromisoformat(value)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "To use the new converter we have to add it to `znjson.config.ACTIVE_CONVERTERS` via `znjson.register()`" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [], + "source": [ + "znjson.register(DatetimeConverter)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"_type\": \"datetime\",\n", + " \"value\": \"2022-03-11T10:02:18.048121\"\n", + "}\n" + ] + } + ], + "source": [ + "json_string = json.dumps(dt, cls=znjson.ZnEncoder, indent=4)\n", + "print(json_string)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [ + { + "data": { + "text/plain": "datetime.datetime(2022, 3, 11, 10, 2, 18, 48121)" + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "json.loads(json_string, cls=znjson.ZnDecoder)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/setup.py b/setup.py index 3ced3cd..00c144d 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="znjson", - version="0.1.0", + version="0.1.2", author="zincwarecode", author_email="zincwarecode@gmail.com", description="A Python Package to Encode/Decode some common file formats to json", @@ -14,7 +14,7 @@ long_description_content_type="text/markdown", url="https://github.com/zincware/ZnJSON", download_url="https://github.com/zincware/ZnJSON/archive/beta.tar.gz", - keywords=["json", "zntrack"], + keywords=["json", "zntrack", "jsonpickle", "serialization", "deserialization"], packages=setuptools.find_packages(), classifiers=[ "Development Status :: 3 - Alpha", diff --git a/tests/converter/test_class_converter.py b/tests/converter/test_class_converter.py index 4ca8913..a582776 100644 --- a/tests/converter/test_class_converter.py +++ b/tests/converter/test_class_converter.py @@ -1,12 +1,9 @@ import json -import numpy as np import pytest import znjson -znjson.register(znjson.converter.ClassConverter) - class HelloWorld: def __init__(self): @@ -25,3 +22,13 @@ def test_encode(example_class): def test_decode(example_class): encoded_str = json.dumps(example_class, cls=znjson.ZnEncoder) assert isinstance(json.loads(encoded_str, cls=znjson.ZnDecoder), type(example_class)) + + +def test_unable_to_encode(): + class OutOfScope: + """Can not be imported, so it can not be encoded""" + + pass + + with pytest.raises(TypeError): + json.dumps(OutOfScope(), cls=znjson.ZnEncoder) diff --git a/znjson/__init__.py b/znjson/__init__.py index a56524d..77a7951 100644 --- a/znjson/__init__.py +++ b/znjson/__init__.py @@ -5,6 +5,6 @@ __all__ = ["ConverterBase", "ZnDecoder", "ZnEncoder", "register", "deregister", "config"] -__version__ = "0.1.0" +__version__ = "0.1.2" register()