2012-04-20 37 views
73

Tôi đang cố tạo một biểu diễn chuỗi JSON của một cá thể lớp và gặp khó khăn. Hãy nói rằng lớp được xây dựng như thế này:Nối tiếp lớp thể hiện thành JSON

class testclass: 
    value1 = "a" 
    value2 = "b" 

Một cuộc gọi đến các json.dumps được thực hiện như thế này:

t = testclass() 
json.dumps(t) 

Nó được thất bại và nói với tôi rằng TestClass không phải là JSON serializable.

TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable 

Tôi cũng đã cố gắng sử dụng các module dưa:

t = testclass() 
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL)) 

Và nó cung cấp thông tin lớp dụ nhưng không phải là một nội dung theo bộ của instance lớp.

b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.' 

Tôi đang làm gì sai?

+0

http://stackoverflow.com/questions/2343535/easiest-way-to-serialize-a-simple-class-object-with-simplejson – delicateLatticeworkFever

+3

Sử dụng một dòng, 's = json.dumps (obj , default = lambda x: x .__ dict __) ', để tuần tự hóa các biến cá thể của đối tượng (' self.value1', 'self.value2', ...). Cách đơn giản nhất và thẳng tiến nhất của nó. Nó sẽ tuần tự hóa các cấu trúc đối tượng lồng nhau. Hàm 'default' được gọi khi một đối tượng nào đó không được nối tiếp trực tiếp. Bạn cũng có thể xem câu trả lời của tôi bên dưới. Tôi tìm thấy những câu trả lời phổ biến không cần thiết phức tạp, mà có lẽ đã đúng một thời gian khá dài. – codeman48

+0

'testclass' của bạn không có phương thức' __init __() ', vì vậy tất cả các cá thể sẽ chia sẻ cùng một thuộc tính hai lớp (' value1' và 'value2') được định nghĩa trong câu lệnh lớp. Bạn có hiểu sự khác biệt giữa một lớp và một thể hiện của một lớp không? – martineau

Trả lời

100

Vấn đề cơ bản là bộ mã hóa JSON json.dumps() chỉ biết cách tuần tự hóa một tập hợp các loại đối tượng hạn chế theo mặc định, tất cả các loại được cài sẵn. Danh sách ở đây: https://docs.python.org/3.3/library/json.html#encoders-and-decoders

Một giải pháp tốt là làm cho lớp của bạn được kế thừa từ JSONEncoder và sau đó triển khai hàm JSONEncoder.default() và làm cho hàm đó phát ra đúng JSON cho lớp của bạn.

Một giải pháp đơn giản là gọi số json.dumps() trên thành viên .__dict__ của trường hợp đó. Đó là một tiêu chuẩn Python dict và nếu lớp của bạn là đơn giản, nó sẽ được JSON serializable.

class Foo(object): 
    def __init__(self): 
     self.x = 1 
     self.y = 2 

foo = Foo() 
s = json.dumps(foo) # raises TypeError with "is not JSON serializable" 

s = json.dumps(foo.__dict__) # s set to: {"x":1, "y":2} 

Phương pháp trên được thảo luận trong bài viết blog này:

        Serializing arbitrary Python objects to JSON using __dict__

Chú ý: Tôi đã chỉnh sửa câu trả lời này; phiên bản gốc chỉ thảo luận cách tiếp cận tuần tự .__dict__.

+1

Tôi đã thử điều này. Kết quả cuối cùng của cuộc gọi đến json.dumps (t .__ dict__) chỉ là {}. – ferhan

+5

Đó là vì lớp của bạn không có hàm '.__ init __()', vì vậy cá thể lớp của bạn có một từ điển trống. Nói cách khác, '{}' là kết quả chính xác cho mã ví dụ của bạn. – steveha

+2

Cảm ơn. Điều này làm các trick. Tôi đã thêm một __init__ đơn giản không có tham số và hiện đang gọi json.dumps (t .__ dict__) trả về dữ liệu thích hợp theo định dạng: {"value2": "345", "value1": "123"} Tôi đã có nhìn thấy các bài viết như thế này trước đây, không chắc liệu tôi có cần một serializer tùy chỉnh cho các thành viên hay không, cần __init__ không được đề cập một cách rõ ràng hoặc tôi đã bỏ lỡ nó. Cảm ơn bạn. – ferhan

2

JSON không thực sự có nghĩa là để tuần tự hóa các đối tượng Python tùy ý. Thật tuyệt vời khi tuần tự hóa các đối tượng dict, nhưng mô-đun pickle thực sự là những gì bạn nên sử dụng nói chung. Đầu ra từ pickle không thực sự có thể đọc được, nhưng nó không được sử dụng tốt. Nếu bạn nhấn mạnh vào việc sử dụng JSON, bạn có thể kiểm tra mô-đun jsonpickle, đây là một phương pháp lai thú vị.

https://github.com/jsonpickle/jsonpickle

+6

Vấn đề chính tôi thấy với dưa chua là định dạng đặc trưng của Python, trong khi JSON là định dạng độc lập nền tảng.JSON đặc biệt hữu ích nếu bạn đang viết một ứng dụng web hoặc một chương trình phụ trợ cho một số ứng dụng di động. Điều đó đã được nói, cảm ơn vì đã chỉ ra jsonpickle. –

+0

@Haroldo_OK Không jsonpickle vẫn xuất khẩu sang JSON, chỉ cần không phải là rất con người có thể đọc được? – Caelum

15

Tôi chỉ làm:

data=json.dumps(myobject.__dict__) 

Đây không phải là toàn bộ câu trả lời, và nếu bạn có một số loại lớp đối tượng phức tạp bạn chắc chắn sẽ không có được tất cả mọi thứ. Tuy nhiên tôi sử dụng điều này cho một số đối tượng đơn giản của tôi.

Một cách hoạt động thực sự tốt là lớp "tùy chọn" mà bạn nhận được từ mô-đun OptionParser. Ở đây nó cùng với yêu cầu JSON.

def executeJson(self, url, options): 
     data=json.dumps(options.__dict__) 
     if options.verbose: 
      print data 
     headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} 
     return requests.post(url, data, headers=headers) 
+0

Bạn có thể muốn tự xóa, nếu bạn không sử dụng nó trong một lớp học. – SpiRail

+1

Điều đó sẽ hoạt động tốt, miễn là đối tượng không bao gồm các đối tượng khác. –

11

Có một cách mà làm việc tuyệt vời cho tôi rằng bạn có thể thử:

json.dumps() có thể mất một tham số tùy chọn mặc định nơi bạn có thể chỉ định một chức năng tùy chỉnh serializer với nhiều loại không rõ, mà trong trường hợp của tôi trông giống như

def serialize(obj): 
    """JSON serializer for objects not serializable by default json code""" 

    if isinstance(obj, date): 
     serial = obj.isoformat() 
     return serial 

    if isinstance(obj, time): 
     serial = obj.isoformat() 
     return serial 

    return obj.__dict__ 

hai IFS Đầu tiên là dành cho ngày và thời gian serialization và sau đó có một obj.__dict__ trở đối với bất kỳ đối tượng khác.

cuộc gọi cuối cùng trông như:

json.dumps(myObj, default=serialize) 

Nó đặc biệt tốt khi bạn đang serializing một bộ sưu tập và bạn không muốn gọi __dict__ một cách rõ ràng cho mọi đối tượng. Ở đây nó được thực hiện cho bạn tự động.

Cho đến giờ, tôi đã làm việc rất tốt cho tôi, mong chờ những suy nghĩ của bạn.

+0

Rất cám ơn câu trả lời này - nó đã giúp tôi rất nhiều. – Caribou

+1

Bạn sẽ nhận được nhiều điểm hơn cho câu trả lời này :) nó là một giải pháp rất đẹp và thanh lịch !! – keisar

0

Tôi tin rằng thay vì thừa kế như được đề xuất trong câu trả lời được chấp nhận, tốt hơn là nên sử dụng đa hình. Nếu không, bạn phải có một tuyên bố lớn nếu khác để tùy chỉnh mã hóa của mọi đối tượng. Điều đó có nghĩa tạo ra một bộ mã hóa mặc định chung cho JSON như:

def jsonDefEncoder(obj): 
    if hasattr(obj, 'jsonEnc'): 
     return obj.jsonEnc() 
    else: #some default behavior 
     return obj.__dict__ 

và sau đó có một chức năng jsonEnc() trong mỗi lớp bạn muốn serialize. ví dụ.

class A(object): 
    def __init__(self,lengthInFeet): 
     self.lengthInFeet=lengthInFeet 
    def jsonEnc(self): 
     return {'lengthInMeters': lengthInFeet * 0.3 } # each foot is 0.3 meter 

Sau đó, bạn gọi json.dumps(classInstance,default=jsonDefEncoder)

3

Sử dụng jsonpickle

import jsonpickle 

object = YourClass() 
json_object = jsonpickle.encode(object) 
3

Bạn có thể chỉ định default tên tham số trong json.dumps() chức năng:

json.dumps(obj, default=lambda x: x.__dict__) 

Giải thích:

Mẫu các tài liệu (2.7, 3.6):

``default(obj)`` is a function that should return a serializable version 
of obj or raise TypeError. The default simply raises TypeError. 

(Hoạt động trên Python 2.7 và Python 3.x)

Lưu ý: Trong trường hợp này, bạn cần instance biến này và không class biến, như ví dụ trong câu hỏi cố gắng làm. (Tôi giả định người hỏi có nghĩa là class instance là một đối tượng của một lớp học)

Tôi đã học được điều này trước tiên từ câu trả lời của @ phihag here. Tìm thấy nó là cách đơn giản và sạch nhất để thực hiện công việc.

0

Có một số câu trả lời hay về cách bắt đầu thực hiện việc này. Nhưng có một số điều cần lưu ý:

  • Điều gì xảy ra nếu cá thể được lồng trong cấu trúc dữ liệu lớn?
  • Nếu bạn muốn tên lớp thì sao?
  • Điều gì xảy ra nếu bạn muốn deserialize trường hợp?
  • Nếu bạn đang sử dụng __slots__ thay vì __dict__ thì sao?
  • Nếu bạn không muốn tự làm điều đó thì sao?

json-tricks là thư viện (do tôi tạo và những người khác đóng góp) đã có thể thực hiện việc này trong một thời gian dài. Ví dụ:

class MyTestCls: 
    def __init__(self, **kwargs): 
     for k, v in kwargs.items(): 
      setattr(self, k, v) 

cls_instance = MyTestCls(s='ub', dct={'7': 7}) 

json = dumps(cls_instance, indent=4) 
instance = loads(json) 

Bạn sẽ lấy lại bản sao. Ở đây json trông như thế này:

{ 
    "__instance_type__": [ 
     "json_tricks.test_class", 
     "MyTestCls" 
    ], 
    "attributes": { 
     "s": "ub", 
     "dct": { 
      "7": 7 
     } 
    } 
} 

Nếu bạn muốn thực hiện giải pháp của riêng bạn, bạn có thể nhìn vào nguồn gốc của json-tricks để không quên một số trường hợp đặc biệt (như __slots__).

Nó cũng thực hiện các loại khác như mảng numpy, datetimes, số phức; nó cũng cho phép nhận xét.

1

Dưới đây là hai chức năng đơn giản để tuần tự hóa các lớp không phức tạp, không có gì lạ mắt như được giải thích trước đây.

Tôi sử dụng tính năng này cho công cụ loại cấu hình vì tôi có thể thêm thành viên mới vào các lớp mà không cần điều chỉnh mã.

import json 

class SimpleClass: 
    def __init__(self, a=None, b=None, c=None): 
     self.a = a 
     self.b = b 
     self.c = c 

def serialize_json(instance=None, path=None): 
    dt = {} 
    dt.update(vars(instance)) 

    with open(path, "w") as file: 
     json.dump(dt, file) 

def deserialize_json(cls=None, path=None): 
    def read_json(_path): 
     with open(_path, "r") as file: 
      return json.load(file) 

    data = read_json(path) 

    instance = object.__new__(cls) 

    for key, value in data.items(): 
     setattr(instance, key, value) 

    return instance 

# Usage: Create class and serialize under Windows file system. 
write_settings = SimpleClass(a=1, b=2, c=3) 
serialize_json(write_settings, r"c:\temp\test.json") 

# Read back and rehydrate. 
read_settings = deserialize_json(SimpleClass, r"c:\temp\test.json") 

# results are the same. 
print(vars(write_settings)) 
print(vars(read_settings)) 

# output: 
# {'c': 3, 'b': 2, 'a': 1} 
# {'c': 3, 'b': 2, 'a': 1} 
Các vấn đề liên quan