python2.7 json.loads() 将字符串默认解码为
2019-05-16 本文已影响0人
zbharper
python2.7 中 str 与 unicode 的转换一直是个头疼的问题,在使用json模块进行序列化与反序列化时再次踩坑。
1)客户端产生一个字典格式的数据结构,其中带有一段utf-8编码的字符串
import json
raw = u"我爱中国".encode("utf-8")
send_data = {
"id": 111, #不重要
"content": raw # 数据内容为utf-8编码
}
print type(send_data['content']) # str
2)客户端将数据进行json序列化,通过消息队列发送至服务器
send_body = json.dumps(send_data)
# send to rabbitmq
3)服务器端收到后进行解码,交给后端处理
# receive_body get from rabbitmq
receive_data = json.loads(receive_body)
4)后端使用数据,此时数据已经是unicode 而不是utf-8,再次解码会出错!
print type(receive.data['content']) # unicode
从协议分层的角度,(1)与(4)处于同一逻辑层,抛开(2),(3)的json序列化与反序列化过程,发送与接收的数据格式应该相同,但是receive_data 与 send_data中的字符编码不一样,稍不注意使用(如按数据格式协议解码)就会出错。
查看json官方文档,发现在反序列化json.loads()的过程中会自动将所有字符串解码为unicode格式。而序列化过程json.dumps()会默认将非ascii编码的字符转换为unicode,同时可以通过参数ensure_ascii=False选择保持原有编码不变。有点绕,但是坑爹的地方就在于此,做几个实验:
1. 默认参数不变,对两种编码( utf8, unicode)进行序列化
>>> unicode_str = u"abc我爱中国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"'
输入不同,序列化之后的结果相同!
2. 使用json.dumps(obj, ensure_ascii=False), 对两种编码进行序列化
>>> 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"'
序列化后的结果不同!分别保持了原有的编码!
3. 对上述的序列化字符串进行反序列化
>>> 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'
四个序列化字符串反序列化结果全都相同!
结论
- 反序列化后总会得到unicode字符串,无论序列化阶段如何搞
- 序列化阶段通过参数设置,可能生成不同的序列化结果
- 要想少出错,在上层的数据协议里最好将字符串都定义为unicode格式
- 尽快转到python3