Cách tạo một lớp tuần tự hóa JSON


834

Làm thế nào để tạo một lớp Python tuần tự hóa?

Một lớp học đơn giản:

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

Tôi nên làm gì để có thể nhận được đầu ra của:

>>> import json

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

Không có lỗi


31
Thật không may là tất cả các câu trả lời dường như trả lời câu hỏi "Làm thế nào để tôi tuần tự hóa một lớp học?" thay vì câu hỏi hành động "Làm thế nào để tôi tạo một lớp tuần tự hóa?" Các câu trả lời này giả định rằng bạn đang tự thực hiện tuần tự hóa, thay vì chuyển đối tượng sang một số mô-đun khác nối tiếp nó.
Kyle Delaney

Nếu bạn đang sử dụng Python3.5 +, bạn có thể sử dụng jsons. Nó sẽ chuyển đổi đối tượng của bạn (và tất cả các thuộc tính của nó theo cách đệ quy ) thành một lệnh. import jsonsxem câu trả lời dưới đây - nó hoạt động hoàn toàn tốt
tswaehn

Câu trả lời:


551

Bạn có một ý tưởng về đầu ra dự kiến? Ví dụ như điều này sẽ làm gì?

>>> f  = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'

Trong trường hợp đó bạn chỉ có thể gọi json.dumps(f.__dict__).

Nếu bạn muốn đầu ra tùy chỉnh nhiều hơn thì bạn sẽ phải phân lớp JSONEncodervà thực hiện tuần tự hóa tùy chỉnh của riêng bạn.

Đối với một ví dụ tầm thường, xem bên dưới.

>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
        def default(self, o):
            return o.__dict__    

>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'

Sau đó, bạn chuyển lớp này vào json.dumps()phương thức là clskwarg:

json.dumps(cls=MyEncoder)

Nếu bạn cũng muốn giải mã thì bạn sẽ phải cung cấp một tùy chỉnh object_hookcho JSONDecoderlớp. Ví dụ

>>> 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
Sử dụng __dict__sẽ không hoạt động trong mọi trường hợp. Nếu các thuộc tính chưa được đặt sau khi đối tượng được khởi tạo, __dict__có thể không được điền đầy đủ. Trong ví dụ trên, bạn vẫn ổn, nhưng nếu bạn có các thuộc tính lớp mà bạn cũng muốn mã hóa, chúng sẽ không được liệt kê __dict__trừ khi chúng được sửa đổi trong __init__lệnh gọi của lớp hoặc bằng một cách khác sau khi đối tượng được khởi tạo.
Kris Hardy

8
+1, nhưng from_json()hàm được sử dụng như hook đối tượng nên có một else: return json_objectcâu lệnh, vì vậy nó cũng có thể xử lý các đối tượng chung.
jogojapan

8
@KrisHardy __dict__cũng không hoạt động nếu bạn sử dụng __slots__trên một lớp phong cách mới.
badp 13/12/13

7
Bạn có thể sử dụng một tùy chỉnh JSONEncodernhư trên để tạo một giao thức tùy chỉnh, chẳng hạn như kiểm tra sự tồn tại của __json_serializable__phương thức và gọi nó để có được một biểu diễn tuần tự hóa JSON của đối tượng. Đây sẽ là phù hợp với mô hình Python khác, như __getitem__, __str__, __eq__, và __len__.
jpmc26

5
__dict__cũng sẽ không hoạt động đệ quy, ví dụ, nếu một thuộc tính của đối tượng của bạn là một đối tượng khác.
Neel

634

Đây là một giải pháp đơn giản cho một tính năng đơn giản:

.toJSON() phương pháp

Thay vì một lớp tuần tự hóa JSON, hãy thực hiện một phương thức tuần tự hóa:

import json

class Object:
    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__, 
            sort_keys=True, indent=4)

Vì vậy, bạn chỉ cần gọi nó để tuần tự hóa:

me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"

print(me.toJSON())

sẽ xuất ra:

{
    "age": 35,
    "dog": {
        "name": "Apollo"
    },
    "name": "Onur"
}

82
Rất hạn chế. Nếu bạn có một dict {"foo": "bar", "baz": "bat"}, điều đó sẽ tuần tự hóa thành JSON dễ dàng. Nếu thay vào đó bạn có {"foo": "bar", "baz": MyObject ()}, thì bạn không thể. Tình huống lý tưởng sẽ là các đối tượng lồng nhau được tuần tự hóa thành JSON theo cách đệ quy, không rõ ràng.
Đánh dấu E. Haase

30
Nó vẫn sẽ hoạt động. Bạn đang mất tích o.__dict___. Hãy thử ví dụ của riêng bạn: class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
Onur Yıldırım

14
Là giải pháp này có thể đảo ngược? Tức là có dễ dàng để xây dựng lại đối tượng từ json?
Jorge Leitao

2
@ JCLeitão Không. Bạn có thể có hai lớp khác nhau với cùng một trường. Các đối tượng a và b của lớp đó (có thể có cùng thuộc tính) sẽ có cùng a.__dict__/ b.__dict__.
Martin Thoma

7
Điều này không hoạt động với các datetime.datetimetrường hợp. Nó đưa ra lỗi sau:'datetime.datetime' object has no attribute '__dict__'
Bruno Finger

171

Đối với các lớp phức tạp hơn, bạn có thể xem xét công cụ jsonpickle :

jsonpickle là một thư viện Python để tuần tự hóa và giải tuần tự hóa các đối tượng Python phức tạp đến và từ JSON.

Các thư viện Python tiêu chuẩn để mã hóa Python thành JSON, chẳng hạn như stdlib's json, simplejson và demjson, chỉ có thể xử lý các nguyên hàm Python có tương đương JSON trực tiếp (ví dụ: dicts, list, string, ints, v.v.). jsonpickle xây dựng trên đầu các thư viện này và cho phép các cấu trúc dữ liệu phức tạp hơn được tuần tự hóa thành JSON. jsonpickle có thể cấu hình cao và có thể mở rộng cho phép người dùng chọn phụ trợ JSON và thêm các phụ trợ bổ sung.

(liên kết đến jsonpickle trên PyPi)


32
Đến từ C #, đây là những gì tôi đã mong đợi. Một lớp lót đơn giản và không gây rối với các lớp.
Jerther

2
jsonpickle là tuyệt vời. Nó hoạt động hoàn hảo cho một đối tượng khổng lồ, phức tạp, lộn xộn với nhiều cấp độ
wvducky

Có một ví dụ về cách thích hợp để lưu cái này vào một tập tin không? Tài liệu chỉ cho thấy cách mã hóa và giải mã một jsonpickleđối tượng. Ngoài ra, điều này đã không thể giải mã một câu lệnh của các dicts chứa các tệp dữ liệu gấu trúc.
dùng5359531

3
@ user5359531 bạn có thể sử dụng obj = jsonpickle.decode(file.read())file.write(jsonpickle.encode(obj)).
Kilian Batzner

1
Một câu hỏi dành riêng cho django: việc sử dụng jsonpickle để tuần tự hóa dữ liệu phiên có lỗ hổng tương tự như dưa chua không? (như được mô tả ở đây docs.djangoproject.com/en/1.11/topics/http/simes/iêu )?
Paul Bormans

89

Hầu hết các câu trả lời liên quan đến việc thay đổi cuộc gọi thành json.dumps () , điều này không phải lúc nào cũng có thể hoặc không mong muốn (ví dụ, nó có thể xảy ra trong một thành phần khung).

Nếu bạn muốn có thể gọi json.dumps (obj) như vậy, thì một giải pháp đơn giản được kế thừa từ 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

Điều này hoạt động nếu lớp của bạn chỉ là biểu diễn dữ liệu cơ bản, đối với những thứ khó hơn, bạn luôn có thể đặt khóa rõ ràng.


2
Đây thực sự có thể là một giải pháp tốt đẹp :) Tôi tin cho trường hợp của mình. Lợi ích: bạn truyền đạt "hình dạng" của đối tượng bằng cách biến nó thành một lớp với init, nó vốn có tính tuần tự hóa và nó có thể hiểu là repr .
PascalVKooten

1
Mặc dù "truy cập dấu chấm" vẫn còn thiếu :(
PascalVKooten

2
Ahh có vẻ như làm việc! Cảm ơn, không chắc tại sao đây không phải là câu trả lời được chấp nhận. Tôi hoàn toàn đồng ý rằng thay đổi dumpskhông phải là một giải pháp tốt. Nhân tiện, trong hầu hết các trường hợp, bạn có thể muốn có dictsự kế thừa cùng với ủy quyền, điều đó có nghĩa là bạn sẽ có một số dictthuộc tính loại trong lớp của mình, sau đó bạn sẽ chuyển thuộc tính này làm tham số như là khởi tạo một cái gì đó như thế nào super().__init__(self.elements).
cglacet

47

Tôi thích câu trả lời của Onur nhưng sẽ mở rộng để bao gồm một toJSON()phương thức tùy chọn để các đối tượng tự tuần tự hóa:

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

Tôi thấy đây là sự cân bằng tốt nhất giữa việc sử dụng json.dumpsxử lý tùy chỉnh hiện có và giới thiệu. Cảm ơn!
Daniel Buckmaster

12
Tôi thực sự thực sự thích điều này; nhưng thay vì try-catchcó thể làm một cái gì đó nhưif 'toJSON' in obj.__attrs__(): ... để tránh một sự thất bại thầm lặng (trong trường hợp thất bại ở toJSON () vì một số lý do khác ngoài việc không có ở đó) ... một thất bại có thể dẫn đến tham nhũng dữ liệu.
thclark

39

Một tùy chọn khác là bọc kết xuất JSON trong lớp của chính nó:

import json

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

    def __repr__(self):
        return json.dumps(self.__dict__)

Hoặc, thậm chí tốt hơn, phân lớp lớp FileItem từ một JsonSerializablelớp:

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

Kiểm tra:

>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'

2
Xin chào, tôi không thực sự thích cách tiếp cận "bộ mã hóa tùy chỉnh" này, sẽ tốt hơn nếu bạn có thể làm cho lớp của bạn trở nên ổn định. Tôi cố gắng, và cố gắng và cố gắng và không có gì. Có bất kỳ ý tưởng làm thế nào để làm điều này. Điều này là mô-đun json kiểm tra lớp của bạn chống lại các loại python tích hợp, và thậm chí nói rằng các lớp tùy chỉnh làm cho bộ mã hóa của bạn :). Nó có thể bị làm giả không? Vì vậy, tôi có thể làm một cái gì đó cho lớp học của tôi để nó hoạt động như danh sách đơn giản cho mô-đun json? Tôi thử subgroupcheckinstancecheck nhưng không có gì.
Bojan Radojevic

@ADRENALIN Bạn có thể kế thừa từ một loại chính (có thể là chính tả), nếu tất cả các giá trị thuộc tính lớp được tuần tự hóa và bạn không bận tâm đến việc hack. Bạn cũng có thể sử dụng jsonpickle hoặc json_tricks hoặc một cái gì đó thay vì tiêu chuẩn (vẫn là một bộ mã hóa tùy chỉnh, nhưng không phải là một bộ mã bạn cần viết hoặc gọi). Ví dụ trước, ví dụ này lưu trữ nó dưới dạng chính tả của các thuộc tính, mà bạn có thể thay đổi bằng cách thực hiện __json__encode__/ __json_decode__(tiết lộ: Tôi đã thực hiện cái cuối cùng).
Đánh dấu

30

Chỉ cần thêm to_jsonphương thức vào lớp của bạn như thế này:

def to_json(self):
  return self.message # or how you want it to be serialized

Và thêm mã này (từ câu trả lời này ) , vào một nơi nào đó ở đầu mọi thứ:

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder().default
JSONEncoder.default = _default

Điều này sẽ mô-đun json-patch json khi nó được nhập, do đó, JSONEncoder.default () sẽ tự động kiểm tra một phương thức "to_json ()" đặc biệt và sử dụng nó để mã hóa đối tượng nếu tìm thấy.

Giống như Onur đã nói, nhưng lần này bạn không phải cập nhật mọi thứ json.dumps()trong dự án của mình.


6
Cảm ơn nhiều! Đây là câu trả lời duy nhất cho phép tôi làm những gì tôi muốn: có thể tuần tự hóa một đối tượng mà không cần thay đổi mã hiện có. Các phương pháp khác chủ yếu không làm việc cho tôi. Đối tượng được xác định trong thư viện của bên thứ ba và mã tuần tự hóa cũng là bên thứ ba. Thay đổi chúng sẽ rất khó xử. Với phương pháp của bạn, tôi chỉ cần làm TheObject.to_json = my_serializer.
Yongwei Wu

24

Tôi đã gặp vấn đề này vào một ngày khác và đã triển khai một phiên bản mã hóa tổng quát hơn cho các đối tượng Python có thể xử lý các đối tượng lồng nhaucác trường được kế thừa :

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

Thí dụ:

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)

Kết quả:

{
  "a": "A", 
  "b": [
    {
      "ab": {
        "b": "B", 
        "i": "I", 
        "y": "y"
      }
    }
  ], 
  "c": {
    "c": "YES"
  }, 
  "i": "I"
}

1
Mặc dù điều này hơi cũ..Tôi đang đối mặt với một số lỗi nhập vòng tròn. Vì vậy, thay vì return objtrong dòng cuối cùng tôi đã làm điều này return super(ObjectEncoder, self).default(obj). Tham khảo TẠI ĐÂY
sốTypeFoo

24

Nếu bạn đang sử dụng Python3.5 +, bạn có thể sử dụng jsons. Nó sẽ chuyển đổi đối tượng của bạn (và tất cả các thuộc tính của nó theo cách đệ quy) thành một lệnh.

import jsons

a_dict = jsons.dump(your_object)

Hoặc nếu bạn muốn một chuỗi:

a_str = jsons.dumps(your_object)

Hoặc nếu lớp của bạn thực hiện jsons.JsonSerializable:

a_dict = your_object.json

3
Nếu bạn có thể sử dụng Python 3.7+, tôi thấy rằng giải pháp sạch nhất để chuyển đổi các lớp python thành các chuỗi và chuỗi JSON (và viceversa) là trộn jsonsthư viện với các bảng dữ liệu . Cho đến nay, rất tốt cho tôi!
Ruluk

3
Đây là một thư viện bên ngoài, không được tích hợp trong bản cài đặt Python tiêu chuẩn.
Noumenon

chỉ dành cho lớp có thuộc tính vị trí
yehudahs

Bạn có thể, nhưng bạn không cần sử dụng các vị trí . Chỉ khi bán phá giá theo chữ ký của một lớp cụ thể, bạn sẽ cần các vị trí . Trong phiên bản 1.1.0 sắp tới, điều đó cũng không còn nữa.
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')))

Nếu sử dụng tiêu chuẩn json, bạn cần xác định một defaultchức năng

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

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

2
Tôi đã đơn giản hóa điều này bằng cách loại bỏ hàm _asdict bằng lambda json.dumps(User('alice', 'alice@mail.com'), default=lambda x: x.__dict__)
JustEngland

8

jsonbị giới hạn về các đối tượng mà nó có thể in và jsonpickle(bạn có thể cần a pip install jsonpickle) bị giới hạn về các điều khoản mà nó không thể thụt lề văn bản. Nếu bạn muốn kiểm tra nội dung của một đối tượng mà lớp bạn không thể thay đổi, tôi vẫn không thể tìm thấy cách nào chặt chẽ hơn:

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

Lưu ý: vẫn không thể in các phương thức đối tượng.


6

Lớp này có thể thực hiện các mẹo, nó chuyển đổi đối tượng thành json tiêu chuẩn.

import json


class Serializer(object):
    @staticmethod
    def serialize(object):
        return json.dumps(object, default=lambda o: o.__dict__.values()[0])

sử dụng:

Serializer.serialize(my_object)

làm việc trong python2.7python3.


Tôi thích phương pháp này nhất. Tôi gặp vấn đề khi cố gắng tuần tự hóa các đối tượng phức tạp hơn mà các thành viên / phương thức không được tuần tự hóa. Đây là cách triển khai của tôi hoạt động trên nhiều đối tượng hơn: `` `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 ngoại trừ TypeError: o .__ dict __ [k] = str (v) return o return json.dumps (check (obj) .__ dict__, indent = 2) ``
Will Charlton

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)

Từ doc : Tham số default(obj)là một hàm sẽ trả về một phiên bản nối tiếp của obj hoặc nâng TypeError. Mặc định defaultđơn giản là tăng TypeError.
luckydonald

4

jaraco đã đưa ra một câu trả lời khá gọn gàng. Tôi cần sửa một số thứ nhỏ, nhưng cách này hiệu quả:

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

Lưu ý rằng chúng ta cần hai bước để tải. Để bây giờ, __python__tài sản không được sử dụng.

Làm thế nào phổ biến là điều này?

Sử dụng phương pháp của AlJohri , tôi kiểm tra mức độ phổ biến của các phương pháp:

Tuần tự hóa (Python -> JSON):

Giải trừ (JSON -> Python):


4

Điều này đã làm việc tốt cho tôi:

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__

và sau đó

class FileItem(JsonSerializable):
    ...

log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2))

3

Nếu bạn không nhớ cài đặt gói cho nó, bạn có thể sử dụng json-trick :

pip install json-tricks

Sau đó, bạn chỉ cần nhập dump(s)từ json_tricksthay vì json và nó thường sẽ hoạt động:

from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)

cái nào sẽ cho

{
        "__instance_type__": [
                "module_name.test_class",
                "MyTestCls"
        ],
        "attributes": {
                "attr": "val",
                "dct_attr": {
                        "hello": 42
                }
        }
}

Và cơ bản là vậy!


Điều này sẽ làm việc tuyệt vời nói chung. Có một số trường hợp ngoại lệ, ví dụ: nếu những điều đặc biệt xảy ra __new__, hoặc nhiều phép thuật siêu hình đang diễn ra.

Rõ ràng tải cũng hoạt động (nếu không thì điểm gì):

from json_tricks import loads
json_str = loads(json_str)

Điều này không cho rằng module_name.test_class.MyTestClscó thể được nhập và không thay đổi theo những cách không tương thích. Bạn sẽ lấy lại một ví dụ , không phải một số từ điển hoặc một cái gì đó, và nó phải là một bản sao giống hệt với bản sao bạn đã bỏ.

Nếu bạn muốn tùy chỉnh cách một cái gì đó được (de) nối tiếp, bạn có thể thêm các phương thức đặc biệt vào lớp của mình, như vậy:

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

mà chỉ tuần tự hóa một phần của các tham số thuộc tính, làm ví dụ.

Và như một phần thưởng miễn phí, bạn nhận được (de) tuần tự hóa các mảng numpy, ngày & thời gian, bản đồ được đặt hàng, cũng như khả năng bao gồm các nhận xét trong json.

Tuyên bố miễn trừ trách nhiệm: Tôi đã tạo json_tricks , vì tôi gặp vấn đề tương tự như bạn.


1
Tôi vừa thử nghiệm json_tricks và nó đã hoạt động tốt (vào năm 2019).
pauljohn32

2

jsonweb dường như là giải pháp tốt nhất cho tôi. Xem 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"}'

Nó hoạt động tốt cho các đối tượng lồng nhau? Bao gồm giải mã và mã hóa
Simone Zandara

1

Đây là 3 xu của tôi ...
Điều này thể hiện tuần tự json rõ ràng cho một đối tượng trăn giống như cây.
Lưu ý: Nếu bạn thực sự muốn một số mã như thế này, bạn có thể sử dụng lớp FilePath xoắn .

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

Tôi gặp phải vấn đề này khi tôi cố lưu trữ mô hình của Peewee vào PostgreSQL JSONField.

Sau khi vật lộn một lúc, đây là giải pháp chung.

Chìa khóa cho giải pháp của tôi là thông qua mã nguồn của Python và nhận ra rằng tài liệu mã (được mô tả ở đây ) đã giải thích cách mở rộng json.dumpsdữ liệu hiện có để hỗ trợ các loại dữ liệu khác.

Giả sử bạn hiện tại có một mô hình chứa một số trường không thể tuần tự hóa thành JSON và mô hình chứa trường JSON ban đầu trông như thế này:

class SomeClass(Model):
    json_field = JSONField()

Chỉ cần xác định một tùy chỉnh JSONEncodernhư thế này:

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)

Và sau đó chỉ cần sử dụng nó JSONFieldnhư dưới đây của bạn :

class SomeClass(Model):
    json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)

Chìa khóa là default(self, obj)phương pháp trên. Đối với mỗi đơn ... is not JSON serializablekhiếu nại bạn nhận được từ Python, chỉ cần thêm mã để xử lý loại không thể định dạng thành JSON (chẳng hạn như Enumhoặc datetime)

Ví dụ: đây là cách tôi hỗ trợ một lớp kế thừa từ 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)

Cuối cùng, với mã được triển khai như trên, bạn có thể chuyển đổi bất kỳ mô hình Peewee nào thành đối tượng có thể hiểu được JSON như bên dưới:

peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)

Mặc dù đoạn mã trên là (phần nào) dành riêng cho Peewee, nhưng tôi nghĩ:

  1. Nó áp dụng cho các ORM khác (Django, v.v.) nói chung
  2. Ngoài ra, nếu bạn hiểu cách thức json.dumpshoạt động, giải pháp này cũng hoạt động với Python (sans ORM) nói chung

Bất kỳ câu hỏi, xin vui lòng gửi trong phần ý kiến. Cảm ơn!


1

Hàm này sử dụng đệ quy để lặp qua mọi phần của từ điển và sau đó gọi các phương thức repr () của các lớp không phải là kiểu dựng sẵn.

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

Tôi đã đưa ra giải pháp của riêng tôi. Sử dụng phương pháp này, vượt qua bất kỳ tài liệu nào ( dict , list , ObjectId , v.v.) để tuần tự hóa.

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

Tôi đã chọn sử dụng trang trí để giải quyết vấn đề tuần tự hóa đối tượng datetime. Đây là mã của tôi:

#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

Bằng cách nhập mô-đun trên, các mô-đun khác của tôi sử dụng json theo cách thông thường (không chỉ định từ khóa mặc định) để tuần tự hóa dữ liệu có chứa các đối tượng thời gian ngày. Mã serializer datetime được tự động gọi cho json.dumps và json.dump.


0

Tôi thích phương pháp của Lost Koder nhất. Tôi gặp vấn đề khi cố gắng tuần tự hóa các đối tượng phức tạp hơn mà các thành viên / phương thức không được tuần tự hóa. Đây là triển khai của tôi hoạt động trên nhiều đối tượng hơn:

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

Nếu bạn có thể cài đặt một gói, tôi khuyên bạn nên thử thì là , nó chỉ hoạt động tốt cho dự án của tôi. Một điều thú vị về gói này là nó có giao diện giống như picklevậy, vì vậy nếu bạn đã sử dụng pickletrong dự án của mình, bạn có thể chỉ cần thay thế trongdill và xem nếu tập lệnh chạy, mà không thay đổi bất kỳ mã nào. Vì vậy, nó là một giải pháp rất rẻ để thử!

(Chống tiết lộ đầy đủ: Tôi không có cách nào liên kết và chưa bao giờ đóng góp cho dự án thì là.)

Cài đặt gói:

pip install dill

Sau đó chỉnh sửa mã của bạn để nhập dillthay vì pickle:

# import pickle
import dill as pickle

Chạy kịch bản của bạn và xem nếu nó hoạt động. (Nếu có, bạn có thể muốn dọn sạch mã của mình để không còn bị che khuất pickletên mô-đun nữa!)

Một số chi tiết cụ thể về kiểu dữ liệu dillcó thể và không thể tuần tự hóa, từ trang dự án :

dill có thể ngâm các loại tiêu chuẩn sau:

none, type, bool, int, long, float, phức tạp, str, unicode, tuple, list, dict, file, buffer, buildin, cả các lớp kiểu cũ và mới, các thể hiện của các lớp kiểu cũ và mới, set, froundredet, mảng , chức năng, ngoại lệ

dill cũng có thể chọn loại tiêu chuẩn 'kỳ lạ' hơn:

các hàm với sản lượng, hàm lồng nhau, lambdas, ô, phương thức, không liên kết, mô-đun, mã, phương thức, dictproxy, phương thức mô tả, getetdescriptor, thành viên mô tả, trình bao bọc, xrange, lát, không thực hiện, dấu chấm

dill chưa thể chọn các loại tiêu chuẩn này:

khung, máy phát điện, truy nguyên


0

Tôi không thấy đề cập ở đây về phiên bản nối tiếp hoặc backcompat, vì vậy tôi sẽ đăng giải pháp của mình mà tôi đã sử dụng một chút. Tôi có thể có nhiều thứ để học hỏi hơn, cụ thể là Java và Javascript có lẽ trưởng thành hơn tôi ở đây nhưng ở đây đi

https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe


0

Để thêm tùy chọn khác: Bạn có thể sử dụng attrsgói và asdictphương thức.

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

json.dumps(objects, cls=ObjectEncoder)

và để chuyển đổi trở lại

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)

lớp học trông như thế này

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

0

Ngoài câu trả lời của Onur , Bạn có thể muốn xử lý loại thời gian như bên dưới.
(để xử lý: đối tượng 'datetime.datetime' không có ngoại lệ ' dict '.)

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

Sử dụng:

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

0

Trước tiên, chúng ta cần làm cho đối tượng của mình tuân thủ JSON, vì vậy chúng ta có thể kết xuất nó bằng mô-đun JSON tiêu chuẩn. Tôi đã làm theo cách này:

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

Xây dựng trên câu trả lời của 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)

Sự khác biệt là

  1. Hoạt động cho bất kỳ lần lặp nào thay vì chỉ listtuple(nó hoạt động cho mảng NumPy, v.v.)
  2. Hoạt động cho các loại động (những loại có chứa a __dict__).
  3. Bao gồm các kiểu gốc floatNonedo đó chúng không được chuyển đổi thành chuỗi.

Còn lại như một bài tập cho người đọc là để xử lý __slots__, các lớp vừa có thể lặp lại vừa có thành viên, các lớp là từ điển và cũng có thành viên, v.v.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.