클래스 JSON을 직렬화 가능하게 만드는 방법


833

파이썬 클래스를 직렬화 가능하게 만드는 방법?

간단한 수업 :

class FileItem:
    def __init__(self, fname):
        self.fname = fname

출력을 얻으려면 어떻게해야합니까?

>>> import json

>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable

오류없이


31
불행히도 모든 답변이 "클래스를 어떻게 직렬화합니까?"라는 질문에 대답하는 것 같습니다. "질문을 어떻게 직렬화 할 수 있습니까?" 이 답변은 직렬화하는 다른 모듈로 객체를 전달하는 대신 직렬화를 직접 수행한다고 가정합니다.
Kyle Delaney

Python3.5 이상을 사용하는 경우 json을 사용할 수 있습니다. 객체 (및 모든 속성을 재귀 적으로 )를 dict 로 변환합니다 . import jsons아래 답변을 참조하십시오-완벽하게 작동합니다
tswaehn

답변:


551

예상 출력에 대한 아이디어가 있습니까? 예를 들어 이렇게합니까?

>>> 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>
>>> 

44
__dict__모든 경우에 사용하는 것은 아닙니다. 객체가 인스턴스화 된 후 속성이 설정 __dict__되지 않은 경우 완전히 채워지지 않을 수 있습니다. 위의 예에서는 문제가 없지만 인코딩 할 클래스 속성이 __dict__있는 경우 클래스 __init__호출에서 또는 객체를 인스턴스화 한 후 다른 방법 으로 수정하지 않으면 속성이 나열되지 않습니다 .
Kris Hardy

8
+1이지만 from_json()객체 후크로 사용되는 함수에는 else: return json_object명령문이 있어야 하므로 일반 객체도 처리 할 수 ​​있습니다.
jogojapan

8
새 스타일 클래스 __dict__를 사용하면 @KrisHardy 도 작동하지 않습니다 __slots__.
badp

7
JSONEncoder위와 같이 사용자 정의 를 사용하여 __json_serializable__메소드 존재 여부 확인 및 객체의 JSON 직렬화 가능 표현을 얻기 위해 호출하는 등 사용자 정의 프로토콜을 작성할 수 있습니다 . 이것은 다른 파이썬 패턴 등으로 유지 될 것이다 __getitem__, __str__, __eq__,와 __len__.
jpmc26

5
__dict__예를 들어 객체의 속성이 다른 객체 인 경우에도 재귀 적으로 작동하지 않습니다.
Neel

634

간단한 기능에 대한 간단한 솔루션은 다음과 같습니다.

.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"
}

82
매우 제한적입니다. { "foo": "bar", "baz": "bat"} dict이 있으면 JSON으로 쉽게 직렬화됩니다. 대신 { "foo": "bar", "baz": MyObject ()}가 있으면 사용할 수 없습니다. 이상적인 상황은 중첩 된 객체가 명시 적으로가 아니라 재귀 적으로 JSON으로 직렬화되는 것입니다.
Mark E. Haase

30
여전히 작동합니다. 당신이 없습니다 o.__dict___. 자신의 예를보십시오 : class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
하기 Onur 디림에게

14
이 솔루션은 가역적입니까? 즉, json에서 객체를 쉽게 재구성 할 수 있습니까?
Jorge Leitao

2
@ JCLeitão 아니요. 같은 필드를 가진 두 개의 다른 클래스를 가질 수 있습니다. 해당 클래스의 객체 a와 b (아마도 동일한 속성을 가짐)는 동일한 a.__dict__/ b.__dict__입니다.
Martin Thoma 2016 년

7
datetime.datetime인스턴스 에서는 작동하지 않습니다 . 다음과 같은 오류가 발생합니다.'datetime.datetime' object has no attribute '__dict__'
Bruno Finger

171

더 복잡한 클래스의 경우 jsonpickle 도구를 고려할 수 있습니다 .

jsonpickle은 복잡한 Python 객체를 JSON과 직렬화 및 역 직렬화하는 Python 라이브러리입니다.

stdlib의 json, simplejson 및 demjson과 같이 JSON을 Python으로 인코딩하기위한 표준 Python 라이브러리는 JSON과 직접적으로 동일한 Python 기본 체 (예 : dicts, list, strings, ints 등) 만 처리 할 수 ​​있습니다. jsonpickle은 이러한 라이브러리 위에 빌드되며보다 복잡한 데이터 구조를 JSON으로 직렬화 할 수 있습니다. jsonpickle은 구성 및 확장 성이 뛰어나 사용자가 JSON 백엔드를 선택하고 추가 백엔드를 추가 할 수 있습니다.

(PyPi의 jsonpickle에 링크)


32
C #에서 왔을 때, 이것은 내가 기대했던 것입니다. 간단한 하나의 라이너로 클래스를 망칠 필요가 없습니다.
Jerther

2
jsonpickle은 굉장합니다. 이 클래스의 여러 수준과 거대한 복잡한, 지저분한 개체에 대해 완벽하게 작동
wisbucky

이것을 파일에 저장하는 올바른 방법의 예가 있습니까? 이 문서는 jsonpickle객체 를 인코딩하고 디코딩하는 방법 만 보여줍니다 . 또한 팬더 데이터 프레임을 포함하는 dicts의 dict을 해독 할 수 없었습니다.
user5359531

3
@ user5359531 obj = jsonpickle.decode(file.read())및 을 사용할 수 있습니다 file.write(jsonpickle.encode(obj)).
Kilian Batzner

1
django에 대한 질문 : 세션 데이터를 직렬화하기 위해 jsonpickle을 사용하면 pickle과 동일한 취약점이 있습니까? (여기에 설명 된 docs.djangoproject.com/en/1.11/topics/http/sessions/… )?
Paul Bormans

89

대부분의 대답은 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

이것은 클래스가 기본 데이터 표현 일 경우 작동합니다. 까다로운 작업으로 항상 키를 명시 적으로 설정할 수 있습니다.


2
이것은 정말 좋은 해결책이 될 수 있습니다. :) 내 경우에는 믿습니다. 이점 : 객체를 init 클래스로 만들어서 객체의 "모양"을 전달하면 본질적으로 직렬화 가능하며 repr로 해석 할 수있는 것처럼 보입니다 .
PascalVKooten

1
"dot-access"가 여전히 누락
되었지만

2
아아, 그것은 작동하는 것 같습니다! 감사합니다. 왜 이것이 허용되지 않는지 잘 모르겠습니다. 나는 변경하는 dumps것이 좋은 해결책이 아니라는 것에 전적으로 동의합니다 . 그건 그렇고, 대부분의 경우 dict위임과 함께 상속을 원할 것 입니다. 즉 dict, 클래스 안에 일부 유형 속성이 있으면이 속성을 매개 변수로 초기화와 같은 매개 변수로 전달합니다 super().__init__(self.elements).
cglacet

47

Onur의 답변이 마음에 들지만 toJSON()객체를 직렬화 하는 선택적 방법 을 포함하도록 확장 합니다.

def dumper(obj):
    try:
        return obj.toJSON()
    except:
        return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)

나는 이것이 기존의 사용 json.dumps과 사용자 지정 처리 도입 사이의 최상의 균형이라는 것을 알았습니다 . 감사!
Daniel Buckmaster

12
나는 실제로 이것을 정말로 좋아합니다. 그러나 자동 실패 (예 : 존재하지 않는 다른 이유로 toJSON ()에서 실패하는 경우)를 피하기 위해 try-catch아마도 if 'toJSON' in obj.__attrs__():... 데이터 손상을 일으킬 수있는 실패를 피하기 위해.
thclark

39

또 다른 옵션은 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"}'

2
안녕하세요,이 "맞춤 인코더"접근법을 정말로 좋아하지 않습니다. 클래스 json을 seriazable로 만들 수 있다면 더 좋을 것입니다. 나는 노력하고 시도하고 아무것도하지 않습니다. 이 작업을 수행하는 방법에 대한 아이디어가 있습니까? 문제는 json 모듈이 내장 된 파이썬 유형에 대해 클래스를 테스트하고 사용자 정의 클래스의 경우 인코더를 작성한다고 말합니다. :) 가짜 일 수 있습니까? 그래서 클래스에 무언가를 할 수있어 json 모듈에 대한 간단한 목록처럼 행동합니까? 나는 subclasscheckinstancecheck를 시도 하지만 아무것도하지 않습니다.
보얀라도 예비치

@ADRENALIN 모든 클래스 속성 값이 직렬화 가능하고 해킹을 신경 쓰지 않으면 기본 유형 (아마도 dict)에서 상속받을 수 있습니다. jsonpickle 또는 json_tricks 또는 표준 엔코더 대신 무언가를 사용할 수도 있습니다 (여전히 사용자 정의 인코더이지만 쓰거나 호출 해야하는 인코더는 아닙니다). 전자는 인스턴스를 피클 링하고 후자는 속성의 dict으로 저장합니다.이 속성은 __json__encode__/ __json_decode__(공개 : 마지막으로 만들었습니다)를 통해 변경할 수 있습니다 .
Mark

30

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

JSONEncoder.default ()가 자동으로 특수한 "to_json ()"메소드를 확인하고 발견 된 경우이를 사용하여 JSON 모듈을 가져 오면 원숭이 패치를 원숭이 패치합니다.

Onur가 말했듯이 이번에는 json.dumps()프로젝트의 모든 것을 업데이트 할 필요는 없습니다 .


6
큰 감사를 드린다! 이것은 내가 원하는 것을 할 수있는 유일한 대답입니다 : 기존 코드를 변경하지 않고 객체를 직렬화 할 수 있습니다. 다른 방법은 대부분 저에게 효과적이지 않습니다. 개체가 타사 라이브러리에 정의되어 있으며 직렬화 코드도 타사입니다. 그것들을 바꾸는 것은 어색 할 것입니다. 당신의 방법으로, 나는 단지 할 필요가 있습니다 TheObject.to_json = my_serializer.
Yongwei Wu

24

다른 날이 문제를 겪고 중첩 된 객체상속 된 필드를 처리 할 수있는 더 일반적인 버전의 Python 객체 용 Encoder를 구현했습니다 .

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"
}

1
이것은 조금 낡았지만 순환 수입 오류가 발생했습니다. return obj마지막 줄 대신이 작업 을 수행했습니다 return super(ObjectEncoder, self).default(obj). 여기를
SomeTypeFoo

24

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

3
Python 3.7 이상을 사용할 수 있다면 파이썬 클래스를 dicts로 변환하고 JSON 문자열 (및 그 반대로)을 변환하는 가장 깨끗한 솔루션은 jsons라이브러리를 dataclasses 와 혼합하는 입니다. 지금까지는 나에게 좋습니다!
Ruluk

3
이것은 표준 Python 설치에 내장되어 있지 않은 외부 라이브러리입니다.
누 메논

슬롯 속성 을 가진 클래스에만 해당
yehudahs

할 수는 있지만 slot 을 사용할 필요는 없습니다 . 특정 클래스의 서명에 따라 덤핑 할 때만 슬롯 이 필요 합니다 . 다음 버전 1.1.0에서는 더 이상 그렇지 않습니다.
RH

11
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')))

standard 사용하는 경우 jsonu는 default함수 를 정의해야 합니다.

import json
def default(o):
    return o._asdict()

print(json.dumps(User('alice', 'alice@mail.com'), default=default))

2
I는 람다로 _asdict 기능을 제거하여이를 simplifed json.dumps(User('alice', 'alice@mail.com'), default=lambda x: x.__dict__)
JustEngland

8

json인쇄 할 수있는 개체 측면에서 제한적이며 jsonpickle(필요한 경우 pip install jsonpickle) 텍스트를 들여 쓸 수없는 측면에서 제한적입니다. 클래스를 변경할 수없는 객체의 내용을 검사하려는 경우 여전히 다음보다 직접적인 방법을 찾을 수 없습니다.

 import json
 import jsonpickle
 ...
 print  json.dumps(json.loads(jsonpickle.encode(object)), indent=2)

참고 : 여전히 객체 메소드를 인쇄 할 수 없습니다.


6

이 클래스는 트릭을 수행 할 수 있으며 객체를 표준 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.


이 방법이 가장 마음에 들었습니다. 회원 / 방법을 직렬화 할 수없는 더 복잡한 객체를 직렬화하려고 할 때 문제가 발생했습니다. 더 많은 객체에서 작동하는 구현은 다음과 같습니다.```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 TypeError를 제외하고 : o .__ dict __ [k] = str (v) return o return json.dumps (check (obj) .__ dict__, indent = 2)```
Will 찰턴

4
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)

에서 문서 : 매개 변수는 default(obj)OBJ 또는 인상 형식 오류의 직렬화 버전을 반환해야하는 기능입니다. 기본값은 default단순히 TypeError를 발생시킵니다.
luckydonald

4

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) :

역 직렬화 (JSON-> Python) :


4

이것은 나를 위해 잘 작동했습니다 :

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))

3

패키지 설치가 마음에 들지 않으면 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을 만들었습니다 .


1
방금 json_tricks를 테스트했으며 2019 년에 아름답게 작동했습니다.
pauljohn32

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"}'

중첩 된 객체에 적합합니까? 디코딩 및 인코딩 포함
Simone Zandara

1

여기 내 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)

1

Peewee의 모델을 PostgreSQL에 저장하려고 할 때이 문제가 발생했습니다 JSONField.

잠시 동안 고생 한 후 일반적인 해결책이 있습니다.

내 솔루션의 핵심은 Python의 소스 코드를 살펴보고 코드 문서 ( here )가 기존 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 serializablePython에서 수신하는 모든 단일 불만에 대해 직렬화 할 수없는 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에만 해당되지만, 나는 생각합니다.

  1. 일반적으로 다른 ORM (Django 등)에 적용 가능
  2. 또한 json.dumps작동 방식 을 이해하면 이 솔루션은 일반적으로 Python (sans ORM)에서도 작동합니다.

질문은 의견 섹션에 게시하십시오. 감사!


1

이 함수는 재귀를 사용하여 사전의 모든 부분을 반복 한 다음 기본 제공 유형이 아닌 클래스 의 repr () 메서드 를 호출합니다 .

def sterilize(obj):
    object_type = type(obj)
    if isinstance(obj, dict):
        return {k: sterilize(v) for k, v in obj.items()}
    elif object_type in (list, tuple):
        return [sterilize(v) for v in obj]
    elif object_type in (str, int, bool):
        return obj
    else:
        return obj.__repr__()


0

나는 내 자신의 해결책을 생각해 냈습니다. 이 메소드를 사용 하여 직렬화 할 문서 ( 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

0

날짜 시간 객체 직렬화 문제를 해결하기 위해 데코레이터를 사용하기로 결정했습니다. 내 코드는 다음과 같습니다.

#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을 일반적인 방법으로 (기본 키워드를 지정하지 않고) 사용하여 날짜 시간 객체가 포함 된 데이터를 직렬화합니다. 날짜 시간 시리얼 라이저 코드는 자동으로 json.dumps 및 json.dump를 호출합니다.


0

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)

0

패키지를 설치할 수 있다면 dill을 시도하는 것이 좋습니다 . 이는 내 프로젝트에서 잘 작동했습니다. 이 패키지의 좋은 점은와 동일한 인터페이스 pickle를 가지고 있기 때문에 pickle프로젝트에서 이미 사용 dill하고 있다면 코드를 변경하지 않고 간단히 스크립트 를 대체하고 실행할 수 있습니다. 따라서 시도하는 것이 매우 저렴한 솔루션입니다!

(전체 공개 : 저는 절대로 딜 프로젝트와 관련이 없으며 결코 기여하지 않았습니다.)

패키지를 설치하십시오 :

pip install dill

그런 다음 가져 오기 dill대신 코드를 편집하십시오 pickle.

# import pickle
import dill as pickle

스크립트를 실행하고 작동하는지 확인하십시오. (그렇다면 더 이상 pickle모듈 이름을 가리지 않도록 코드를 정리할 수 있습니다 !)

프로젝트 페이지dill 에서 직렬화 할 수 있거나 직렬화 할 수없는 데이터 유형에 대한 일부 세부 사항 :

dill 다음 표준 유형을 피클 할 수 있습니다.

없음, 유형, bool, int, long, float, complex, str, 유니 코드, 튜플, 목록, dict, 파일, 버퍼, 내장, 이전 및 새 스타일 클래스, 이전 및 새 스타일 클래스의 인스턴스, 세트, ​​고정 세트, 배열 , 함수, 예외

dill 더 많은 '이국적인'표준 유형을 피클 할 수도 있습니다.

수율, 중첩 함수, 람다, 셀, 메소드, 언 바운드 메소드, 모듈, 코드, 메소드 랩퍼, dictproxy, 메소드 디스크립터, getsetdescriptor, 멤버 디스크립터, 랩퍼 디스크립터, xrange, 슬라이스, 구현되지 않은, 줄임표, 종료

dill 아직 이러한 표준 유형을 피클 할 수 없습니다.

프레임, 생성기, 역 추적



0

다른 옵션을 추가하려면 다음을 수행하십시오 . attrs패키지 및 asdict메소드를 사용할 수 있습니다 .

class ObjectEncoder(JSONEncoder):
    def default(self, o):
        return attr.asdict(o)

json.dumps(objects, cls=ObjectEncoder)

다시 변환

def from_json(o):
    if '_obj_name' in o:
        type_ = o['_obj_name']
        del o['_obj_name']
        return globals()[type_](**o)
    else:
        return o

data = JSONDecoder(object_hook=from_json).decode(data)

수업은 다음과 같습니다

@attr.s
class Foo(object):
    x = attr.ib()
    _obj_name = attr.ib(init=False, default='Foo')

0

Onur의 답변 외에도 다음과 같은 날짜 시간 유형을 처리하려고합니다.
( 'datetime.datetime'객체에는 ' dict '예외 속성이 없습니다 .)

def datetime_option(value):
    if isinstance(value, datetime.date):
        return value.timestamp()
    else:
        return value.__dict__

용법:

def toJSON(self):
    return json.dumps(self, default=datetime_option, sort_keys=True, indent=4)

0

먼저 객체를 JSON과 호환되도록해야 표준 JSON 모듈을 사용하여 덤프 할 수 있습니다. 나는 이렇게했다 :

def serialize(o):
    if isinstance(o, dict):
        return {k:serialize(v) for k,v in o.items()}
    if isinstance(o, list):
        return [serialize(e) for e in o]
    if isinstance(o, bytes):
        return o.decode("utf-8")
    return o

0

Quinten Cabo답변을 바탕으로 :

def sterilize(obj):
    if type(obj) in (str, float, int, bool, type(None)):
        return obj
    elif isinstance(obj, dict):
        return {k: sterilize(v) for k, v in obj.items()}
    elif hasattr(obj, '__iter__') and callable(obj.__iter__):
        return [sterilize(v) for v in obj]
    elif hasattr(obj, '__dict__'):
        return {k: sterilize(v) for k, v in obj.__dict__.items() if k not in ['__module__', '__dict__', '__weakref__', '__doc__']}
    else:
        return repr(obj)

차이점은

  1. 그냥 list및 대신 iterable에 대해 tuple작동합니다 (NumPy 배열 등에서 작동합니다)
  2. 동적 유형 (을 포함하는 유형)에서 작동합니다 __dict__.
  3. 기본 유형을 포함 float하고 None그래서 그들은 문자열로 변환되지 않습니다.

독자에게 연습으로 남겨 __slots__두는 것은 반복 가능하고 멤버가있는 클래스, 사전이며 멤버가있는 클래스 등을 처리하는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.