python3 - 파이썬 api 사용법




JSON을 사용하여 세트를 직렬화하는 방법은 무엇입니까? (4)

컬렉션에 복제본이 포함되지 않도록하기 위해 __hash____eq__ 메서드가있는 객체가 포함 된 Python set 가 있습니다.

이 결과 set 를 json으로 인코딩해야하지만 빈 setjson.dumps 메소드로 json.dumps TypeError 합니다.

  File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 178, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable

사용자 정의 default 메서드가있는 json.JSONEncoder 클래스의 확장을 만들 수 있지만 set 통해 변환을 시작할 위치조차 확실하지 않습니다. 기본 메서드 내에서 set 값에서 사전을 만든 다음 그 값에 대한 인코딩을 반환해야합니까? 이상적으로, 나는 원래의 인코더가 질식하는 모든 데이터 타입을 처리 할 수있는 기본 메소드를 만들고 싶다. (데이터 소스로 Mongo를 사용하기 때문에 날짜도이 에러를 발생시키는 것처럼 보인다)

올바른 방향의 힌트를 주시면 감사하겠습니다.

편집하다:

답변 해주셔서 감사합니다! 아마 내가 더 정확 했어야 했어.

나는 번역 된 set 의 한계를 극복하기 위해 여기에 답을 활용했다. (그리고 upvoted) 그러나 문제가되는 내부 키가있다.

set 의 객체는 __dict__ 변환되는 복잡한 객체이지만 json 인코더의 기본 유형에는 적합하지 않은 속성의 값을 포함 할 수도 있습니다.

set 들어갈 여러 가지 유형이 있으며 해시는 기본적으로 엔티티의 고유 ID를 계산하지만 NoSQL의 진정한 정신에는 하위 개체의 내용을 정확히 알 수 없습니다.

하나의 객체는 starts 에 대한 날짜 값을 포함 할 수 있지만 다른 객체는 "비 프리미티브"객체를 포함하는 키를 포함하지 않는 다른 스키마를 가질 수 있습니다.

그래서 내가 생각할 수있는 유일한 해결책은 JSONEncoder 를 확장하여 default 메서드를 대체하여 여러 사례를 default 하는 것이 었습니다. 그러나이 작업을 수행하는 방법과 설명서가 모호한 것인지 잘 모르겠습니다. 중첩 된 객체에서 default 으로 반환되는 값은 키로 이동합니까, 아니면 전체 객체를 보는 일반 포함 / 삭제입니까? 이 메서드는 중첩 값을 어떻게 수용합니까? 이전 질문을 살펴본 결과 케이스 별 인코딩에 대한 최선의 방법을 찾을 수없는 것 같습니다. (불행히도 여기에서해야 할 일처럼 보입니다.)


사전, 목록 및 기본 객체 유형 (int, string, bool) 만 JSON에서 사용할 수 있습니다.


일반적인 파이썬 오브젝트가 아닌 세트를 인코딩 할 필요가 있고 그것을 쉽게 사람이 읽을 수 있도록 유지하고자한다면, Raymond Hettinger의 대답의 단순화 된 버전을 사용할 수 있습니다 :

import json
import collections

class JSONSetEncoder(json.JSONEncoder):
    """Use with json.dumps to allow Python sets to be encoded to JSON

    Example
    -------

    import json

    data = dict(aset=set([1,2,3]))

    encoded = json.dumps(data, cls=JSONSetEncoder)
    decoded = json.loads(encoded, object_hook=json_as_python_set)
    assert data == decoded     # Should assert successfully

    Any object that is matched by isinstance(obj, collections.Set) will
    be encoded, but the decoded value will always be a normal Python set.

    """

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        else:
            return json.JSONEncoder.default(self, obj)

def json_as_python_set(dct):
    """Decode json {'_set_object': [1,2,3]} to set([1,2,3])

    Example
    -------
    decoded = json.loads(encoded, object_hook=json_as_python_set)

    Also see :class:`JSONSetEncoder`

    """
    if '_set_object' in dct:
        return set(dct['_set_object'])
    return dct

set 만났을 때 list 을 반환하는 사용자 지정 인코더를 만들 수 있습니다. 다음은 그 예입니다.

>>> import json
>>> class SetEncoder(json.JSONEncoder):
...    def default(self, obj):
...       if isinstance(obj, set):
...          return list(obj)
...       return json.JSONEncoder.default(self, obj)
... 
>>> json.dumps(set([1,2,3,4,5]), cls=SetEncoder)
'[1, 2, 3, 4, 5]'

이런 식으로 다른 유형도 감지 할 수 있습니다. 목록이 실제로 세트임을 유지해야하는 경우 사용자 정의 인코딩을 사용할 수 있습니다. return {'type':'set', 'list':list(obj)} 것입니다.

삽화가 들어간 네스트 형에 대해서는, 이것을 직렬화하는 것을 고려해주세요.

>>> class Something(object):
...    pass
>>> json.dumps(set([1,2,3,4,5,Something()]), cls=SetEncoder)

이로 인해 다음과 같은 오류가 발생합니다.

TypeError: <__main__.Something object at 0x1691c50> is not JSON serializable

이것은, 엔코더가 돌려 주어진 list 결과를 취해, 그 아이에 대한 순차 주사를 재귀 적으로 호출하는 것을 나타냅니다. 여러 유형에 대해 사용자 정의 serializer를 추가하려면 다음을 수행하십시오.

>>> class SetEncoder(json.JSONEncoder):
...    def default(self, obj):
...       if isinstance(obj, set):
...          return list(obj)
...       if isinstance(obj, Something):
...          return 'CustomSomethingRepresentation'
...       return json.JSONEncoder.default(self, obj)
... 
>>> json.dumps(set([1,2,3,4,5,Something()]), cls=SetEncoder)
'[1, 2, 3, 4, 5, "CustomSomethingRepresentation"]'

Raymond Hettinger의 해결책 을 Python 3에 적용 시켰습니다 .

변경된 사항은 다음과 같습니다.

  • unicode 사라졌다.
  • super() 를 사용하여 부모의 default 으로 호출을 업데이트했습니다.
  • base64 를 사용하여 bytes 유형을 str 에 직렬화합니다 (파이썬 3의 bytes 가 JSON으로 변환 될 수 없기 때문에)
from decimal import Decimal
from base64 import b64encode, b64decode
from json import dumps, loads, JSONEncoder
import pickle

class PythonObjectEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (list, dict, str, int, float, bool, type(None))):
            return super().default(obj)
        return {'_python_object': b64encode(pickle.dumps(obj)).decode('utf-8')}

def as_python_object(dct):
    if '_python_object' in dct:
        return pickle.loads(b64decode(dct['_python_object'].encode('utf-8')))
    return dct

data = [1,2,3, set(['knights', 'who', 'say', 'ni']), {'key':'value'}, Decimal('3.14')]
j = dumps(data, cls=PythonObjectEncoder)
print(loads(j, object_hook=as_python_object))
# prints: [1, 2, 3, {'knights', 'who', 'say', 'ni'}, {'key': 'value'}, Decimal('3.14')]




set