我正在尝试编写一个Python脚本,将一些JSON发布到Web服务器并获取一些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
变成字典。
我的问题:
当你给它一个BOM时json
barfs似乎有点傻。我可以对urllib
或json
库做些什么不同的事情让它知道这是一个UTF8字符串并按此处理它?我不想手动删除前3个字节。
答案 0 :(得分:12)
对于运行此服务的人,您应该大喊大叫,因为UTF-8文本上的BOM没有任何意义。 BOM存在以消除字节顺序的歧义,UTF-8被定义为little-endian。
那就是说,理想情况下你应该在对它们做任何其他事情之前解码字节。幸运的是,Python有一个识别和删除BOM的编解码器:utf-8-sig
。
>>> '\xef\xbb\xbffoo'.decode('utf-8-sig')
u'foo'
所以你只需要:
data = json.loads(response.decode('utf-8-sig'))
答案 1 :(得分: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())
答案 2 :(得分:0)
由于我没有足够的评论声誉,我会写一个答案。
当我需要打开Stream
的基础StreamWriter
时,我经常遇到这个问题。但是,可以选择让基础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);
}