Skip to content

Instantly share code, notes, and snippets.

@nyatla
Last active April 28, 2020 12:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nyatla/296a776bc2ecf68ed2e380441ee95864 to your computer and use it in GitHub Desktop.
Save nyatla/296a776bc2ecf68ed2e380441ee95864 to your computer and use it in GitHub Desktop.
lightweight picklelike implementation to json format.
"""pickle可能なオブジェクトをJSONと相互変換します。
Copyright 2020 nyatla
pickle可能なオブジェクトをJSONに変換してシリアライズ/復元します。
変換元のオブジェクトは、配列,辞書,JsonPicklableの継承クラスです。
再帰構造の動作チェックはしてません。
オブジェクトは、{"__cls":str,"d":object}の辞書形式に変換します。
JSONからオブジェクトに戻すときは、"__cls"キーに文字列を持つ要素をクラスインスタンスと判定し、
同名のクラスが存在し、かつそのクラスがpickle可能な場合のみインスタンスの復元を試みます。
それ以外の場合は、そのままjsonオブジェクトに変換します。
このツールはPickleインストラクションを再現しません。データ構造をできる限り"そのまま"Jsonに変換します。
This module serialize/desirialize pickleable objects to/from JSON.
The conversion source object is array, dictionary, and JsonPicklable extend classes.The recursive structure is not checked.
The class object is encoded into the dictionary format of {"__cls": str, "d": object}.
When decode from JSON to an object, the elements are detected that has a character string in the "__cls" key as a class instance.
Attempts to decode instances only if a class name with in globals() and the class is extends picklable.
Otherwise, decode it to json object as it is.
This tool does not reproduce Pickle instructions.
Classes:
JsonPicklable
Functions:
dumps ->str
loads ->object
Licence:
Copyright (c) 2020, nyatla
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
"""
__version__ = '1.0.1'
__all__ = [
'dumps', 'loads','JsonPicklable'
]
#%%
import json
import sys
import inspect
class JsonPicklable:
"""
pickle可能なオブジェクトが継承する修飾クラスです。
dump/loadするクラスは、このクラスを継承する必要があります。
継承先で__getstate__()と__setstate__()を上書きしない場合は、インスタンスの__dict__をシリアライズします。
"""
def _jdump(self):
t=type(self)
return {"__cls":t.__module__+"."+t.__name__,"d":self.__getstate__()}
def __getstate__(self):
odict = self.__dict__.copy()
return odict
def __setstate__(self, state):
self.__dict__.update(state)
def _dump_default(item):
if isinstance(item,JsonPicklable):
return item._jdump()
else:
print("%s %s is not pickleable"%(str(type(item)),str(item)))
raise TypeError
def _load_hook(dct):
class_name = dct.get('__cls')
if type(class_name) is str:
#module内のクラスの一覧を検索
sp=class_name.split(".")
mod=sys.modules.get(".".join(sp[0:-1]))
if mod is not None:
cl={i[1].__module__+"."+i[1].__name__:i[1] for i in inspect.getmembers(mod,inspect.isclass)}
c=cl.get(class_name)
if c is not None:
o=c.__new__(c)
o.__setstate__(dct.get("d"))
return o
return dct
def dumps(bj, *v, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None,sort_keys=False, **kw):
"""
json.dumps互換の関数です。
"""
return json.dumps(bj,default=_dump_default,*v,skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,allow_nan=allow_nan,cls=cls, indent=indent,separators=separators,sort_keys=sort_keys,**kw)
def loads(s, *v, cls=None, parse_float=None, parse_int=None, parse_constant=None,**kw):
"""
json.loads互換の関数です。
"""
return json.loads(s, *v, cls=cls, object_hook=_load_hook,parse_float=parse_float, parse_int=parse_int, parse_constant=parse_constant,**kw)
#%%
if __name__ == "__main__":
"""
テスト
"""
class A(JsonPicklable):
def __init__(self):
self.a=1
self.b="a"
class B(JsonPicklable):
def __init__(self):
self.a=2
self.b="b"
self.c=A()
def __getstate__(self):
"""
Make override if you have owen pickle function.
独自のpickle実装があればこの関数を上書きします。
"""
odict = super().__getstate__()
return odict
def __setstate__(self, state):
"""
Make override if you have owen pickle function.
独自のpickle実装があればこの関数を上書きします。
"""
self.__dict__.update(state)
for b in [
[A(),B()],
[{"__cls":1},A(),B()],
[{"__cls":"name"},A(),B()],
[{"__cls":[A(),B()]}]
]:
print("----")
#ダンプ
ds=dumps(b)
print("dump:"+ds)
#ロード
lo=loads(ds)
print("load:"+str(lo))
#ダンプ
ds2=dumps(lo)
print("dump:"+ds2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment