json.dump上的json.loads上的UnicodeDecodeError,在python 2.x中的ensure_ascii = False

时间:2016-03-09 00:33:36

标签: python json python-2.7 python-unicode

我使用json.dump转储dict对象。为了避免UnicodeDecodeError,我在this advice之后设置ensure_ascii=False

with open(my_file_path, "w") as f:
    f.write(json.dumps(my_dict, ensure_ascii=False))

转储文件已成功创建,但在加载转储文件时会发生UnicodeDecodeError:

with open(my_file_path, "r") as f:
    return json.loads(f.read())

如何在加载转储文件时避免UnicodeDecodeError

错误消息和堆栈跟踪

错误消息为UnicodeDecodeError: 'utf8' codec can't decode byte 0x93 in position 0: invalid start byte,stacktrace为:

/Users/name/.pyenv/versions/anaconda-2.0.1/python.app/Contents/lib/python2.7/json/__init__.pyc in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    336     if (cls is None and encoding is None and object_hook is None and
    337             parse_int is None and parse_float is None and
--> 338             parse_constant is None and object_pairs_hook is None and not kw):
    339         return _default_decoder.decode(s)
    340     if cls is None:

/Users/name/.pyenv/versions/anaconda-2.0.1/python.app/Contents/lib/python2.7/json/decoder.pyc in decode(self, s, _w)
    364         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    365         end = _w(s, end).end()
--> 366         if end != len(s):
    367             raise ValueError(errmsg("Extra data", s, end, len(s)))
    368         return obj

/Users/name/.pyenv/versions/anaconda-2.0.1/python.app/Contents/lib/python2.7/json/decoder.pyc in raw_decode(self, s, idx)
    380             obj, end = self.scan_once(s, idx)
    381         except StopIteration:
--> 382             raise ValueError("No JSON object could be decoded")
    383         return obj, end

UnicodeDecodeError: 'utf8' codec can't decode byte 0x93 in position 0: invalid start byte

1 个答案:

答案 0 :(得分:1)

在Python2中,您可以在调用ensure_ascii=False之前使用json.loads 并解析结果

import json

my_dict = {b'\x93': [b'foo', b'\x93', {b'\x93': b'\x93'}]}

dumped = json.dumps(my_dict, ensure_ascii=False)
print(repr(dumped))
# '{"\\u201c": ["foo", "\\u201c", {"\\u201c": "\\u201c"}]}'
result = json.loads(dumped.decode('cp1252'))
print(result)
# {u'\u201c': [u'foo', u'\u201c', {u'\u201c': u'\u201c'}]}

但是,请注意result返回的json.loads包含unicode,而不是str。因此resultmy_dict不完全相同。

请注意json.loads always decodes strings to unicode,所以如果您有兴趣使用json.dumpsjson.loads忠实地恢复dict,那么您需要从仅包含dict的dict开始unicode,没有str s。

此外,在Python3中json.dumps 要求所有dicts都具有unicode字符串的键。因此上述解决方案在Python3中不起作用。

可以在Python2和Python3中使用的替代方法是确保您 传递json.loads一个密钥和值为unicode的dict(或包含no str S)。例如,如果使用convert(下面)递归更改 将unicode传递给json.loads之前的密钥和值:

import json

def convert(obj, enc):
    if isinstance(obj, str):
        return obj.decode(enc)
    if isinstance(obj, (list, tuple)):
        return [convert(item, enc) for item in obj]
    if isinstance(obj, dict):
        return {convert(key, enc) : convert(val, enc)
                for key, val in obj.items()}
    else: return obj

my_dict = {'\x93': ['foo', '\x93', {'\x93': '\x93'}]}
my_dict = convert(my_dict, 'cp1252')

dumped = json.dumps(my_dict)
print(repr(dumped))
# '{"\\u201c": ["foo", "\\u201c", {"\\u201c": "\\u201c"}]}'
result = json.loads(dumped)
print(result)
# {u'\u201c': [u'foo', u'\u201c', {u'\u201c': u'\u201c'}]}
assert result == my_dict

convert会解码str内的列表,元组和词组中找到的所有my_dict

上面,我使用了'cp1252'作为编码,因为(Fumu pointed out'\x93'解码cp1252LEFT DOUBLE QUOTATION MARK

In [18]: import unicodedata as UDAT

In [19]: UDAT.name('\x93'.decode('cp1252'))
Out[19]: 'LEFT DOUBLE QUOTATION MARK'

如果您知道str中的my_dict已经使用其他编码进行了编码, 您当然应该使用该编码调用convert

更好的是,在构建 convert时,请务必确保所有str被解码为unicode ,而不是使用my_dict。 。