클래스 JSON을 직렬화 가능하게 만드는 방법
파이썬 클래스를 직렬화하는 방법은 무엇입니까?
간단한 수업 :
class FileItem:
def __init__(self, fname):
self.fname = fname
다음과 같은 결과를 얻으려면 어떻게해야합니까?
json.dumps()
오류 없음 ( FileItem instance at ... is not JSON serializable
)
예상되는 출력에 대한 아이디어가 있습니까? 예를 들어 이것이 할 것인가?
>>> f = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'
이 경우 json.dumps(f.__dict__)
.
더 많은 사용자 정의 출력을 원하면 JSONEncoder
사용자 정의 직렬화 를 하위 클래스 화 하고 구현해야합니다.
간단한 예는 아래를 참조하십시오.
>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
그런 다음이 클래스를 kwarg 로 json.dumps()
메소드에 cls
전달합니다.
json.dumps(cls=MyEncoder)
디코딩도 원한다면 클래스에 커스텀 object_hook
을 제공해야 JSONDecoder
합니다. 예를 들어
>>> def from_json(json_object):
if 'fname' in json_object:
return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>>
다음은 간단한 기능에 대한 간단한 솔루션입니다.
.toJSON()
방법
JSON 직렬화 가능 클래스 대신 직렬화 메소드를 구현하십시오.
import json
class Object:
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
따라서 직렬화하기 위해 호출하면됩니다.
me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"
print(me.toJSON())
다음을 출력합니다.
{
"age": 35,
"dog": {
"name": "Apollo"
},
"name": "Onur"
}
더 복잡한 클래스의 경우 jsonpickle 도구를 고려할 수 있습니다 .
jsonpickle은 복잡한 Python 객체와 JSON 간의 직렬화 및 역 직렬화를위한 Python 라이브러리입니다.
stdlib의 json, simplejson 및 demjson과 같이 Python을 JSON으로 인코딩하기위한 표준 Python 라이브러리는 직접 JSON에 해당하는 Python 기본 형식 (예 : dicts, 목록, 문자열, int 등) 만 처리 할 수 있습니다. jsonpickle은 이러한 라이브러리 위에 빌드되며 더 복잡한 데이터 구조를 JSON으로 직렬화 할 수 있습니다. jsonpickle은 고도로 구성 가능하고 확장 가능하므로 사용자가 JSON 백엔드를 선택하고 추가 백엔드를 추가 할 수 있습니다.
대부분의 답변에는 json.dumps ()에 대한 호출 변경이 포함되며 , 이는 항상 가능하거나 바람직하지는 않습니다 (예 : 프레임 워크 구성 요소 내부에서 발생할 수 있음).
json.dumps (obj)를있는 그대로 호출 하려면 간단한 솔루션이 dict 에서 상속하는 것입니다 .
class FileItem(dict):
def __init__(self, fname):
dict.__init__(self, fname=fname)
f = FileItem('tasks.txt')
json.dumps(f) #No need to change anything here
이것은 클래스가 기본 데이터 표현 일 경우 작동하며, 까다로운 작업을 위해 항상 명시 적으로 키를 설정할 수 있습니다.
또 다른 옵션은 JSON 덤프를 자체 클래스로 래핑하는 것입니다.
import json
class FileItem:
def __init__(self, fname):
self.fname = fname
def __repr__(self):
return json.dumps(self.__dict__)
또는 더 나은 방법은 클래스에서 FileItem 클래스를 서브 클래 싱하는 것 JsonSerializable
입니다.
import json
class JsonSerializable(object):
def toJson(self):
return json.dumps(self.__dict__)
def __repr__(self):
return self.toJson()
class FileItem(JsonSerializable):
def __init__(self, fname):
self.fname = fname
테스트 :
>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'
나는 Onur의 대답을 좋아 하지만 toJSON()
객체가 스스로 직렬화 하는 선택적 메소드 를 포함하도록 확장 합니다.
def dumper(obj):
try:
return obj.toJSON()
except:
return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)
요 전에이 문제를 발견하고 중첩 된 개체 와 상속 된 필드를 처리 할 수있는 Python 개체 용 인코더의보다 일반적인 버전을 구현했습니다 .
import json
import inspect
class ObjectEncoder(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, "to_json"):
return self.default(obj.to_json())
elif hasattr(obj, "__dict__"):
d = dict(
(key, value)
for key, value in inspect.getmembers(obj)
if not key.startswith("__")
and not inspect.isabstract(value)
and not inspect.isbuiltin(value)
and not inspect.isfunction(value)
and not inspect.isgenerator(value)
and not inspect.isgeneratorfunction(value)
and not inspect.ismethod(value)
and not inspect.ismethoddescriptor(value)
and not inspect.isroutine(value)
)
return self.default(d)
return obj
예:
class C(object):
c = "NO"
def to_json(self):
return {"c": "YES"}
class B(object):
b = "B"
i = "I"
def __init__(self, y):
self.y = y
def f(self):
print "f"
class A(B):
a = "A"
def __init__(self):
self.b = [{"ab": B("y")}]
self.c = C()
print json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True)
결과:
{
"a": "A",
"b": [
{
"ab": {
"b": "B",
"i": "I",
"y": "y"
}
}
],
"c": {
"c": "YES"
},
"i": "I"
}
to_json
다음과 같이 클래스 에 메소드를 추가 하십시오.
def to_json(self):
return self.message # or how you want it to be serialized
그리고이 코드 ( 이 답변에서 ) 를 모든 것의 맨 위에 추가하십시오 .
from json import JSONEncoder
def _default(self, obj):
return getattr(obj.__class__, "to_json", _default.default)(obj)
_default.default = JSONEncoder().default
JSONEncoder.default = _default
가져올 때 json 모듈을 원숭이 패치하므로 JSONEncoder.default ()는 특수 "to_json ()"메서드를 자동으로 확인하고 발견 된 경우이를 사용하여 객체를 인코딩합니다.
Onur가 말한 것처럼 이번에는 json.dumps()
프로젝트의 모든 항목을 업데이트 할 필요가 없습니다 .
Python3.5 이상을 사용하는 경우 jsons
. 객체 (및 모든 속성을 재귀 적으로)를 dict로 변환합니다.
import jsons
a_dict = jsons.dump(your_object)
또는 문자열을 원하는 경우 :
a_str = jsons.dumps(your_object)
또는 클래스가 구현 된 경우 jsons.JsonSerializable
:
a_dict = your_object.json
import simplejson
class User(object):
def __init__(self, name, mail):
self.name = name
self.mail = mail
def _asdict(self):
return self.__dict__
print(simplejson.dumps(User('alice', 'alice@mail.com')))
표준 json
을 사용하는 경우 default
함수 를 정의해야 합니다.
import json
def default(o):
return o._asdict()
print(json.dumps(User('alice', 'alice@mail.com'), default=default))
이 클래스는 트릭을 수행 할 수 있으며 객체를 표준 json으로 변환합니다.
import json
class Serializer(object):
@staticmethod
def serialize(object):
return json.dumps(object, default=lambda o: o.__dict__.values()[0])
용법:
Serializer.serialize(my_object)
에서 작업 python2.7
하고 python3
.
json
는 인쇄 할 수있는 개체의 측면에서 제한되며 jsonpickle
(필요할 수 있음 pip install jsonpickle
) 텍스트를 들여 쓸 수 없다는 측면에서 제한됩니다. 클래스를 변경할 수없는 객체의 내용을 검사하고 싶다면 다음보다 더 직접적인 방법을 찾을 수 없습니다.
import json
import jsonpickle
...
print json.dumps(json.loads(jsonpickle.encode(object)), indent=2)
참고 : 여전히 개체 메서드를 인쇄 할 수 없습니다.
import json
class Foo(object):
def __init__(self):
self.bar = 'baz'
self._qux = 'flub'
def somemethod(self):
pass
def default(instance):
return {k: v
for k, v in vars(instance).items()
if not str(k).startswith('_')}
json_foo = json.dumps(Foo(), default=default)
assert '{"bar": "baz"}' == json_foo
print(json_foo)
jaraco 는 꽤 깔끔한 대답을했습니다. 몇 가지 사소한 문제를 해결해야했지만 작동합니다.
암호
# Your custom class
class MyCustom(object):
def __json__(self):
return {
'a': self.a,
'b': self.b,
'__python__': 'mymodule.submodule:MyCustom.from_json',
}
to_json = __json__ # supported by simplejson
@classmethod
def from_json(cls, json):
obj = cls()
obj.a = json['a']
obj.b = json['b']
return obj
# Dumping and loading
import simplejson
obj = MyCustom()
obj.a = 3
obj.b = 4
json = simplejson.dumps(obj, for_json=True)
# Two-step loading
obj2_dict = simplejson.loads(json)
obj2 = MyCustom.from_json(obj2_dict)
# Make sure we have the correct thing
assert isinstance(obj2, MyCustom)
assert obj2.__dict__ == obj.__dict__
로드하려면 두 단계가 필요합니다. 지금은 __python__
속성이 사용되지 않습니다.
이것은 얼마나 흔한가요?
AlJohri 방법을 사용하여 접근 방식의 인기를 확인합니다.
직렬화 (Python-> JSON) :
to_json
: 2018-06-27에 266,595toJSON
: 2018-06-27에 96,307__json__
: 2018-06-27에 8,504for_json
: 2018-06-27에 6,937
역 직렬화 (JSON-> Python) :
from_json
: 2018-06-27에 226,101
이것은 나를 위해 잘 작동했습니다.
class JsonSerializable(object):
def serialize(self):
return json.dumps(self.__dict__)
def __repr__(self):
return self.serialize()
@staticmethod
def dumper(obj):
if "serialize" in dir(obj):
return obj.serialize()
return obj.__dict__
그리고
class FileItem(JsonSerializable):
...
과
log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2))
jsonweb이 나에게 가장 적합한 솔루션 인 것 같습니다. 참조 http://www.jsonweb.info/en/latest/를
from jsonweb.encode import to_object, dumper
@to_object()
class DataModel(object):
def __init__(self, id, value):
self.id = id
self.value = value
>>> data = DataModel(5, "foo")
>>> dumper(data)
'{"__type__": "DataModel", "id": 5, "value": "foo"}'
패키지 설치에 신경 쓰지 않는다면 json-tricks를 사용할 수 있습니다 .
pip install json-tricks
그 후 당신은 단지 가져와야 dump(s)
에서 json_tricks
대신 JSON의, 그리고 작업 보통거야 :
from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)
줄 것이다
{
"__instance_type__": [
"module_name.test_class",
"MyTestCls"
],
"attributes": {
"attr": "val",
"dct_attr": {
"hello": 42
}
}
}
그리고 그게 기본입니다!
이것은 일반적으로 훌륭하게 작동합니다. 예를 들어에서 특별한 일이 발생 __new__
하거나 더 많은 메타 클래스 마법이 진행되는 경우와 같은 몇 가지 예외가 있습니다 .
분명히 로딩도 작동합니다 (그렇지 않으면 요점).
from json_tricks import loads
json_str = loads(json_str)
이것은 module_name.test_class.MyTestCls
가져올 수 있고 호환되지 않는 방식으로 변경되지 않았다고 가정합니다 . 딕셔너리 등이 아닌 인스턴스가 반환되며 덤프 한 것과 동일한 복사본이어야합니다.
어떤 것이 (비) 직렬화되는 방법을 사용자 정의하려면 다음과 같이 클래스에 특수 메서드를 추가 할 수 있습니다.
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
예를 들어 속성 매개 변수의 일부만 직렬화합니다.
그리고 무료 보너스로, numpy 배열, 날짜 및 시간, 정렬 된 맵의 (비) 직렬화 및 json에 주석을 포함 할 수있는 기능을 제공합니다.
면책 조항 : 나는 당신과 같은 문제가 있었기 때문에 json_tricks를 만들었습니다 .
여기 내 3 센트가 있습니다.
이것은 나무와 같은 파이썬 객체에 대한 명시적인 json 직렬화를 보여줍니다.
참고 : 실제로 이와 같은 코드가 필요하다면 트위스트 된 FilePath 클래스를 사용할 수 있습니다 .
import json, sys, os
class File:
def __init__(self, path):
self.path = path
def isdir(self):
return os.path.isdir(self.path)
def isfile(self):
return os.path.isfile(self.path)
def children(self):
return [File(os.path.join(self.path, f))
for f in os.listdir(self.path)]
def getsize(self):
return os.path.getsize(self.path)
def getModificationTime(self):
return os.path.getmtime(self.path)
def _default(o):
d = {}
d['path'] = o.path
d['isFile'] = o.isfile()
d['isDir'] = o.isdir()
d['mtime'] = int(o.getModificationTime())
d['size'] = o.getsize() if o.isfile() else 0
if o.isdir(): d['children'] = o.children()
return d
folder = os.path.abspath('.')
json.dump(File(folder), sys.stdout, default=_default)
Peewee의 모델을 PostgreSQL에 저장하려고 할 때이 문제가 발생했습니다 JSONField
.
한동안 고생 한 후 일반적인 해결책은 다음과 같습니다.
내 솔루션의 핵심은 Python의 소스 코드를 살펴보고 코드 문서 ( 여기에 설명되어 있음 )가 이미 json.dumps
다른 데이터 유형을 지원 하도록 기존 항목 을 확장하는 방법을 설명 하고 있음을 인식하는 것 입니다.
현재 JSON으로 직렬화 할 수없는 일부 필드가 포함 된 모델이 있고 JSON 필드가 포함 된 모델이 원래 다음과 같다고 가정합니다.
class SomeClass(Model):
json_field = JSONField()
다음 JSONEncoder
과 같이 사용자 정의를 정의 하십시오.
class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
return < whatever value you want >
return json.JSONEncoder.default(self, obj)
@staticmethod
def json_dumper(obj):
return json.dumps(obj, cls=CustomJsonEncoder)
그런 다음 JSONField
아래와 같이 사용하십시오 .
class SomeClass(Model):
json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)
핵심은 default(self, obj)
위 의 방법입니다. ... is not JSON serializable
Python으로부터받은 모든 불만 사항에 대해 직렬화 불가능 -JSON 유형 (예 : Enum
또는 datetime
) 을 처리하는 코드를 추가하기 만하면됩니다.
예를 들어, 다음에서 상속하는 클래스를 지원하는 방법은 Enum
다음 과 같습니다.
class TransactionType(Enum):
CURRENT = 1
STACKED = 2
def default(self, obj):
if isinstance(obj, TransactionType):
return obj.value
return json.JSONEncoder.default(self, obj)
마지막으로, 위와 같이 구현 된 코드를 사용하면 모든 Peewee 모델을 아래와 같이 JSON 직렬화 가능한 객체로 변환 할 수 있습니다.
peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)
위의 코드는 (다소) Peewee에만 해당되지만 다음과 같이 생각합니다.
- 일반적으로 다른 ORM (Django 등)에 적용 가능
- 또한
json.dumps
작동 방식 을 이해했다면 이 솔루션은 일반적으로 Python (sans ORM)에서도 작동합니다.
질문이 있으시면 댓글 섹션에 게시하십시오. 감사!
이것은 모든 자식이있는 객체를 JSON으로 직렬화하고 다시 파싱하는 작은 라이브러리입니다.
https://github.com/Toubs/PyJSONSerialization/
나는 내 자신의 해결책을 생각해 냈습니다. 이 메서드를 사용 하여 직렬화 할 문서 ( dict , list , ObjectId 등)를 전달합니다 .
def getSerializable(doc):
# check if it's a list
if isinstance(doc, list):
for i, val in enumerate(doc):
doc[i] = getSerializable(doc[i])
return doc
# check if it's a dict
if isinstance(doc, dict):
for key in doc.keys():
doc[key] = getSerializable(doc[key])
return doc
# Process ObjectId
if isinstance(doc, ObjectId):
doc = str(doc)
return doc
# Use any other custom serializting stuff here...
# For the rest of stuff
return doc
날짜 시간 객체 직렬화 문제를 해결하기 위해 데코레이터를 사용하기로 결정했습니다. 내 코드는 다음과 같습니다.
#myjson.py
#Author: jmooremcc 7/16/2017
import json
from datetime import datetime, date, time, timedelta
"""
This module uses decorators to serialize date objects using json
The filename is myjson.py
In another module you simply add the following import statement:
from myjson import json
json.dumps and json.dump will then correctly serialize datetime and date
objects
"""
def json_serial(obj):
"""JSON serializer for objects not serializable by default json code"""
if isinstance(obj, (datetime, date)):
serial = str(obj)
return serial
raise TypeError ("Type %s not serializable" % type(obj))
def FixDumps(fn):
def hook(obj):
return fn(obj, default=json_serial)
return hook
def FixDump(fn):
def hook(obj, fp):
return fn(obj,fp, default=json_serial)
return hook
json.dumps=FixDumps(json.dumps)
json.dump=FixDump(json.dump)
if __name__=="__main__":
today=datetime.now()
data={'atime':today, 'greet':'Hello'}
str=json.dumps(data)
print str
위의 모듈을 가져 오면 다른 모듈은 기본 키워드를 지정하지 않고 일반적인 방식으로 json을 사용하여 날짜 시간 개체를 포함하는 데이터를 직렬화합니다. datetime serializer 코드는 json.dumps 및 json.dump에 대해 자동으로 호출됩니다.
나는 Lost Koder의 방법을 가장 좋아했습니다. 멤버 / 메소드를 직렬화 할 수없는 더 복잡한 개체를 직렬화하려고 할 때 문제가 발생했습니다. 더 많은 개체에서 작동하는 구현은 다음과 같습니다.
class Serializer(object):
@staticmethod
def serialize(obj):
def check(o):
for k, v in o.__dict__.items():
try:
_ = json.dumps(v)
o.__dict__[k] = v
except TypeError:
o.__dict__[k] = str(v)
return o
return json.dumps(check(obj).__dict__, indent=2)
패키지를 설치할 수 있다면 내 프로젝트에서 잘 작동 하는 dill을 사용해 보는 것이 좋습니다 . 이 패키지의 좋은 점은이 패키지가와 동일한 인터페이스를 가지고 있다는 것입니다 pickle
. 따라서 pickle
프로젝트에서 이미 사용 dill
하고있는 경우 코드를 변경하지 않고 간단히 대체 하여 스크립트가 실행되는지 확인할 수 있습니다. 따라서 시도하는 것은 매우 저렴한 솔루션입니다!
(완전한 공개 금지 : 저는 딜 프로젝트와 관련이 없으며 기여한 적이 없습니다.)
패키지 설치 :
pip install dill
그런 다음 코드를 수정하여 다음 dill
대신 가져올 수 있습니다 pickle
.
# import pickle
import dill as pickle
스크립트를 실행하고 작동하는지 확인하십시오. (그렇다면 더 이상 pickle
모듈 이름을 섀도 잉하지 않도록 코드를 정리할 수 있습니다 !)
프로젝트 페이지dill
에서 직렬화 할 수 있거나 할 수없는 데이터 유형에 대한 몇 가지 세부 사항 :
dill
다음 표준 유형을 피클 할 수 있습니다.none, type, bool, int, long, float, complex, str, unicode, tuple, list, dict, file, buffer, builtin, both old and new style classes, instances of old and new style classes, set, frozenset, array , 함수, 예외
dill
더 많은 '이국적인'표준 유형을 피클 할 수도 있습니다.yield, 중첩 함수, 람다, 셀, 메서드, unboundmethod, 모듈, 코드, methodwrapper, dictproxy, methoddescriptor, getsetdescriptor, memberdescriptor, wrapperdescriptor, xrange, slice, notimplemented, ellipsis, quit가있는 함수
dill
아직 이러한 표준 유형을 피클 할 수 없습니다.프레임, 생성기, 트레이스 백
여기에 직렬 버전 관리 또는 백컴 패트에 대한 언급이 없으므로 잠시 사용했던 솔루션을 게시하겠습니다. 저는 배울 것이 더 많을 것입니다. 특히 Java와 Javascript는 아마도 저보다 성숙 할 것입니다.
https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
이 문제에 대한 많은 접근 방식이 있습니다. 'ObjDict'(pip install objdict)는 다른 것입니다. JSON에서로드 된 데이터를 가장 잘 처리하기 위해 딕셔너리처럼 작동 할 수도있는 객체와 같은 자바 스크립트를 제공하는 데 중점을 두지 만 유용 할 수있는 다른 기능도 있습니다. 이것은 원래 문제에 대한 또 다른 대안 솔루션을 제공합니다.
참고 URL : https://stackoverflow.com/questions/3768895/how-to-make-a-class-json-serializable
'developer tip' 카테고리의 다른 글
macOS Mojave 업데이트 후 Git이 작동하지 않음 (xcrun : 오류 : 잘못된 활성 개발자 경로 (/ Library / Developer / CommandLineTools)) (0) | 2020.09.30 |
---|---|
입력 유형 = "날짜"형식을 변경하는 방법이 있습니까? (0) | 2020.09.30 |
Pandas DataFrame에 한 행 추가 (0) | 2020.09.30 |
자바 스크립트에서 localStorage를 지우시겠습니까? (0) | 2020.09.30 |
Jackson을 사용하여 객체 배열을 역 직렬화하는 방법 (0) | 2020.09.30 |