ある同一のデータを別々のプログラミング言語で扱うといったケースは少なくないかと思います。例えば、API経由でのデータのやり取りや、データの加工や分析だけちょっとpythonを使いたいなどですかね。
そこで今回はJSON形式でのインターフェースとなるようなクラスをとりあえずpythonで用意できないかと考えました。
1. シリアライズ可能なフィールドを持つクラスの例
メインはto_json
関数とfrom_json
関数です
-
to_json
: 生成したオブジェクトからstringのjsonを生成します -
from_json
: stringのjsonからオブジェクトを生成します
import json
class PersonClass:
def __init__(self, name, age):
self.name = name
self.age = age
def to_json(self):
return json.dumps(self.__dict__)
@classmethod
def from_json(cls, json_string):
data = json.loads(json_string)
return cls(**data)
def __eq__(self, other):
if isinstance(other, PersonClass):
return self.__dict__ == other.__dict__
return False
def __hash__(self):
return hash(tuple(sorted(self.__dict__.items())))
def __str__(self):
return f"PersonClass(name={self.name}, age={self.age})"
person_from = PersonClass("bob", 25)
print(person_from) # PersonClass(name=bob, age=25)
print(hash(person_from)) # 4914228952729239745
person_to = PersonClass.from_json(person_from.to_json())
print(person_to) # PersonClass(name=bob, age=25)
print(hash(person_to)) # 4914228952729239745
print(person_from == person_to) # True
2. シリアライズ不可のフィールドがある時
PersonClass
がデフォルトでシリアライズ不可能なobject, Enum型のフィールドを持つ場合を考えます。
詳しくはこちらを参照 : https://qiita.com/vossibop/items/b902ceabf199272c9cf4
変更点としては以下です
-
CustomJSONEncoder
でシリアライズ時のオブジェクトの型変換などを定義 -
to_json
内のjson.dumps()
で上記encoderを使用する -
from_json
内のjson.loads()
で得たdict型をそのままコンストラクタに渡すとオブジェクトのインスタンス生成がうまくできないので以下のようにして渡すreturn cls(data['name'], data['age'], Country(**data['country']))
import json
from datetime import date
from enum import Enum
class CustomJSONEncoder(json.JSONEncoder):
def default(self, obj):
# Enum -> str,int
if isinstance(obj, Enum):
return obj.value
# datetime -> str
if isinstance(obj, datetime):
return obj.isoformat()
# object -> dict
elif isinstance(obj, object) and hasattr(obj, '__dict__'):
return obj.__dict__
# dict,str,int,... -> default
return super().default(obj)
class Language(Enum):
EN = 1
JA = 2
FR = 3
DE = 4
IT = 5
class Country:
def __init__(self, name: str, language: int):
self.name = name
self.language = Language(language)
def to_json(self):
return json.dumps(self.__dict__)
@classmethod
def from_json(cls, json_string):
data = json.loads(json_string)
return cls(**data)
def __eq__(self, other):
if isinstance(other, Country):
return self.__dict__ == other.__dict__
return False
def __hash__(self):
return hash(tuple(sorted(self.__dict__.items())))
def __str__(self):
return f"Country(name={self.name}, language={self.language})"
class PersonClass:
def __init__(self, name: str, age: int, country: Country):
self.name = name
self.age = age
self.country = country
def to_json(self, encoder: JSONEncoder):
return json.dumps(self, cls=encoder)
@classmethod
def from_json(cls, json_string):
data = json.loads(json_string)
return cls(data['name'], data['age'], Country(**data['country']))
def __eq__(self, other):
if isinstance(other, PersonClass):
return self.__dict__ == other.__dict__
return False
def __hash__(self):
return hash(tuple(sorted(self.__dict__.items())))
def __str__(self):
return f"PersonClass(name={self.name}, age={self.age}, country={self.country})"
country = Country("United States", 1)
person_from = PersonClass("bob", 25, country)
print(person_from) # PersonClass(name=bob, age=25, country=Country(name=United States, language=Language.EN))
print(hash(person_from)) # -7954654959397923626
person_to = PersonClass.from_json(person_from.to_json(CustomJSONEncoder))
print(person_to) # PersonClass(name=bob, age=25, country=Country(name=United States, language=Language.EN))
print(hash(person_to)) # -7954654959397923626
print(person_from == person_to) # True