Python如何把不同類(lèi)型數(shù)據(jù)的json序列化
現(xiàn)代網(wǎng)絡(luò)應(yīng)用Web APP或大型網(wǎng)站的后臺(tái)一般只有一個(gè),然后客戶(hù)端卻是各種各樣的(iOS, android, 瀏覽器), 而且客戶(hù)端的開(kāi)發(fā)語(yǔ)言很可能與后臺(tái)的開(kāi)發(fā)語(yǔ)言不一樣。這時(shí)我們需要后臺(tái)能夠提供可以跨平臺(tái)跨語(yǔ)言的一種標(biāo)準(zhǔn)的數(shù)據(jù)交換格式供前后端溝通(這就是Web API的作用)。如今大家最常用的跨平臺(tái)跨語(yǔ)言數(shù)據(jù)交換格式就是JSON(JavaScript Object Notation)了。JSON是一種文本序列化格式(它輸出的是unicode文件,大多數(shù)時(shí)候會(huì)被編碼為utf-8),人們很容易進(jìn)行閱讀和編寫(xiě)。python自帶的dumps方法很有用,能很容易將字典dict類(lèi)型數(shù)據(jù)轉(zhuǎn)化為json格式,然后還有很多類(lèi)型的數(shù)據(jù)(如日期,集合, 自定義的類(lèi)和Django的QuerySet類(lèi)型),我們需要自定義序列化方法才能將它們轉(zhuǎn)化為json格式。今天小編我就來(lái)對(duì)python的json模塊做下總結(jié),并詳細(xì)介紹如何把不同類(lèi)型的數(shù)據(jù)json序列化。
何謂序列化(serialization)每種編程語(yǔ)言都有各自的數(shù)據(jù)類(lèi)型, 將屬于自己語(yǔ)言的數(shù)據(jù)類(lèi)型或?qū)ο筠D(zhuǎn)換為可通過(guò)網(wǎng)絡(luò)傳輸或可以存儲(chǔ)到本地磁盤(pán)的數(shù)據(jù)格式(如:XML、JSON或特定格式的字節(jié)串)的過(guò)程稱(chēng)為序列化(seralization);反之則稱(chēng)為反序列化。
Python的JSON模塊
python自帶的json庫(kù)(無(wú)需額外安裝), 主要包含了dumps, loads, dump和load四種方法其作用分別如下所示。
json.loads() - 將json字符串轉(zhuǎn)換為python數(shù)據(jù)類(lèi)型 json.dumps() - 將python數(shù)據(jù)類(lèi)型轉(zhuǎn)化為json字符串 json.dump() - 將python輸入轉(zhuǎn)化為json格式存入磁盤(pán)文件 json.load() - 將磁盤(pán)文件中json格式數(shù)據(jù)轉(zhuǎn)換為python數(shù)據(jù)類(lèi)型python數(shù)據(jù)格式與json數(shù)據(jù)格式對(duì)應(yīng)轉(zhuǎn)換關(guān)系如下:
Python JSON dict Object list, tuple array str string int, float, numbers True true False false None null
你注意到了嗎? 還有很多python數(shù)據(jù)類(lèi)型(set, datetime)不在上表中哦。
json的模塊dumps方法介紹 - python數(shù)據(jù)的序列化json模塊的dumps方法可以將python常用數(shù)據(jù)格式轉(zhuǎn)化為json格式。該方法還提供了很多可選參數(shù)如ident, separators, ensure_ascii, sort_keys和default參數(shù)。這些參數(shù)都非常有用,我們會(huì)稍后逐一介紹。
dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
我們先來(lái)看看一個(gè)最簡(jiǎn)單的例子。你注意到了嗎? 生成的json格式數(shù)據(jù)外面都加了單引號(hào),這說(shuō)明dict類(lèi)型數(shù)據(jù)已經(jīng)轉(zhuǎn)化成了json字符串。
>>> import json>>> json.dumps({'name':'John', 'score': 112})’{'name': 'John', 'score': 112}’
如果一個(gè)dict很長(zhǎng),生成的json字符串會(huì)非常長(zhǎng),這時(shí)我們可以設(shè)置indent參數(shù)使生成的json格式數(shù)據(jù)更優(yōu)美,更容易人們閱讀。代碼如下所示:
>>> import json>>> json.dumps({'name':'John', 'score': 112}, indent=4)’{n 'name': 'John',n 'score': 112n}’>>> print(json.dumps({'name':'John', 'score': 112}, indent=4)){ 'name': 'John', 'score': 112}
然而使用indent參數(shù)的代價(jià)是json字符串里增加了額外的空白,機(jī)器閱讀根本不需要它們,
即使不用indent參數(shù),你會(huì)發(fā)現(xiàn)dumps生成的json字符串中的’,’號(hào)和’:’號(hào)分隔符后都會(huì)附加一個(gè)默認(rèn)空白字符,我們可以通過(guò)separators參數(shù)重新指定分隔符,從而去除無(wú)用的空白字符,如下所示。這樣可以減少無(wú)用數(shù)據(jù)的的傳輸,節(jié)省帶寬增加數(shù)據(jù)傳輸速度。
>>> import json>>> json.dumps({'name':'John', 'score': 112})’{'name': 'John', 'score': 112}’# 使用separators選項(xiàng)>>> json.dumps({'name':'John', 'score': 112}, separators=(’,’,’:’))’{'name':'John','score':112}’
如果字符串有非ASCII字符(比如中文),它們?cè)趈son序列化時(shí)都會(huì)被轉(zhuǎn)義成’uXXXX’組成的ascii字符串。如果想得到更加易讀的字符串,可以設(shè)置ensure_ascii=False。
>>> import json>>> json.dumps({'Name':'小明', 'Age': 16})’{'Name': 'u5c0fu660e', 'Age': 16}’# 設(shè)置ensure_ascii=False>>> json.dumps({'Name':'小明', 'Age': 16}, ensure_ascii=False)’{'Name': '小明', 'Age': 16}’
一般的dict默認(rèn)是無(wú)序的,你還可以設(shè)置sort_keys = True對(duì)生成的json格式數(shù)據(jù)進(jìn)行排序,這里就不演示了。default參數(shù)我們后面會(huì)重點(diǎn)介紹。
json模塊的dump,loads和load方法介紹與dumps方法不同,json模塊的dump方法用于將生成的json數(shù)據(jù)寫(xiě)入磁盤(pán)文件。其用法和dumps類(lèi)似,唯一不同的是需要指定需要寫(xiě)入的文件,具體用法如下所示:
import jsonwith open('json.txt', ’w’) as f: json.dump({'Name':'小明', 'Age': 16}, f, ensure_ascii=True)
json的loads方法用于將json格式數(shù)據(jù)轉(zhuǎn)化為python格式,實(shí)現(xiàn)數(shù)據(jù)的反序列化,如下所示。千萬(wàn)別忘了在json符串外的單引號(hào)哦。
>>> import json>>> json.loads(’{'Name': '小明', 'Age': 16}’){’Name’: ’小明’, ’Age’: 16}
json的load方法與loads用法相似,不過(guò)它需要指定存有json數(shù)據(jù)的文件。
>>> import json>>> with open('json.txt', ’r’) as f: json.load(f)很多python格式數(shù)據(jù)不能直接被dumps方法序列化
很多python數(shù)據(jù)類(lèi)型(比如日期,集合和自定義的類(lèi))并不能直接被dumps方法序列化,這時(shí)會(huì)出現(xiàn) xxx is not JSON serializable的錯(cuò)誤,如下面代碼所示。當(dāng)出現(xiàn)類(lèi)似錯(cuò)誤時(shí),我們有兩種解決方案。
通過(guò)數(shù)據(jù)類(lèi)型轉(zhuǎn)換函數(shù)實(shí)現(xiàn) 通過(guò)繼承JSONEncoder和JSONDecoder類(lèi)實(shí)現(xiàn)>>> import json>>> from datetime import datetime# DateTime類(lèi)型>>> json.dumps({'date':datetime.now()})Traceback (most recent call last):TypeError: Object of type ’datetime’ is not JSON serializable# 自定義的User類(lèi)>>> class User(object):def __init__(self, name): self.name = name>>> json.dumps(User('John'))Traceback (most recent call last):TypeError: Object of type ’User’ is not JSON serializable解決方案一: 編寫(xiě)數(shù)據(jù)類(lèi)型轉(zhuǎn)換函數(shù)
該方法的工作原理是先編寫(xiě)數(shù)據(jù)類(lèi)型轉(zhuǎn)化函數(shù),通過(guò)設(shè)置dumps方法里的default參數(shù)調(diào)用格式轉(zhuǎn)化函數(shù),將dumps方法不支持的數(shù)類(lèi)型先轉(zhuǎn)化為字符串格式,再實(shí)現(xiàn)json序列化。
# 將datetime格式數(shù)據(jù)json化>> > import json>> > from datetime import datetime>> > def date_to_str(obj):if isinstance(obj, datetime): return obj.strftime(’%Y-%m-%d %H:%M:%S’)elif isinstance(obj, date): return obj.strftime(’%Y-%m-%d’)return TypeError>> > json.dumps({'date': datetime.now()}, default=date_to_str)’{'date': '2018-09-22 21:25:42'}’# 將set類(lèi)型數(shù)據(jù)json化>> > import json>> > set_data = {’my_set’: {1, 2, 3}}>> > def set_to_list(obj):if isinstance(obj, set): return list(obj)raise TypeError>> > result = json.dumps(set_data, default=set_to_list)
對(duì)于我們自定義的類(lèi), 使用dumps方法時(shí)我們一般要先編寫(xiě)obj_to_dict方法,將object轉(zhuǎn)化為字典dict再JSON序列化。同理,使用loads方法對(duì)json數(shù)據(jù)反序列化時(shí),我們還需要編寫(xiě)dict_to_obj方法,通過(guò)default參數(shù)調(diào)用。下面這2段代碼是比較通用的對(duì)象(object)與字典(dict)互轉(zhuǎn)的經(jīng)典代碼,請(qǐng)用微信收藏后再看。
# 將自定義的類(lèi)轉(zhuǎn)化為字典,dumps方法使用def obj_to_dict(obj): d = {} d[’__class__’] = obj.__class__.__name__ d[’__module__’] = obj.__module__ d.update(obj.__dict__) return d# 將字典轉(zhuǎn)化為自定義的類(lèi),loads方法使用def dict_to_obj(d): if ’__class__’ in d:class_name = d.pop(’__class__’)module_name = d.pop(’__module__’)module = __import__(module_name)class_ = getattr(module, class_name)args = dict((key.encode(’ascii’), value) for key, value in d.items())instance = class_(**args) else:instance = d return instance解決方案二: 繼承JSONEncoder類(lèi)和JSONDecode類(lèi)
另一個(gè)解決方案是繼承JSONEncoder類(lèi)和JSONDecode類(lèi)定義自己的編碼Encoder類(lèi),然后使用cls=MyEncoder,來(lái)調(diào)用編碼器。比如下例中我們定義了自己的DateTimeEncoder,將日期類(lèi)型數(shù)據(jù)序列化。
from datetime import datetime, dateimport jsonclass DateTimeEncoder(json.JSONEncoder): def default(self, obj):if isinstance(obj, datetime): return obj.strftime(’%Y-%m-%d %H:%M:%S’)elif isinstance(obj, date): return obj.strftime(’%Y-%m-%d’)return json.JSONEncoder.default(self, obj)json_data = {’num’: 1, ’date’: datetime.now()}print(json.dumps(json_data, cls=DateTimeEncoder))
對(duì)于自定義的對(duì)象,我們也可以通過(guò)繼承JSONEncoder類(lèi)實(shí)現(xiàn)它的序列化,如下所示:
import jsonclass User(object): def __init__(self, name):self.name = nameclass MyJSONEncoder(json.JSONEncoder): def default(self, obj):d = {}d[’__class__’] = obj.__class__.__name__d[’__module__’] = obj.__module__d.update(obj.__dict__)return duser = User('John')json.dumps(user, cls=MyJSONEncoder))
對(duì)于簡(jiǎn)單的數(shù)據(jù)序列化,方案一更容易理解,代碼也更少。但當(dāng)需要傳輸?shù)臄?shù)據(jù)很大時(shí),使用繼承JSONEncoder類(lèi)來(lái)實(shí)現(xiàn)序列化時(shí)有個(gè)很大的好處,就是可以通過(guò)iterencode()方法把一個(gè)很大的數(shù)據(jù)對(duì)象分多次進(jìn)行序列化,這對(duì)于網(wǎng)絡(luò)持續(xù)傳輸或?qū)懭氪蟮奈募浅S杏谩H缦滤尽?/p>
>>> for chunk in MyJSONEncoder().iterencode(big_object):... print(chunk)Django特有數(shù)據(jù)類(lèi)型序列化
Django編程就是是python編程,以上所介紹的序列化方法對(duì)django也是適用的。不同的是Django還有自己專(zhuān)屬的數(shù)據(jù)類(lèi)型比如QuerySet和ValueQuerySet類(lèi)型數(shù)據(jù),還提供了更便捷的serializers類(lèi)。使用serializers類(lèi)可以輕易將QuerySet格式的數(shù)據(jù)轉(zhuǎn)化為json格式。
# Django Queryset數(shù)據(jù) to Jsonfrom django.core import serializersdata = serializers.serialize('json', SomeModel.objects.all())data1 = serializers.serialize('json', SomeModel.objects.all(), fields=(’name’,’id’))data2 = serializers.serialize('json', SomeModel.objects.filter(field = some_value))
有時(shí)候我們只需要查詢(xún)結(jié)果集的部分字段,可以使用values(’字段名’,’字段名2’)來(lái)要求返回的是哪些列的數(shù)據(jù).但是返回來(lái)的是ValuesQuerySet對(duì)象而不是QuerySet對(duì)象。ValuesQuerySet對(duì)象不能用 serializers.serialize() 方法序列化成json, 需要先轉(zhuǎn)換成list再用 json.dumps()方法序列化成json格式。示例代碼如下所示:
import jsonfrom django.core.serializers.json import DjangoJSONEncoderqueryset = myModel.objects.filter(foo_icontains=bar).values(’f1’, ’f2’, ’f3’)data4 = json.dumps(list(queryset), cls=DjangoJSONEncoder)django-rest-framework
如果你要利用django開(kāi)發(fā)restful的web API, 為不同客戶(hù)端提供序列化過(guò)的json格式數(shù)據(jù),django-rest-framework才是你真正需要的序列化工具。與django自帶的serializers類(lèi)相比,rest framework支持用戶(hù)驗(yàn)證,查詢(xún)過(guò)濾和符合restful規(guī)范的url設(shè)計(jì),我們后面會(huì)專(zhuān)門(mén)介紹。歡迎關(guān)注我的微信。
小結(jié)我們介紹了何為JSON序列化以及python json模塊的dumps, loads, dump和load方法。我們還介紹了如何將dumps方法不支持的數(shù)據(jù)格式(如日期,集合, 自定義的類(lèi)和Django的QuerySet類(lèi)型)如何通過(guò)要自定義格式轉(zhuǎn)化函數(shù)和繼承JsonEncoder類(lèi)將它們轉(zhuǎn)化為json格式。希望本文對(duì)你有所幫助。喜歡的給個(gè)贊吧!
以上就是Python如何把不同類(lèi)型數(shù)據(jù)的json序列化的詳細(xì)內(nèi)容,更多關(guān)于python 數(shù)據(jù)json序列化的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. 開(kāi)發(fā)效率翻倍的Web API使用技巧2. bootstrap select2 動(dòng)態(tài)從后臺(tái)Ajax動(dòng)態(tài)獲取數(shù)據(jù)的代碼3. vue使用moment如何將時(shí)間戳轉(zhuǎn)為標(biāo)準(zhǔn)日期時(shí)間格式4. 什么是Python變量作用域5. ASP常用日期格式化函數(shù) FormatDate()6. CSS3中Transition屬性詳解以及示例分享7. js select支持手動(dòng)輸入功能實(shí)現(xiàn)代碼8. Java xml數(shù)據(jù)格式返回實(shí)現(xiàn)操作9. Python數(shù)據(jù)相關(guān)系數(shù)矩陣和熱力圖輕松實(shí)現(xiàn)教程10. python 如何在 Matplotlib 中繪制垂直線
