python2.7 中 str 與 unicode 的轉(zhuǎn)換一直是個(gè)頭疼的問(wèn)題,在使用json模塊進(jìn)行序列化與反序列化時(shí)再次踩坑。
1)客戶端產(chǎn)生一個(gè)字典格式的數(shù)據(jù)結(jié)構(gòu),其中帶有一段utf-8編碼的字符串
import json
raw = u"我愛(ài)中國(guó)".encode("utf-8")
send_data = {
"id": 111, #不重要
"content": raw # 數(shù)據(jù)內(nèi)容為utf-8編碼
}
print type(send_data['content']) # str
2)客戶端將數(shù)據(jù)進(jìn)行json序列化,通過(guò)消息隊(duì)列發(fā)送至服務(wù)器
send_body = json.dumps(send_data)
# send to rabbitmq
3)服務(wù)器端收到后進(jìn)行解碼,交給后端處理
# receive_body get from rabbitmq
receive_data = json.loads(receive_body)
4)后端使用數(shù)據(jù),此時(shí)數(shù)據(jù)已經(jīng)是unicode 而不是utf-8,再次解碼會(huì)出錯(cuò)!
print type(receive.data['content']) # unicode
從協(xié)議分層的角度,(1)與(4)處于同一邏輯層,拋開(kāi)(2),(3)的json序列化與反序列化過(guò)程,發(fā)送與接收的數(shù)據(jù)格式應(yīng)該相同,但是receive_data 與 send_data中的字符編碼不一樣,稍不注意使用(如按數(shù)據(jù)格式協(xié)議解碼)就會(huì)出錯(cuò)。
查看json官方文檔,發(fā)現(xiàn)在反序列化json.loads()的過(guò)程中會(huì)自動(dòng)將所有字符串解碼為unicode格式。而序列化過(guò)程json.dumps()會(huì)默認(rèn)將非ascii編碼的字符轉(zhuǎn)換為unicode,同時(shí)可以通過(guò)參數(shù)ensure_ascii=False選擇保持原有編碼不變。有點(diǎn)繞,但是坑爹的地方就在于此,做幾個(gè)實(shí)驗(yàn):
1. 默認(rèn)參數(shù)不變,對(duì)兩種編碼( utf8, unicode)進(jìn)行序列化
>>> unicode_str = u"abc我愛(ài)中國(guó)def" # u'abc\u6211\u7231\u4e2d\u56fddef'
>>> utf8_str = unicode_str.encode('utf-8') # 'abc\xe6\x88\x91\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbddef'
>>> ser1 = json.dumps(unicode_str)
>>> ser2 = json.dumps(utf8_str)
>>> ser1
'"abc\\u6211\\u7231\\u4e2d\\u56fddef"'
>>> ser2
'"abc\\u6211\\u7231\\u4e2d\\u56fddef"'
輸入不同,序列化之后的結(jié)果相同!
2. 使用json.dumps(obj, ensure_ascii=False), 對(duì)兩種編碼進(jìn)行序列化
>>> ser3 = json.dumps(unicode_str, ensure_ascii=False)
>>> ser4 = json.dumps(utf8_str, ensure_ascii=False)
>>> ser3
u'"abc\u6211\u7231\u4e2d\u56fddef"'
>>> ser4
'"abc\xe6\x88\x91\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbddef"'
序列化后的結(jié)果不同!分別保持了原有的編碼!
3. 對(duì)上述的序列化字符串進(jìn)行反序列化
>>> new1 = json.loads(ser1)
>>> new2 = json.loads(ser2)
>>> new3 = json.loads(ser3)
>>> new4 = json.loads(ser4)
>>> new1
u'abc\u6211\u7231\u4e2d\u56fddef'
>>> new2
u'abc\u6211\u7231\u4e2d\u56fddef'
>>> new3
u'abc\u6211\u7231\u4e2d\u56fddef'
>>> new4
u'abc\u6211\u7231\u4e2d\u56fddef'
四個(gè)序列化字符串反序列化結(jié)果全都相同!
結(jié)論
- 反序列化后總會(huì)得到unicode字符串,無(wú)論序列化階段如何搞
- 序列化階段通過(guò)參數(shù)設(shè)置,可能生成不同的序列化結(jié)果
- 要想少出錯(cuò),在上層的數(shù)據(jù)協(xié)議里最好將字符串都定義為unicode格式
- 盡快轉(zhuǎn)到python3