把JSON數(shù)據(jù)格式轉(zhuǎn)換為Python的類(lèi)對(duì)象方法詳解(兩種方法)
JOSN字符串轉(zhuǎn)換為自定義類(lèi)實(shí)例對(duì)象
有時(shí)候我們有這種需求就是把一個(gè)JSON字符串轉(zhuǎn)換為一個(gè)具體的Python類(lèi)的實(shí)例,比如你接收到這樣一個(gè)JSON字符串如下:
{"Name": "Tom", "Sex": "Male", "BloodType": "A", "Hobbies": ["籃球", "足球"]}
我需要把這個(gè)轉(zhuǎn)換為具體的一個(gè)Person類(lèi)的實(shí)例,通過(guò)對(duì)象的方式來(lái)進(jìn)行操作。在Java中有很多實(shí)現(xiàn)比如Gson或者FastJosn。如下代碼所示(這里不是全部代碼,值標(biāo)識(shí)最主要的部分):
import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.Product;
String a = "{\"gmtCreate\":1559009853000,\"dataFormat\":1,\"deviceCount\":1,\"nodeType\":0,\"productKey\":\"a1U85pSQrAz\",\"productName\":\"溫度計(jì)\"}";
//JSON字符串反序列化為一個(gè)Product對(duì)象
Product product = JSONObject.parseObject(a, Product.class);
上述這種需求一般發(fā)生在前段傳遞過(guò)來(lái)JSON字符串或者其他系統(tǒng)進(jìn)行RPC通信的時(shí)候也發(fā)送過(guò)來(lái)JSON字符串,作為接收端需要反序列化成對(duì)象來(lái)進(jìn)行處理,而且Fastjson里還有一個(gè)JSONArray.parseArray方法可以轉(zhuǎn)換為對(duì)象列表。可是在Python沒(méi)有像Java中這么方便的東西。
從網(wǎng)上論壇中也看到過(guò)一些,不過(guò)很多都是效果有但是使用起來(lái)麻煩,所以我這里也來(lái)說(shuō)一下我的思路。
方式1:通過(guò)josn.loads來(lái)實(shí)現(xiàn)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import json
class Person:
def __init__(self, data=None):
self._name = "1"
self._sex = ""
self._blood_type = "O"
self._hobbies = []
self._date_of_birth = "1900/1/1"
if data:
self.__dict__ = data
# 通過(guò)屬性的方式來(lái)獲取和設(shè)置實(shí)例變量的值,如果不這樣那么就只能通過(guò)set或者get方法來(lái)做
@property
def date_of_brith(self):
return self._date_of_birth
@date_of_brith.setter
def date_of_brith(self, date_of_brith):
self._date_of_birth = date_of_brith
def main():
try:
str1 = '{"name": "Tom", "sex": "male", "blood_type": "A", "hobbies": ["籃球", "足球"]}'
person1 = json.loads(str1, object_hook=Person)
print(isinstance(person1, Person))
# 這里你會(huì)發(fā)現(xiàn)沒(méi)有date_of_brith這個(gè)內(nèi)容
print(person1.__dict__)
# 獲取date_of_brith屬性值報(bào)錯(cuò),因?yàn)镴SON字符串不包含這個(gè)鍵,但是類(lèi)中的實(shí)例變量有這個(gè),正常來(lái)講你應(yīng)該可以獲取默認(rèn)值,但是由于
# 替換了__dict__,所以就沒(méi)有了,因?yàn)開(kāi)_dict__原本就是實(shí)例變量的字典形式,你替換了自然也就找不到原來(lái)的了。
# print(person.date_of_brith)
# 下面我們通過(guò)正常的方式實(shí)例化一個(gè)對(duì)象
person2 = Person()
print(person2.__dict__)
print(person2.date_of_brith)
except Exception as err:
print(err)
if __name__ == "__main__":
try:
main()
finally:
sys.exit()
object_hook的含義是,默認(rèn)json.loads()返回的是dict,你可以使用object_hook來(lái)讓其返回其他類(lèi)型的值,它這里實(shí)現(xiàn)的原理就是把你傳遞進(jìn)來(lái)的JSON字符串傳遞給了object_hook指定的方法或者類(lèi)(如果是類(lèi)的話(huà)則會(huì)執(zhí)行__init__方法,其實(shí)就是實(shí)例化),這時(shí)候在類(lèi)的__init方法中我們通過(guò)賦值給self.dict__,其實(shí)這就等于對(duì)Person類(lèi)的實(shí)例變量做了替換,除非你的JSON字符串的鍵和實(shí)例變量的名稱(chēng)以及數(shù)量一致否則你無(wú)法通過(guò)你在類(lèi)里定義的實(shí)例變量名稱(chēng)獲取通過(guò)JSON字符串傳遞進(jìn)去的值。如下圖:

所以通過(guò)上面可以看出來(lái),這個(gè)過(guò)程不是為實(shí)例變量賦值的過(guò)程而是一個(gè)替換的過(guò)程,Python是動(dòng)態(tài)語(yǔ)言這一點(diǎn)和JAVA不同。如果你在程序中用單下劃線(xiàn)標(biāo)識(shí)變量為私有(只是規(guī)范而不是真正的私有)那么你傳遞的JSON字符串的鍵也需要有下劃線(xiàn),這樣你通過(guò)實(shí)例的方法才能獲取。既然額外增加下劃線(xiàn)不太現(xiàn)實(shí),那么有沒(méi)有其他辦法呢?看方式2
方式2:通過(guò)反射機(jī)制來(lái)實(shí)現(xiàn)
先看一下類(lèi)的定義
#!/usr/bin/env python # -*- coding: utf-8 -*- class Person: def __init__(self): self._name = "1" self._sex = "" self._blood_type = "O" self._hobbies = [] self._date_of_birth = "1900/1/1" def __str__(self): """ 輸出實(shí)例的類(lèi)名字,而不是一個(gè)地址 :return: 該實(shí)例的類(lèi)名字 """ return self.__class__.__name__ # 當(dāng)一個(gè)方法加上這個(gè)裝飾器之后,hasattr()中的屬性要寫(xiě)成這個(gè)方法的名稱(chēng),而不是實(shí)例變量的名稱(chēng)。 # 如果不加這個(gè)裝飾器,那么hasattr()中的屬性名稱(chēng)要和實(shí)例變量的名稱(chēng)保持一致 @property def Name(self): return self._name @Name.setter def Name(self, name): self._name = name @property def Sex(self): return self._sex @Sex.setter def Sex(self, sex): self._sex = sex @property def BloodType(self): return self._blood_type @BloodType.setter def BloodType(self, blood_type): self._blood_type = blood_type @property def Hobbies(self): return self._hobbies @Hobbies.setter def Hobbies(self, hobbies): self._hobbies = hobbies @property def date_of_brith(self): return self._date_of_birth @date_of_brith.setter def date_of_brith(self, date_of_brith): self._date_of_birth = date_of_brith
下面看看轉(zhuǎn)換的方法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import json
import importlib
def get_instance(str_stream, class_full_path=None):
"""
:param str_stream: json的字符串形式 '{"Name": "Tom", "Sex": "Male", "BloodType": "A"}'
:param class_full_path: package.module.class
:return:
"""
try:
json_obj = json.loads(str_stream)
except Exception as err:
print("輸入的字符串不符合JSON格式,請(qǐng)檢查。")
return None
if class_full_path is None:
return json_obj
else:
try:
# 獲取模塊路徑
module_path = ".".join(class_full_path.split(".")[0:-1])
# 獲取類(lèi)名稱(chēng)
class_name = class_full_path.split(".")[-1]
# 通過(guò)模塊名加載模塊
CC = importlib.import_module(module_path)
# 判斷是否有class_name所代表的屬性
if hasattr(CC, class_name):
# 獲取模塊中屬性
temp_obj = getattr(CC, class_name)
# 實(shí)例化對(duì)象
obj = temp_obj()
for key in json_obj.keys():
obj.__setattr__(key, json_obj[key])
return obj
else:
pass
except Exception as err:
print(err)
return None
def main():
try:
str1 = '{"Name": "Tom", "Sex": "Male", "BloodType": "A", "Hobbies": ["籃球", "足球"]}'
person1 = get_instance(str1, class_full_path="AAA.Classes.Person")
# 查看類(lèi)型
print(type(person1))
# 查看屬性
print(person1.__dict__)
# 查看指定屬性
print(person1.Name)
except Exception as err:
print(err)
if __name__ == "__main__":
try:
main()
finally:
sys.exit()
__import__() 有2個(gè)參數(shù),第一個(gè)是類(lèi),第二個(gè)是fromlist,如果不寫(xiě)fromlist,則按照下面的寫(xiě)法會(huì)只導(dǎo)入AAA包,如果fromlist有值則會(huì)導(dǎo)入AAA下面的Classes模塊cc = __import__("AAA.Classes", fromlist=True)不寫(xiě)fromlist 相當(dāng)于 import AAA ,如果寫(xiě)了就相當(dāng)于是from AAA import Classes編程時(shí)如果使用動(dòng)態(tài)加載建議使用importlib.import_module(),而不是__import__()。
下面看一下效果:

可以看到,這樣操作之后就是給實(shí)例變量賦值而不是像之前那樣的替換,而且保留了類(lèi)中實(shí)例變量的私有規(guī)范。不過(guò)需要說(shuō)明的是JSON字符串中的鍵名稱(chēng)要和類(lèi)里面定義的屬性名稱(chēng)一樣,也就是鍵名稱(chēng)要和類(lèi)中@property裝飾的方法同名。我們也可以看到這種使用方式也有默認(rèn)JSONObject.parseObject的意思。
不過(guò)這只是一個(gè)簡(jiǎn)單的實(shí)現(xiàn),只能通過(guò)單一JSON字符串生成對(duì)象不能生成對(duì)象列表。當(dāng)然有興趣的朋友可以自己根據(jù)這個(gè)思路進(jìn)行擴(kuò)展。
另外既然無(wú)論是loads還是我自己的方法搜需要保證JSON字符串的鍵和變量名稱(chēng)一致大家就不要糾結(jié)于名稱(chēng)一致的問(wèn)題,但是我的方法做到了保持實(shí)例變量命名、操作實(shí)例屬性時(shí)候的規(guī)范,同時(shí)對(duì)類(lèi)也沒(méi)有過(guò)多的入侵性而不像loads方法中還需要在類(lèi)的init方法里面增加不必要的內(nèi)容。在我這個(gè)方法中如果能實(shí)現(xiàn)忽略大小寫(xiě)通用性就更好了。歡迎大家來(lái)提供思路。
總結(jié)
以上所述是小編給大家介紹的把JSON數(shù)據(jù)格式轉(zhuǎn)換為Python的類(lèi)對(duì)象方法詳解,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Python字符串str和json格式相互轉(zhuǎn)換
- 利用Python實(shí)現(xiàn)Shp格式向GeoJSON的轉(zhuǎn)換方法
- python3 json數(shù)據(jù)格式的轉(zhuǎn)換(dumps/loads的使用、dict to str/str to dict、json字符串/字典的相互轉(zhuǎn)換)
- Python中xml和json格式相互轉(zhuǎn)換操作示例
- Python基于pandas實(shí)現(xiàn)json格式轉(zhuǎn)換成dataframe的方法
- 利用python將json數(shù)據(jù)轉(zhuǎn)換為csv格式的方法
- Python如何將LabelMe生成的JSON格式轉(zhuǎn)換成YOLOv8支持的TXT格式
相關(guān)文章
win10下python2和python3共存問(wèn)題解決方法
在本篇文章里小編給大家整理了關(guān)于win10下python2和python3共存問(wèn)題解決方法,有興趣的朋友們參考下。2019-12-12
python使用socket連接遠(yuǎn)程服務(wù)器的方法
這篇文章主要介紹了python使用socket連接遠(yuǎn)程服務(wù)器的方法,涉及Python中socket通信的基本技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
python利用datetime模塊計(jì)算時(shí)間差
python中通過(guò)datetime模塊可以很方便的計(jì)算兩個(gè)時(shí)間的差,datetime的時(shí)間差單位可以是天、小時(shí)、秒,甚至是微秒,下面我們就來(lái)詳細(xì)看下datetime的強(qiáng)大功能吧2015-08-08
Python?中的反轉(zhuǎn)字符串reversed(),切片
這篇文章主要介紹了Python?中的反轉(zhuǎn)字符串reversed(),切片?,以相反的順序反轉(zhuǎn)和處理字符串可能是編程中的一項(xiàng)常見(jiàn)任務(wù)。Python?提供了一組工具和技術(shù),可以幫助我們快速有效地執(zhí)行字符串反轉(zhuǎn),下面來(lái)看看具體內(nèi)容吧2021-12-12
深入探究python中Pandas庫(kù)處理缺失數(shù)據(jù)和數(shù)據(jù)聚合
在本篇文章中,我們將深入探討Pandas庫(kù)中兩個(gè)重要的數(shù)據(jù)處理功能:處理缺失數(shù)據(jù)和數(shù)據(jù)聚合,文中有詳細(xì)的代碼示例,對(duì)我們的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-07-07
postman模擬訪(fǎng)問(wèn)具有Session的post請(qǐng)求方法
今天小編就為大家分享一篇postman模擬訪(fǎng)問(wèn)具有Session的post請(qǐng)求方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07
Python實(shí)現(xiàn)串口通信(pyserial)過(guò)程解析
這篇文章主要介紹了Python實(shí)現(xiàn)串口通信(pyserial)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
Flask進(jìn)階之構(gòu)建RESTful?API和數(shù)據(jù)庫(kù)交互操作
這篇文章主要為大家介紹了Flask進(jìn)階之構(gòu)建RESTful API和數(shù)據(jù)庫(kù)交互操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
解決Django中checkbox復(fù)選框的傳值問(wèn)題
這篇文章主要介紹了解決Django中checkbox復(fù)選框的傳值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03

