Cách chuyển đổi dữ liệu JSON thành đối tượng Python


281

Tôi muốn sử dụng Python để chuyển đổi dữ liệu JSON thành đối tượng Python.

Tôi nhận được các đối tượng dữ liệu JSON từ API Facebook mà tôi muốn lưu trữ trong cơ sở dữ liệu của mình.

Chế độ xem hiện tại của tôi trong Django (Python) ( request.POSTchứa JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • Điều này hoạt động tốt, nhưng làm cách nào để xử lý các đối tượng dữ liệu JSON phức tạp?

  • Sẽ không tốt hơn nhiều nếu tôi bằng cách nào đó có thể chuyển đổi đối tượng JSON này thành đối tượng Python để dễ sử dụng?


Thông thường JSON được chuyển đổi thành danh sách vanilla hoặc dicts. Đó là điều bạn muốn? Hay bạn đang hy vọng chuyển đổi JSON trực tiếp thành một loại tùy chỉnh?
Shakakai

Tôi muốn chuyển đổi nó thành một đối tượng, một cái gì đó tôi có thể truy cập bằng cách sử dụng dấu "." . Giống như từ ví dụ trên -> reponse.name, answer.education.id, v.v ....
Sai Krishna

44
Sử dụng dicts là một cách yếu để làm lập trình hướng đối tượng. Từ điển là một cách rất kém để truyền đạt kỳ vọng cho người đọc mã của bạn. Sử dụng một từ điển, làm thế nào bạn có thể chỉ định rõ ràng và có thể sử dụng lại rằng một số cặp khóa-giá trị từ điển là bắt buộc, trong khi những cái khác thì không? Điều gì về việc xác nhận rằng một giá trị nhất định nằm trong phạm vi hoặc tập hợp được chấp nhận? Còn các hàm dành riêng cho loại đối tượng bạn đang làm việc (hay còn gọi là phương thức) thì sao? Từ điển là tiện dụng và linh hoạt, nhưng quá nhiều nhà phát triển hành động như họ quên Python là một ngôn ngữ hướng đối tượng vì một lý do.
Hầm

1
Có một thư viện python cho điều này github.com/jsonpickle/jsonpickle (bình luận từ câu trả lời là quá thấp trong các chủ đề và wont có thể truy cập.)
lời chúc tốt nhất

Câu trả lời:


355

Bạn có thể làm điều đó trong một dòng, sử dụng namedtupleobject_hook:

import json
from collections import namedtuple

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
print x.name, x.hometown.name, x.hometown.id

hoặc, để sử dụng lại dễ dàng:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def json2obj(data): return json.loads(data, object_hook=_json_object_hook)

x = json2obj(data)

Nếu bạn muốn nó để xử lý các phím mà không phải là tên thuộc tính tốt, hãy kiểm tra namedtuple's renametham số .


8
điều này có thể dẫn đến lỗi Giá trị, ValueError: Tên loại và tên trường không thể bắt đầu bằng một số: '123'
PvdL

3
Là một người mới sử dụng Python, tôi rất quan tâm nếu đây là một điều tiết kiệm khi bảo mật là một vấn đề.
benjist

8
Điều này tạo ra một lớp khác nhau mới mỗi lần bắt gặp một đối tượng JSON trong khi phân tích cú pháp, phải không?
fikr4n

2
Hấp dẫn. Tôi nghĩ rằng dựa vào d.keys()d.values()lặp đi lặp lại theo cùng một thứ tự là không được đảm bảo, nhưng tôi đã sai. Các tài liệu nói: "Nếu các khóa, giá trị và chế độ xem các mục được lặp đi lặp lại mà không có sửa đổi can thiệp vào từ điển, thứ tự của các mục sẽ tương ứng trực tiếp.". Tốt để biết cho các khối mã nhỏ, địa phương như vậy. Tôi muốn thêm một bình luận mặc dù để cảnh báo rõ ràng cho những người duy trì mã của một phụ thuộc như vậy.
cfi

1
Tôi không nhận thức được bất kỳ hoạt động đảo ngược mục đích chung tốt đẹp. Bất kỳ cá nhân có tên nào cũng có thể được chuyển thành một dict bằng cách sử dụng x._asdict(), điều này có thể giúp ích cho các trường hợp đơn giản.
DS.

127

Kiểm tra phần có tiêu đề Giải mã đối tượng JSON trong json tài liệu mô-đun . Bạn có thể sử dụng điều đó để giải mã một đối tượng JSON thành một loại Python cụ thể.

Đây là một ví dụ:

class User(object):
    def __init__(self, name, username):
        self.name = name
        self.username = username

import json
def object_decoder(obj):
    if '__type__' in obj and obj['__type__'] == 'User':
        return User(obj['name'], obj['username'])
    return obj

json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}',
           object_hook=object_decoder)

print type(User)  # -> <type 'type'>

Cập nhật

Nếu bạn muốn truy cập dữ liệu trong từ điển thông qua mô-đun json, hãy làm điều này:

user = json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}')
print user['name']
print user['username']

Giống như một cuốn từ điển thông thường.


1
Này, tôi vừa đọc lên và tôi nhận ra rằng từ điển sẽ hoàn toàn làm được, chỉ có tôi đang tự hỏi làm thế nào để chuyển đổi các đối tượng JSON thành từ điển và làm cách nào để truy cập dữ liệu này từ từ điển?
Sai Krishna

Tuyệt vời, nó gần như rõ ràng, chỉ muốn biết thêm một điều nữa là nếu có đối tượng này -> {'giáo dục': {'name1': 456, 'name2': 567}}, làm thế nào để tôi truy cập dữ liệu này?
Sai Krishna

nó chỉ là topLevelData ['giáo dục'] ['name1'] ==> 456. có ý nghĩa gì không?
Shakakai

1
@Ben: Tôi nghĩ bình luận của bạn không phù hợp. Trong tất cả các câu trả lời ở đây hiện tại, đây là người duy nhất có được các lớp đúng. Điều đó có nghĩa là: Đó là thao tác một lượt và kết quả sử dụng đúng loại. Bản thân Pickle dành cho các ứng dụng khác với JSON (nhị phân so với đại diện văn bản) và jsonpickle là một lib không chuẩn. Tôi muốn biết làm thế nào bạn giải quyết vấn đề mà std json lib không cung cấp cây phân tích cú pháp phía trên cho móc đối tượng
cfi

Tôi phải đồng ý với @Ben về điều này. Đây là một giải pháp thực sự tồi tệ. Không thể mở rộng được. Bạn sẽ cần duy trì tên của các trường dưới dạng chuỗi và dưới dạng trường. Nếu bạn muốn cấu trúc lại các trường của mình, việc giải mã sẽ thất bại (tất nhiên dữ liệu đã được tuần tự hóa sẽ không còn phù hợp nữa). Khái niệm tương tự đã được triển khai tốt với jsonpickle
Guyarad

98

Đây không phải là mã golf, nhưng đây là mẹo ngắn nhất của tôi, sử dụng types.SimpleNamespacelàm thùng chứa cho các đối tượng JSON.

So với namedtuplegiải pháp hàng đầu , đó là:

  • có thể nhanh hơn / nhỏ hơn vì nó không tạo ra một lớp cho mỗi đối tượng
  • ngắn hơn
  • không có renametùy chọn và có thể có cùng giới hạn đối với các khóa không phải là định danh hợp lệ (sử dụng setattrdưới nắp)

Thí dụ:

from __future__ import print_function
import json

try:
    from types import SimpleNamespace as Namespace
except ImportError:
    # Python 2.x fallback
    from argparse import Namespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

x = json.loads(data, object_hook=lambda d: Namespace(**d))

print (x.name, x.hometown.name, x.hometown.id)

2
Nhân tiện, thư viện serialization Marshmallow cung cấp một tính năng tương tự với trình @post_loadtrang trí của nó . marshmallow.readthedocs.io/en/latest/ từ
Taylor Edmiston

3
Để tránh sự phụ thuộc vào argparse: thay thế nhập khẩu argparse bằng from types import SimpleNamespacevà sử dụng:x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
maxschlepzig

8
Đây là giải pháp thanh lịch nhất, nên ở trên cùng.
ScalaWilliam

4
Đã chỉnh sửa để sử dụng giải pháp của @ maxschlepzig khi chạy dưới Python 3.x ( types.SimpleNamespacekhông may tồn tại trong 2.7, không may).
Dan Lenski

1
tại sao print_function?
chwi

90

Bạn có thể thử điều này:

class User(object):
    def __init__(self, name, username, *args, **kwargs):
        self.name = name
        self.username = username

import json
j = json.loads(your_json)
u = User(**j)

Chỉ cần tạo một Object mới và truyền các tham số dưới dạng bản đồ.


1
Tôi nhận được TypeError: Đối tượng 'Người dùng' không thể đăng ký
Mahdi

1
Đây phải là câu trả lời được chấp nhận. làm việc cho tôi quảng cáo đơn giản nhất nhiều so với tất cả phần còn lại.
Izik

Tôi đã không sử dụng * args, ** kwargs, nhưng giải pháp đã có hiệu quả.
Malkaviano

1
Người dùng (** j) nói rằng nó thiếu thông số tên và tên người dùng, làm thế nào để dict được khởi tạo?
Aaron Stainback

40

Đây là một thay thế json dưa chua nhanh chóng và bẩn

import json

class User:
    def __init__(self, name, username):
        self.name = name
        self.username = username

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

    @classmethod
    def from_json(cls, json_str):
        json_dict = json.loads(json_str)
        return cls(**json_dict)

# example usage
User("tbrown", "Tom Brown").to_json()
User.from_json(User("tbrown", "Tom Brown").to_json()).to_json()

1
Đây không phải là cách tiếp cận tốt. Lúc đầu, to_json và from_json không nên được đặt trong lớp của bạn. Lúc thứ hai, nó sẽ không hoạt động cho các lớp lồng nhau.
Jurass

17

Đối với các đối tượng phức tạp, bạn có thể sử dụng JSON Pickle

Thư viện Python để tuần tự hóa bất kỳ biểu đồ đối tượng tùy ý nào vào JSON. Nó có thể lấy gần như mọi đối tượng Python và biến đối tượng thành JSON. Ngoài ra, nó có thể khôi phục lại đối tượng thành Python.


6
Tôi nghĩ rằng jsonstruct là tốt hơn. jsonstruct originally a fork of jsonpickle (Thanks guys!). The key difference between this library and jsonpickle is that during deserialization, jsonpickle requires Python types to be recorded as part of the JSON. This library intends to remove this requirement, instead, requires a class to be passed in as an argument so that its definition can be inspected. It will then return an instance of the given class. This approach is similar to how Jackson (of Java) works.
Abhishek Gupta

3
Các vấn đề với jsonitectt là nó dường như không được duy trì (trên thực tế, nó trông có vẻ bị bỏ rơi) và nó không chuyển đổi được danh sách các đối tượng, như thế nào '[{"name":"object1"},{"name":"object2"}]'. jsonpickle cũng không xử lý nó tốt.
LS

1
Tôi không biết tại sao câu trả lời này không nhận được nhiều phiếu hơn. Hầu hết các giải pháp khác là khá ngoài kia. Ai đó đã phát triển một thư viện tuyệt vời để khử / tuần tự hóa JSON - tại sao không sử dụng nó? Ngoài ra, dường như hoạt động tốt với các danh sách - vấn đề của bạn với nó là gì?
Guyarad

1
@guyarad, vấn đề là: x = jsonpickle.decode ('[{"name": "object1"}, {"name": "object2"}]') đưa ra danh sách từ điển ([{'name': ' object1 '}, {' name ':' object2 '}]), không phải là danh sách các đối tượng có thuộc tính (x [0] .name ==' object1 '), đây là những gì câu hỏi ban đầu yêu cầu. Để có được điều đó, tôi đã kết thúc bằng cách sử dụng phương pháp object_hook / Namespace được đề xuất bởi eddygeek, nhưng cách tiếp cận nhanh / bẩn của ubershmekel cũng có vẻ tốt. Tôi nghĩ rằng tôi có thể sử dụng object_hook với set_encoder_options () (không có giấy tờ!) Của jsonpickle, nhưng sẽ mất nhiều mã hơn mô-đun json cơ bản. Tôi muốn được chứng minh là sai!
LS

@LS nếu bạn không có quyền kiểm soát đầu vào, đó thực sự là những gì OP yêu cầu, jsonpickle không lý tưởng vì nó mong đợi loại thực tế ở mỗi cấp độ (và sẽ giả sử các loại cơ bản nếu thiếu). Cả hai giải pháp đều "dễ thương".
Guyarad

12

Nếu bạn đang sử dụng Python 3.5+, bạn có thể sử dụng jsonsđể tuần tự hóa và giải tuần tự hóa thành các đối tượng Python cũ đơn giản:

import jsons

response = request.POST

# You'll need your class attributes to match your dict keys, so in your case do:
response['id'] = response.pop('user_id')

# Then you can load that dict into your class:
user = jsons.load(response, FbApiUser)

user.save()

Bạn cũng có thể tạo FbApiUserkế thừa từ jsons.JsonSerializablecho thanh lịch hơn:

user = FbApiUser.from_json(response)

Các ví dụ này sẽ hoạt động nếu lớp của bạn bao gồm các loại mặc định của Python, như chuỗi, số nguyên, danh sách, thời gian, v.v ... jsonslib sẽ yêu cầu gợi ý loại cho các loại tùy chỉnh.


7

Nếu bạn đang sử dụng python 3.6+, bạn có thể sử dụng marshmallow-dataclass . Trái với tất cả các giải pháp được liệt kê ở trên, nó vừa đơn giản, vừa an toàn:

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user, err = User.Schema().load({"name": "Ramirez"})

TypeError: make_data_class() got an unexpected keyword argument 'many'
JOhn

@JOhn: Bạn nên mở một sự cố với trường hợp kiểm tra có thể lặp lại trong github.com/lovasoa/marshmallow_dataclass/issues
lovasoa

5

Tôi đã viết một khung công tác tuần tự hóa nhỏ (de) được gọi là any2any giúp thực hiện các phép biến đổi phức tạp giữa hai loại Python.

Trong trường hợp của bạn, tôi đoán bạn muốn chuyển đổi từ một từ điển (thu được json.loads) thành một đối tượng phức tạp response.education ; response.name, với cấu trúc lồng nhau response.education.id, v.v ... Vì vậy, đó chính xác là những gì khung này được tạo ra. Tài liệu này chưa tuyệt vời, nhưng bằng cách sử dụng any2any.simple.MappingToObject, bạn sẽ có thể làm điều đó rất dễ dàng. Xin hỏi nếu bạn cần giúp đỡ.


Sebpiq, đã cài đặt any2any và đang gặp khó khăn khi hiểu chuỗi dự định của các cuộc gọi phương thức. Bạn có thể đưa ra một ví dụ đơn giản về việc chuyển đổi một từ điển thành một đối tượng Python với một thuộc tính cho mỗi khóa không?
sansjoe

Xin chào @sansjoe! Nếu bạn đã cài đặt nó từ pypi, phiên bản đã hoàn toàn lỗi thời, tôi đã thực hiện tái cấu trúc hoàn chỉnh vài tuần trước. Bạn nên sử dụng phiên bản github (Tôi cần tạo một bản phát hành phù hợp!)
sebpiq

Tôi đã cài đặt nó từ pypy vì github nói sẽ cài đặt nó từ pypy. Ngoài ra, bạn nói PyPy đã được ra khỏi tháng ngày trước .. Nó không làm việc :( Tôi nộp một báo cáo lỗi tho! Github.com/sebpiq/any2any/issues/11
sneilan

5

Cải thiện câu trả lời rất hay của lovasoa.

Nếu bạn đang sử dụng python 3.6+, bạn có thể sử dụng:
pip install marshmallow-enum
pip install marshmallow-dataclass

Nó đơn giản và loại an toàn.

Bạn có thể chuyển đổi lớp của mình theo chuỗi-json và ngược lại:

Từ Object đến String Json:

    from marshmallow_dataclass import dataclass
    user = User("Danilo","50","RedBull",15,OrderStatus.CREATED)
    user_json = User.Schema().dumps(user)
    user_json_str = user_json.data

Từ String Json đến Object:

    json_str = '{"name":"Danilo", "orderId":"50", "productName":"RedBull", "quantity":15, "status":"Created"}'
    user, err = User.Schema().loads(json_str)
    print(user,flush=True)

Định nghĩa lớp:

class OrderStatus(Enum):
    CREATED = 'Created'
    PENDING = 'Pending'
    CONFIRMED = 'Confirmed'
    FAILED = 'Failed'

@dataclass
class User:
    def __init__(self, name, orderId, productName, quantity, status):
        self.name = name
        self.orderId = orderId
        self.productName = productName
        self.quantity = quantity
        self.status = status

    name: str
    orderId: str
    productName: str
    quantity: int
    status: OrderStatus

1
Bạn không cần hàm tạo, chỉ cần truyền init = True vào dataclass và bạn đã sẵn sàng để sử dụng.
Josef Korbel

4

Vì không ai cung cấp câu trả lời khá giống của tôi, tôi sẽ đăng nó ở đây.

Đó là một lớp mạnh mẽ có thể dễ dàng chuyển đổi qua lại giữa json strdicttôi đã sao chép từ câu trả lời của mình sang câu hỏi khác :

import json

class PyJSON(object):
    def __init__(self, d):
        if type(d) is str:
            d = json.loads(d)

        self.from_dict(d)

    def from_dict(self, d):
        self.__dict__ = {}
        for key, value in d.items():
            if type(value) is dict:
                value = PyJSON(value)
            self.__dict__[key] = value

    def to_dict(self):
        d = {}
        for key, value in self.__dict__.items():
            if type(value) is PyJSON:
                value = value.to_dict()
            d[key] = value
        return d

    def __repr__(self):
        return str(self.to_dict())

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, key):
        return self.__dict__[key]

json_str = """... json string ..."""

py_json = PyJSON(json_str)

2

Sửa đổi phản hồi @DS một chút, để tải từ tệp:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def load_data(file_name):
  with open(file_name, 'r') as file_data:
    return file_data.read().replace('\n', '')
def json2obj(file_name): return json.loads(load_data(file_name), object_hook=_json_object_hook)

Một điều: điều này không thể tải các mục với số phía trước. Như thế này:

{
  "1_first_item": {
    "A": "1",
    "B": "2"
  }
}

Bởi vì "1_first_item" không phải là tên trường python hợp lệ.


2

Trong khi tìm kiếm một giải pháp, tôi đã tình cờ thấy bài đăng trên blog này: https : // blog. Maximumhege.net/2016/11/12/json-deserialization-of-nested-objects/

Nó sử dụng kỹ thuật tương tự như đã nêu trong các câu trả lời trước nhưng với cách sử dụng trang trí. Một điều nữa tôi thấy hữu ích là thực tế là nó trả về một đối tượng đã gõ vào cuối quá trình khử lưu huỳnh

class JsonConvert(object):
    class_mappings = {}

    @classmethod
    def class_mapper(cls, d):
        for keys, cls in clsself.mappings.items():
            if keys.issuperset(d.keys()):   # are all required arguments present?
                return cls(**d)
        else:
            # Raise exception instead of silently returning None
            raise ValueError('Unable to find a matching class for object: {!s}'.format(d))

    @classmethod
    def complex_handler(cls, Obj):
        if hasattr(Obj, '__dict__'):
            return Obj.__dict__
        else:
            raise TypeError('Object of type %s with value of %s is not JSON serializable' % (type(Obj), repr(Obj)))

    @classmethod
    def register(cls, claz):
        clsself.mappings[frozenset(tuple([attr for attr,val in cls().__dict__.items()]))] = cls
        return cls

    @classmethod
    def to_json(cls, obj):
        return json.dumps(obj.__dict__, default=cls.complex_handler, indent=4)

    @classmethod
    def from_json(cls, json_str):
        return json.loads(json_str, object_hook=cls.class_mapper)

Sử dụng:

@JsonConvert.register
class Employee(object):
    def __init__(self, Name:int=None, Age:int=None):
        self.Name = Name
        self.Age = Age
        return

@JsonConvert.register
class Company(object):
    def __init__(self, Name:str="", Employees:[Employee]=None):
        self.Name = Name
        self.Employees = [] if Employees is None else Employees
        return

company = Company("Contonso")
company.Employees.append(Employee("Werner", 38))
company.Employees.append(Employee("Mary"))

as_json = JsonConvert.to_json(company)
from_json = JsonConvert.from_json(as_json)
as_json_from_json = JsonConvert.to_json(from_json)

assert(as_json_from_json == as_json)

print(as_json_from_json)

2

Mở rộng câu trả lời của DS một chút, nếu bạn cần đối tượng có thể thay đổi (không có tên là tuple ), bạn có thể sử dụng thư viện bản ghi thay vì têntuple :

import json
from recordclass import recordclass

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse into a mutable object
x = json.loads(data, object_hook=lambda d: recordclass('X', d.keys())(*d.values()))

Đối tượng đã sửa đổi sau đó có thể được chuyển đổi trở lại thành json rất dễ dàng bằng cách sử dụng Simplejson :

x.name = "John Doe"
new_json = simplejson.dumps(x)

1

Nếu bạn đang sử dụng Python 3.6 hoặc mới hơn, bạn có thể xem squema - một mô-đun nhẹ cho các cấu trúc dữ liệu được nhập tĩnh. Nó làm cho mã của bạn dễ đọc đồng thời cung cấp xác thực, chuyển đổi và tuần tự hóa dữ liệu đơn giản mà không cần làm thêm. Bạn có thể nghĩ về nó như là một sự thay thế tinh vi và có nhiều ý kiến ​​hơn cho các tên và dữ liệu được đặt tên. Đây là cách bạn có thể sử dụng nó:

from uuid import UUID
from squema import Squema


class FbApiUser(Squema):
    id: UUID
    age: int
    name: str

    def save(self):
        pass


user = FbApiUser(**json.loads(response))
user.save()

Điều này cũng tương tự với các cách sử dụng ngôn ngữ JVM.
javadba

1

Tôi đã tìm kiếm một giải pháp làm việc với recordclass.RecordClass, hỗ trợ các đối tượng lồng nhau và hoạt động cho cả tuần tự hóa json và khử tuần tự json.

Mở rộng câu trả lời của DS và mở rộng về giải pháp từ BeneStr, tôi đã đưa ra những điều sau đây có vẻ hiệu quả:

Mã số:

import json
import recordclass

class NestedRec(recordclass.RecordClass):
    a : int = 0
    b : int = 0

class ExampleRec(recordclass.RecordClass):
    x : int       = None
    y : int       = None
    nested : NestedRec = NestedRec()

class JsonSerializer:
    @staticmethod
    def dumps(obj, ensure_ascii=True, indent=None, sort_keys=False):
        return json.dumps(obj, default=JsonSerializer.__obj_to_dict, ensure_ascii=ensure_ascii, indent=indent, sort_keys=sort_keys)

    @staticmethod
    def loads(s, klass):
        return JsonSerializer.__dict_to_obj(klass, json.loads(s))

    @staticmethod
    def __obj_to_dict(obj):
        if hasattr(obj, "_asdict"):
            return obj._asdict()
        else:
            return json.JSONEncoder().default(obj)

    @staticmethod
    def __dict_to_obj(klass, s_dict):
        kwargs = {
            key : JsonSerializer.__dict_to_obj(cls, s_dict[key]) if hasattr(cls,'_asdict') else s_dict[key] \
                for key,cls in klass.__annotations__.items() \
                    if s_dict is not None and key in s_dict
        }
        return klass(**kwargs)

Sử dụng:

example_0 = ExampleRec(x = 10, y = 20, nested = NestedRec( a = 30, b = 40 ) )

#Serialize to JSON

json_str = JsonSerializer.dumps(example_0)
print(json_str)
#{
#  "x": 10,
#  "y": 20,
#  "nested": {
#    "a": 30,
#    "b": 40
#  }
#}

# Deserialize from JSON
example_1 = JsonSerializer.loads(json_str, ExampleRec)
example_1.x += 1
example_1.y += 1
example_1.nested.a += 1
example_1.nested.b += 1

json_str = JsonSerializer.dumps(example_1)
print(json_str)
#{
#  "x": 11,
#  "y": 21,
#  "nested": {
#    "a": 31,
#    "b": 41
#  }
#}

1

Các câu trả lời được đưa ra ở đây không trả về đúng loại đối tượng, do đó tôi đã tạo các phương thức này bên dưới. Chúng cũng thất bại nếu bạn cố thêm nhiều trường vào lớp không tồn tại trong JSON đã cho:

def dict_to_class(class_name: Any, dictionary: dict) -> Any:
    instance = class_name()
    for key in dictionary.keys():
        setattr(instance, key, dictionary[key])
    return instance


def json_to_class(class_name: Any, json_string: str) -> Any:
    dict_object = json.loads(json_string)
    return dict_to_class(class_name, dict_object)

0

Python3.x

Aproach tốt nhất tôi có thể đạt được với kiến ​​thức của mình là điều này.
Lưu ý rằng mã này xử lý set () quá.
Cách tiếp cận này là chung chung chỉ cần mở rộng lớp (trong ví dụ thứ hai).
Lưu ý rằng tôi chỉ thực hiện nó với các tệp, nhưng thật dễ dàng để sửa đổi hành vi theo sở thích của bạn.

Tuy nhiên đây là một CoDec.

Với một chút công việc hơn, bạn có thể xây dựng lớp học của mình theo những cách khác. Tôi giả sử một hàm tạo mặc định để thể hiện nó, sau đó tôi cập nhật dict lớp.

import json
import collections


class JsonClassSerializable(json.JSONEncoder):

    REGISTERED_CLASS = {}

    def register(ctype):
        JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in self.REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = self.REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


JsonClassSerializable.register(C)


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


JsonClassSerializable.register(B)


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()

JsonClassSerializable.register(A)

A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)

Biên tập

Với một số nghiên cứu nữa, tôi đã tìm ra một cách để khái quát hóa mà không cần đến cuộc gọi phương thức đăng ký SUPERCLASS , sử dụng siêu dữ liệu

import json
import collections

REGISTERED_CLASS = {}

class MetaSerializable(type):

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in REGISTERED_CLASS:
            REGISTERED_CLASS[cls.__name__] = cls
        return super(MetaSerializable, cls).__call__(*args, **kwargs)


class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()


A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s

0

Bạn có thể dùng

x = Map(json.loads(response))
x.__class__ = MyClass

Ở đâu

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v
                    if isinstance(v, dict):
                        self[k] = Map(v)

        if kwargs:
            # for python 3 use kwargs.items()
            for k, v in kwargs.iteritems():
                self[k] = v
                if isinstance(v, dict):
                    self[k] = Map(v)

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Đối với một giải pháp chung, bằng chứng trong tương lai.


-5

Sử dụng jsonmô-đun ( mới trong Python 2.6 ) hoặc simplejsonmô-đun hầu như luôn được cài đặt.


2
Này, cảm ơn bạn đã trả lời. Bạn có thể vui lòng đăng một ví dụ về cách giải mã JSON và sau đó truy cập dữ liệu đó không?
Sai Krishna

Này, bây giờ bạn đã có một điểm nhưng bằng cách nào đó, tôi thích làm mà không biết và sau đó đảo ngược nó: D.
Sai Krishna

1
@Zach: có các ví dụ ngay trên đầu các tài liệu tôi liên kết đến.
Chris Morgan
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.