服务器响应中的BOM导致JSON解析错误
我正在尝试写一个Python脚本,目的是向一个网络服务器发送一些JSON数据,并获取一些JSON数据作为回应。我在StackOverflow上拼凑了几个不同的例子,感觉现在的代码大部分是能工作的。
import urllib2
import json
url = "http://foo.com/API.svc/SomeMethod"
payload = json.dumps( {'inputs': ['red', 'blue', 'green']} )
headers = {"Content-type": "application/json;"}
req = urllib2.Request(url, payload, headers)
f = urllib2.urlopen(req)
response = f.read()
f.close()
data = json.loads(response) # <-- Crashes
但是最后一行出现了一个错误:
ValueError: 无法解码JSON对象
当我查看response
时,发现里面有有效的JSON数据,但开头的几个字符是一个BOM(字节顺序标记):
>>> response
'\xef\xbb\xbf[\r\n {\r\n ... Valid JSON here
所以,如果我手动去掉前面三个字节:
data = json.loads(response[3::])
一切就正常了,response
也成功转换成了一个字典。
我的问题:
感觉有点奇怪的是,当你给json
一个BOM时,它就会出错。有没有什么方法可以让urllib
或者json
库知道这是一个UTF8字符串,并且正确处理它?我不想手动去掉前面的三个字节。
3 个回答
0
因为我的声望不够,不能评论,所以我就写个回答。
我通常在需要保持一个StreamWriter
的底层Stream
打开时遇到这个问题。不过,那个可以让底层Stream
保持打开的选项,需要指定一种编码(大多数情况下是UTF8),下面是如何做到不输出BOM的方法。
/* Since Encoding.UTF8 (the one you'd normally use in those cases) **emits**
* the BOM, use whats below instead!
*/
// UTF8Encoding has an overload which enables / disables BOMs in the output
UTF8Encoding encoding = new UTF8Encoding(false);
using (MemoryStream ms = new MemoryStream())
using (StreamWriter sw = new StreamWriter(ms, encoding, 4096, true))
using (JsonTextWriter jtw = new JsonTextWriter(sw))
{
serializer.Serialize(jtw, myObject);
}
5
如果我不是唯一一个遇到这个问题的人,而是使用 requests
模块而不是 urllib2
,那么这里有一个在 Python 2.6 和 3.3 中都能用的解决办法:
import requests
r = requests.get(url, params=my_dict, auth=(user, pass))
print(r.headers['content-type']) # 'application/json; charset=utf8'
if r.text[0] == u'\ufeff': # bytes \xef\xbb\xbf in utf-8 encoding
r.encoding = 'utf-8-sig'
print(r.json())
12
你可能应该去找一下这个服务的负责人,跟他们说一下,因为在UTF-8文本中出现字节顺序标记(BOM)是没有意义的。字节顺序标记是用来区分字节顺序的,而UTF-8本身就是定义为小端格式的。
不过,理想情况下,你应该在对字节进行任何操作之前先解码它们。幸运的是,Python有一个可以识别并去掉字节顺序标记的编码器:utf-8-sig
。
>>> '\xef\xbb\xbffoo'.decode('utf-8-sig')
u'foo'
所以你只需要:
data = json.loads(response.decode('utf-8-sig'))