服务器响应中的BOM搞砸了json解析

Mik*_*sen 8 python json urllib urllib2

我正在尝试编写一个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
Run Code Online (Sandbox Code Playgroud)

最后一行抛出异常:

ValueError:无法解码JSON对象

当我看到时response,我看到有效的JSON,但前几个字符是BOM:

>>> response
'\xef\xbb\xbf[\r\n  {\r\n    ... Valid JSON here
Run Code Online (Sandbox Code Playgroud)

所以,如果我手动删除前三个字节:

data = json.loads(response[3::])
Run Code Online (Sandbox Code Playgroud)

一切正常,response变成了字典.

我的问题:

json当你给它一个BOM时,barfs 似乎有点傻.有没有什么不同我可以做urllibjson库让它知道这是一个UTF8字符串并处理它?我不想手动删除前3个字节.

Eev*_*vee 12

您应该对运行此服务的人大喊大叫,因为UTF-8文本上的BOM没有任何意义.BOM存在以消除字节顺序的歧义,UTF-8被定义为little-endian.

也就是说,理想情况下,您应该在对其进行任何其他操作之前解码字节.幸运的是,Python有一个识别和删除BOM的编解码器:utf-8-sig.

>>> '\xef\xbb\xbffoo'.decode('utf-8-sig')
u'foo'
Run Code Online (Sandbox Code Playgroud)

所以你只需要:

data = json.loads(response.decode('utf-8-sig'))
Run Code Online (Sandbox Code Playgroud)

  • 标准特别允许使用UTF-8的BOM,并通过Windows在所有地方使用它来区分UTF-8和OEM字符集.这是愚蠢的,标准建议应用程序不这样做,但它足够普遍,当你看到它时也无法处理它也是愚蠢的.拒绝接受UTF-8 BOM意味着拒绝与.NET服务交互,打开Windows文本文件等.有关详细信息,请参阅[Wikipedia](http://en.wikipedia.org/wiki/Byte_order_mark#UTF-8) . (3认同)
  • @ abarnert,RFC4627(json标准)不允许BOM。 (2认同)

Apr*_*ion 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())
Run Code Online (Sandbox Code Playgroud)