Last active
April 28, 2020 12:44
-
-
Save nyatla/296a776bc2ecf68ed2e380441ee95864 to your computer and use it in GitHub Desktop.
lightweight picklelike implementation to json format.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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