[英]How to convert JSON data into a Python object?
我想將JSON數據轉換成Python object。
我從 Facebook API 收到 JSON 數據對象,我想將其存儲在我的數據庫中。
我在 Django (Python) 中的當前視圖( request.POST
包含 JSON):
response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
更新
使用 Python3,您可以使用SimpleNamespace
和object_hook
在一行中完成:
import json
from types import SimpleNamespace
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: SimpleNamespace(**d))
print(x.name, x.hometown.name, x.hometown.id)
舊答案(Python2)
在 Python2 中,您可以使用namedtuple
和object_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
或者,要輕松地重用它:
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)
如果您希望它處理不是好的屬性名稱的鍵,請查看namedtuple
的rename
參數。
查看json
模塊文檔中標題為“專業化 JSON object 解碼”的部分。 您可以使用它將 JSON object 解碼為特定的 Python 類型。
這是一個例子:
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'>
更新
如果您想通過 json 模塊訪問字典中的數據,請執行以下操作:
user = json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}')
print user['name']
print user['username']
就像一本普通的字典。
你可以試試這個:
class User(object):
def __init__(self, name, username):
self.name = name
self.username = username
import json
j = json.loads(your_json)
u = User(**j)
只需創建一個新的 object,並將參數作為 map 傳遞。
注意:它不適用於嵌套類。
您也可以使用帶有對象的 JSON :
import json
class Address(object):
def __init__(self, street, number):
self.street = street
self.number = number
def __str__(self):
return "{0} {1}".format(self.street, self.number)
class User(object):
def __init__(self, name, address):
self.name = name
self.address = Address(**address)
def __str__(self):
return "{0} ,{1}".format(self.name, self.address)
if __name__ == '__main__':
js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
j = json.loads(js)
print(j)
u = User(**j)
print(u)
這不是代碼高爾夫,但這是我最短的技巧,使用types.SimpleNamespace
作為 JSON 對象的容器。
與領先的namedtuple
解決方案相比,它是:
rename
選項,並且可能對無效標識符的鍵有相同的限制(在封面下使用setattr
)例子:
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)
這是一個快速而骯臟的 json 泡菜替代品
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()
對於復雜的對象,可以使用JSON Pickle
Python 庫,用於將任意 object 圖形序列化為 JSON。 幾乎可以使用任何 Python object 並將 object 變成 Z0ECD11C1D7A287401D148A23BBD7。 此外,它可以將 object 重組回 Python。
如果您使用的是 Python 3.5+,則可以使用jsons
序列化和反序列化為普通的舊 Python 對象:
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()
您還可以讓FbApiUser
繼承自jsons.JsonSerializable
以獲得更優雅:
user = FbApiUser.from_json(response)
如果您的 class 包含 Python 默認類型(如字符串、整數、列表、日期時間等),則這些示例將起作用。不過, jsons
庫將需要自定義類型的類型提示。
如果您使用的是 python 3.6+,則可以使用marshmallow- dataclass 。 與上面列出的所有解決方案相反,它既簡單又類型安全:
from marshmallow_dataclass import dataclass
@dataclass
class User:
name: str
user = User.Schema().load({"name": "Ramirez"})
改進 lovasoa 的非常好的答案。
如果您使用的是 python 3.6+,您可以使用:
pip install marshmallow-enum
和pip install marshmallow-dataclass
它簡單且類型安全。
您可以將 class 轉換為 string-json,反之亦然:
從 Object 到字符串 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
從字符串 Json 到 Object:
json_str = '{"name":"Danilo", "orderId":"50", "productName":"RedBull", "quantity":15, "status":"Created"}'
user, err = User.Schema().loads(json_str)
print(user,flush=True)
Class 定義:
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
dacite也可能是您的解決方案,它支持以下功能:
https://pypi.org/project/dacite/
from dataclasses import dataclass
from dacite import from_dict
@dataclass
class User:
name: str
age: int
is_active: bool
data = {
'name': 'John',
'age': 30,
'is_active': True,
}
user = from_dict(data_class=User, data=data)
assert user == User(name='John', age=30, is_active=True)
由於沒有人提供像我這樣的答案,我將在此處發布。
它是一個強大的 class 可以輕松地在 JSON str
和dict
之間來回轉換,我從我對另一個問題的回答中復制了它:
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)
我編寫了一個名為any2any的小型(反)序列化框架,它有助於在兩個 Python 類型之間進行復雜的轉換。
在您的情況下,我想您想從字典(使用json.loads
獲得)轉換為復雜的 object response.education; response.name
response.education; response.name
,具有嵌套結構response.education.id
等......所以這正是這個框架的目的。 文檔還不是很好,但是通過使用any2any.simple.MappingToObject
,您應該能夠非常輕松地做到這一點。 請詢問您是否需要幫助。
稍微擴展 DS 的答案,如果您需要 object 是可變的(namedtuple 不是),您可以使用recordclass庫而不是 namedtuple:
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()))
修改后的 object 然后可以使用simplejson很容易地轉換回 json :
x.name = "John Doe"
new_json = simplejson.dumps(x)
以下代碼使用對象鍵遞歸地創建動態屬性。
JSON object - fb_data.json
:
{
"name": "John Smith",
"hometown": {
"name": "New York",
"id": 123
},
"list": [
"a",
"b",
"c",
1,
{
"key": 1
}
],
"object": {
"key": {
"key": 1
}
}
}
在轉換中,我們有 3 種情況:
import json
class AppConfiguration(object):
def __init__(self, data=None):
if data is None:
with open("fb_data.json") as fh:
data = json.loads(fh.read())
else:
data = dict(data)
for key, val in data.items():
setattr(self, key, self.compute_attr_value(val))
def compute_attr_value(self, value):
if isinstance(value, list):
return [self.compute_attr_value(x) for x in value]
elif isinstance(value, dict):
return AppConfiguration(value)
else:
return value
if __name__ == "__main__":
instance = AppConfiguration()
print(instance.name)
print(instance.hometown.name)
print(instance.hometown.id)
print(instance.list[4].key)
print(instance.object.key.key)
現在鍵值對是屬性 - 對象。
output:
John Smith
New York
123
1
1
Supports TypeScript
, Python
, Go
, Ruby
, C#
, Java
, Swift
, Rust
, Kotlin
, C++
, Flow
, Objective-C
, JavaScript
, Elm
, and JSON Schema
.
quicktype
從樣本 JSON 數據中推斷類型,然后輸出強類型模型和序列化程序,以便以所需的編程語言處理該數據。
output:
# Generated by https://quicktype.io
#
# To change quicktype's target language, run command:
#
# "Set quicktype target language"
from typing import List, Union
class Hometown:
name: str
id: int
def __init__(self, name: str, id: int) -> None:
self.name = name
self.id = id
class Key:
key: int
def __init__(self, key: int) -> None:
self.key = key
class Object:
key: Key
def __init__(self, key: Key) -> None:
self.key = key
class FbData:
name: str
hometown: Hometown
list: List[Union[Key, int, str]]
object: Object
def __init__(self, name: str, hometown: Hometown, list: List[Union[Key, int, str]], object: Object) -> None:
self.name = name
self.hometown = hometown
self.list = list
self.object = object
此擴展在Visual Studio Code Marketplace中免費提供。
在尋找解決方案時,我偶然發現了這篇博文: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/
它使用與先前答案中所述相同的技術,但使用了裝飾器。 我發現有用的另一件事是它在反序列化結束時返回一個類型為 object
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)
用法:
@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)
我認為最輕的解決方案是
import orjson # faster then json =)
from typing import NamedTuple
_j = '{"name":"Иван","age":37,"mother":{"name":"Ольга","age":58},"children":["Маша","Игорь","Таня"],"married": true,' \
'"dog":null} '
class PersonNameAge(NamedTuple):
name: str
age: int
class UserInfo(NamedTuple):
name: str
age: int
mother: PersonNameAge
children: list
married: bool
dog: str
j = json.loads(_j)
u = UserInfo(**j)
print(u.name, u.age, u.mother, u.children, u.married, u.dog)
>>> Ivan 37 {'name': 'Olga', 'age': 58} ['Mary', 'Igor', 'Jane'] True None
稍微修改 @DS 響應,以從文件加載:
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)
一件事:這無法加載帶有數字的項目。 像這樣:
{
"1_first_item": {
"A": "1",
"B": "2"
}
}
因為“1_first_item”不是有效的 python 字段名稱。
這里給出的答案沒有返回正確的 object 類型,因此我在下面創建了這些方法。 如果您嘗試向給定 JSON 中不存在的 class 添加更多字段,它們也會失敗:
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)
已經有多個可行的答案,但是有一些個人制作的小型庫可以為大多數用戶解決問題。
一個例子是json2object 。 給定一個定義的 class,它將 json 數據反序列化為您的自定義 model,包括自定義屬性和子對象。
它的使用非常簡單。 圖書館維基的一個例子:
from json2object import jsontoobject as jo class Student: def __init__(self): self.firstName = None self.lastName = None self.courses = [Course('')] class Course: def __init__(self, name): self.name = name data = '''{ "firstName": "James", "lastName": "Bond", "courses": [{ "name": "Fighting"}, { "name": "Shooting"} ] } ''' model = Student() result = jo.deserialize(data, model) print(result.courses[0].name)
class SimpleClass:
def __init__(self, **kwargs):
for k, v in kwargs.items():
if type(v) is dict:
setattr(self, k, SimpleClass(**v))
else:
setattr(self, k, v)
json_dict = {'name': 'jane doe', 'username': 'jane', 'test': {'foo': 1}}
class_instance = SimpleClass(**json_dict)
print(class_instance.name, class_instance.test.foo)
print(vars(class_instance))
dataclass-wizard是一個現代選項,同樣可以為您工作。 它支持自動鍵大小寫轉換,例如camelCase或TitleCase ,這兩者在 API 響應中都很常見。
將實例轉儲到dict
/JSON 時的默認鍵轉換是camelCase ,但可以使用主數據類上提供的元配置輕松覆蓋。
https://pypi.org/project/dataclass-wizard/
from dataclasses import dataclass
from dataclass_wizard import fromdict, asdict
@dataclass
class User:
name: str
age: int
is_active: bool
data = {
'name': 'John',
'age': 30,
'isActive': True,
}
user = fromdict(User, data)
assert user == User(name='John', age=30, is_active=True)
json_dict = asdict(user)
assert json_dict == {'name': 'John', 'age': 30, 'isActive': True}
設置 Meta 配置的示例,當序列化為dict
/JSON 時將字段轉換為lisp-case :
DumpMeta(key_transform='LISP').bind_to(User)
如果您使用的是 Python 3.6 或更新版本,您可以查看squema - 一個用於靜態類型數據結構的輕量級模塊。 它使您的代碼易於閱讀,同時提供簡單的數據驗證、轉換和序列化,無需額外工作。 您可以將其視為命名元組和數據類的更復雜和自以為是的替代方案。 以下是您可以使用它的方法:
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()
您可以使用
x = Map(json.loads(response))
x.__class__ = MyClass
在哪里
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]
對於通用的、面向未來的解決方案。
我正在尋找與recordclass.RecordClass
一起使用的解決方案,支持嵌套對象並且適用於 json 序列化和 json 反序列化。
擴展 DS 的答案,並擴展 BeneStr 的解決方案,我想出了以下似乎可行的方法:
代碼:
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)
用法:
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
# }
#}
Python3.x
我能用我的知識達到的最好方法就是這個。
請注意,此代碼也處理 set()。
這種方法是通用的,只需要擴展 class(在第二個示例中)。
請注意,我只是對文件執行此操作,但根據您的喜好修改行為很容易。
然而,這是一個編解碼器。
多做一點工作,您就可以用其他方式構建您的 class。 我假設一個默認構造函數來實例化它,然后我更新 class 字典。
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)
編輯
通過更多的研究,我找到了一種無需SUPERCLASS注冊方法調用即可泛化的方法,使用元類
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
這不是一件很困難的事情,我看到了上面的答案,其中大多數在“列表”中都有性能問題
這段代碼比上面的要快得多
import json
class jsonify:
def __init__(self, data):
self.jsonify = data
def __getattr__(self, attr):
value = self.jsonify.get(attr)
if isinstance(value, (list, dict)):
return jsonify(value)
return value
def __getitem__(self, index):
value = self.jsonify[index]
if isinstance(value, (list, dict)):
return jsonify(value)
return value
def __setitem__(self, index, value):
self.jsonify[index] = value
def __delattr__(self, index):
self.jsonify.pop(index)
def __delitem__(self, index):
self.jsonify.pop(index)
def __repr__(self):
return json.dumps(self.jsonify, indent=2, default=lambda x: str(x))
示例
response = jsonify(
{
'test': {
'test1': [{'ok': 1}]
}
}
)
response.test -> jsonify({'test1': [{'ok': 1}]})
response.test.test1 -> jsonify([{'ok': 1}])
response.test.test1[0] -> jsonify({'ok': 1})
response.test.test1[0].ok -> int(1)
這似乎是一個 AB 問題(問 A 實際問題在哪里 B)。
問題的根源是:如何有效地引用/修改深層嵌套的 JSON 結構,而無需執行 ob['foo']['bar'][42]['quux'],這帶來了打字挑戰,代碼-膨脹問題,可讀性問題和錯誤捕獲問題?
使用glom
https://glom.readthedocs.io/en/latest/tutorial.html
from glom import glom
# Basic deep get
data = {'a': {'b': {'c': 'd'}}}
print(glom(data, 'a.b.c'))
它還將處理列表項: glom(data, 'abc.42.d')
我已經將它與一個幼稚的實現進行了基准測試:
def extract(J, levels):
# Twice as fast as using glom
for level in levels.split('.'):
J = J[int(level) if level.isnumeric() else level]
return J
...它在復雜的 JSON object 上返回 0.14 毫秒,而天真的 impl 則為 0.06 毫秒。
它還可以處理復雜的查詢,例如提取所有foo.bar.records
where .name == 'Joe Bloggs'
編輯:
另一種高效的方法是遞歸使用覆蓋__getitem__
和__getattr__
的 class :
class Ob:
def __init__(self, J):
self.J = J
def __getitem__(self, index):
return Ob(self.J[index])
def __getattr__(self, attr):
value = self.J.get(attr, None)
return Ob(value) if type(value) in (list, dict) else value
現在你可以這樣做:
ob = Ob(J)
# if you're fetching a final raw value (not list/dict
ob.foo.bar[42].quux.leaf
# for intermediate values
ob.foo.bar[42].quux.J
這也令人驚訝地很好地進行了基准測試。 可與我之前的幼稚 impl 相媲美。 如果有人能找到一種方法來整理非葉查詢的訪問權限,請發表評論!
這是我的方式。
import typing
class User:
name: str
age: int
def __init__(self, data: dict):
for k, _ in typing.get_type_hints(self).items():
setattr(self, k, data[k])
data = {
"name": "Susan",
"age": 18
}
user = User(data)
print(user.name, user.age)
# Output: Susan 18
def load_model_from_dict(self, data: dict):
for key, value in data.items():
self.__dict__[key] = value
return self
它有助於返回您自己的 model,其中包含 dict 中不可預見的變量。
所以我正在尋找一種方法來解組任何任意類型(想想數據類的字典,或者數據類數組的字典),而不需要大量的自定義反序列化代碼。
這是我的方法:
import json
from dataclasses import dataclass, make_dataclass
from dataclasses_json import DataClassJsonMixin, dataclass_json
@dataclass_json
@dataclass
class Person:
name: str
def unmarshal_json(data, t):
Unmarhsal = make_dataclass('Unmarhsal', [('res', t)],
bases=(DataClassJsonMixin,))
d = json.loads(data)
out = Unmarhsal.from_dict({"res": d})
return out.res
unmarshalled = unmarshal_json('{"1": {"name": "john"} }', dict[str, Person])
print(unmarshalled)
打印: {'1': Person(name='john')}
If you are looking for type safe deserialization of JSON or any complex dict into a python class I would highly recommend pydantic , which not only has a succinct API (does not require writing 'helper' boilerplate), can integrate with Python dataclasses but has static復雜和嵌套數據結構的運行時類型驗證。
示例用法:
from pydantic import BaseModel
from datetime import datetime
class Item(BaseModel):
field1: str | int # union
field2: int | None = None # optional
field3: str = 'default' # default values
class User(BaseModel):
name: str | None = None
username: str
created: datetime # default type converters
items: list[Item] = [] # nested complex types
data = {
'name': 'Jane Doe',
'username': 'user1',
'created': '2020-12-31T23:59:00+10:00',
'items': [
{'field1': 1, 'field2': 2},
{'field1': 'b'},
{'field1': 'c', 'field3': 'override'}
]
}
user: User = User(**data)
有關更多詳細信息和功能,請查看 pydantic 在其文檔中的理性部分。
使用 python 3.7,我發現以下內容非常簡單有效。 在這種情況下,將 JSON 從文件加載到字典中:
class Characteristic:
def __init__(self, characteristicName, characteristicUUID):
self.characteristicName = characteristicName
self.characteristicUUID = characteristicUUID
class Service:
def __init__(self, serviceName, serviceUUID, characteristics):
self.serviceName = serviceName
self.serviceUUID = serviceUUID
self.characteristics = characteristics
class Definitions:
def __init__(self, services):
self.services = []
for service in services:
self.services.append(Service(**service))
def main():
parser = argparse.ArgumentParser(
prog="BLEStructureGenerator",
description="Taking in a JSON input file which lists all of the services, "
"characteristics and encoded properties. The encoding takes in "
"another optional template services and/or characteristics "
"file where the JSON file contents are applied to the templates.",
epilog="Copyright Brown & Watson International"
)
parser.add_argument('definitionfile',
type=argparse.FileType('r', encoding='UTF-8'),
help="JSON file which contains the list of characteristics and "
"services in the required format")
parser.add_argument('-s', '--services',
type=argparse.FileType('r', encoding='UTF-8'),
help="Services template file to be used for each service in the "
"JSON file list")
parser.add_argument('-c', '--characteristics',
type=argparse.FileType('r', encoding='UTF-8'),
help="Characteristics template file to be used for each service in the "
"JSON file list")
args = parser.parse_args()
definition_dict = json.load(args.definitionfile)
definitions = Definitions(**definition_dict)
使用json
模塊( Python 2.6 中的新模塊)或幾乎總是安裝的simplejson
模塊。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.