如何从JSON获得字符串对象而不是Unicode

我使用Python 2来解析来自ASCII编码文本文件的JSON。

当用jsonsimplejson加载这些文件时,我的所有字符串值都转换为Unicode对象,而不是字符串对象。问题是,我必须将数据与一些只接受字符串对象的库一起使用。我不能更改库也没有更新它们。

是否有可能获得字符串对象而不是Unicode对象?

例子

>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b']  # I want these to be of type `str`, not `unicode`

(2017年一个简单而干净的解决方案是使用最新版本的Python——即Python 3和forward。)

381529 次浏览

这是因为json ()在字符串对象和Unicode对象之间没有区别。它们都是JavaScript中的字符串。

我认为JSON返回Unicode对象是正确的。事实上,我不会接受任何更少的东西,因为JavaScript字符串实际上是unicode对象(即JSON (JavaScript)字符串可以存储Unicode字符的任何),所以创建unicode对象时从JSON转换字符串是有意义的。普通字符串不适合,因为库必须猜测您想要的编码。

最好在任何地方都使用unicode字符串对象。因此,最好的选择是更新库,使它们能够处理Unicode对象。

但如果你真的想要字节串,只需将结果编码为你选择的编码:

>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']

恐怕在simplejson库中没有任何方法自动实现这一点。

simplejson中的扫描器和解码器被设计用来生成Unicode文本。为此,标准库使用一个名为c_scanstring的函数(如果可用,为了速度),如果C版本不可用,则使用py_scanstring函数。simplejson中用于解码可能包含文本的结构的几乎每个例程都会多次调用scanstring函数。你必须要么猴子补丁 simplejson.decoder中的scanstring值,要么JSONDecoder的子类,并提供几乎你自己的任何可能包含文本的全部实现。

然而,simplejson输出Unicode的原因是JSON规范特别提到“一个字符串是0个或多个Unicode字符的集合”…对Unicode的支持被假定为格式本身的一部分。simplejsonscanstring实现甚至可以扫描和解释Inicode转义(甚至可以错误检查格式不正确的多字节字符集表示),所以它能够可靠地将值返回给你的唯一方法是Unicode。

如果你有一个老旧的库,需要str,我建议你要么在解析后费力地搜索嵌套的数据结构(我承认这是你明确表示希望避免的…对不起),或者可能将你的库包装在某种外观中,在那里你可以在更细粒度的级别上处理输入参数。如果数据结构确实嵌套很深,第二种方法可能比第一种方法更易于管理。

我构建了这个递归施法者。它符合我的需要,我认为它是相对完整的。

def _parseJSON(self, obj):
newobj = {}


for key, value in obj.iteritems():
key = str(key)


if isinstance(value, dict):
newobj[key] = self._parseJSON(value)
elif isinstance(value, list):
if key not in newobj:
newobj[key] = []
for i in value:
newobj[key].append(self._parseJSON(i))
elif isinstance(value, unicode):
val = str(value)
if val.isdigit():
val = int(val)
else:
try:
val = float(val)
except ValueError:
val = str(val)
newobj[key] = val


return newobj

只需要像这样传递一个JSON对象:

obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)

我把它作为一个类的私有成员,但您可以根据需要重新使用该方法。

我也遇到了同样的问题。

因为我需要将所有数据传递给PyGTK, Unicode字符串对我来说也不是很有用。这是另一种递归转换方法。实际上,类型安全的JSON转换也需要它——JSON .dump()会放弃任何非字面量,比如Python对象。但是它不转换字典索引。

# removes any objects, turns Unicode back into str
def filter_data(obj):
if type(obj) in (int, float, str, bool):
return obj
elif type(obj) == unicode:
return str(obj)
elif type(obj) in (list, tuple, set):
obj = list(obj)
for i,v in enumerate(obj):
obj[i] = filter_data(v)
elif type(obj) == dict:
for i,v in obj.iteritems():
obj[i] = filter_data(v)
else:
print "invalid object in data, converting to string"
obj = str(obj)
return obj

问题在于simplejsonjson是两个不同的模块,至少在处理Unicode的方式上是这样。你在Python 2.6+中有json,这给你Unicode值,而simplejson返回字符串对象。

在您的环境中尝试easy_installation simplejson,看看是否有效。对我来说确实如此。

我也遇到了这个问题,不得不处理JSON,我想出了一个小循环,将Unicode键转换为字符串。(GAE上的simplejson不返回字符串键。)

obj是从JSON解码的对象:

if NAME_CLASS_MAP.has_key(cls):
kwargs = {}
for i in obj.keys():
kwargs[str(i)] = obj[i]
o = NAME_CLASS_MAP[cls](**kwargs)
o.save()

kwargs是我传递给GAE应用程序的构造函数(它不喜欢**kwargs中的Unicode键)。

它不如威尔斯的解决方案健壮,但要小得多。

可以使用json.loadsobject_hook参数来传入转换器。你不需要在事后进行转换。json模块将始终只传递object_hook字典,并且它将递归地传递嵌套字典,因此您不必自己递归到嵌套字典。我不认为我会将Unicode字符串转换为井显示这样的数字。如果它是Unicode字符串,它在JSON文件中被引用为字符串,所以它应该是字符串(或者文件是坏的)。

另外,我会尽量避免在unicode对象上执行类似str(val)的操作。你应该使用带有有效编码的value.encode(encoding),这取决于你的外部库期望什么。

举个例子:

def _decode_list(data):
rv = []
for item in data:
if isinstance(item, unicode):
item = item.encode('utf-8')
elif isinstance(item, list):
item = _decode_list(item)
elif isinstance(item, dict):
item = _decode_dict(item)
rv.append(item)
return rv


def _decode_dict(data):
rv = {}
for key, value in data.iteritems():
if isinstance(key, unicode):
key = key.encode('utf-8')
if isinstance(value, unicode):
value = value.encode('utf-8')
elif isinstance(value, list):
value = _decode_list(value)
elif isinstance(value, dict):
value = _decode_dict(value)
rv[key] = value
return rv


obj = json.loads(s, object_hook=_decode_dict)

没有内置选项使json模块函数返回字节字符串而不是Unicode字符串。然而,这个简短而简单的递归函数将任何解码的JSON对象从使用Unicode字符串转换为utf -8编码的字节字符串:

def byteify(input):
if isinstance(input, dict):
return {byteify(key): byteify(value)
for key, value in input.iteritems()}
elif isinstance(input, list):
return [byteify(element) for element in input]
elif isinstance(input, unicode):
return input.encode('utf-8')
else:
return input

只要在你从json.loadjson.loads调用中得到的输出上调用这个。

几点注意事项:

  • 要支持Python 2.6或更早版本,请将return {byteify(key): byteify(value) for key, value in input.iteritems()}替换为return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]),因为直到Python 2.7才支持字典推导式。
  • 由于这个答案递归遍历整个解码后的对象,它有几个不受欢迎的性能特征,可以通过非常小心地使用object_hookobject_pairs_hook参数来避免。Mirec Miskuf的回答是迄今为止唯一一个设法正确地实现这一点的方法,尽管因此,它比我的方法要复杂得多。

虽然这里有一些很好的答案,但我最终使用PyYAML来解析我的JSON文件,因为它将键和值作为str类型的字符串而不是unicode类型。因为JSON是YAML的子集,所以它工作得很好:

>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']

笔记

但有一些事情需要注意:

  • 我得到字符串对象,因为我所有的条目都是ASCII编码。如果我使用Unicode编码的条目,我将把它们返回为unicode对象 -没有转换!

  • 你应该(可能总是)使用PyYAML的safe_load函数;如果你用它来加载JSON文件,你不需要“额外的功能”;load函数。

  • 如果你想要一个YAML解析器,对规范的1.2版本(和正确解析非常小的数字)有更多的支持,尝试Ruamel YAML: pip install ruamel.yamlimport ruamel.yaml as yaml是我在测试中所需要的。

转换

如上所述,没有任何转换!如果你不能确定只处理ASCII值(而且大多数时候你不能确定),最好使用转换函数:

我现在使用一个——Mark Amery几次了,它工作得很好,非常容易使用。你也可以使用类似的函数作为object_hook,因为它可能会让你在大文件上获得性能提升。请参阅稍微复杂一些的Mirec Miskuf的回答

我重写了井的 _parse_json ()来处理json对象本身是数组的情况(我的用例)。

def _parseJSON(self, obj):
if isinstance(obj, dict):
newobj = {}
for key, value in obj.iteritems():
key = str(key)
newobj[key] = self._parseJSON(value)
elif isinstance(obj, list):
newobj = []
for value in obj:
newobj.append(self._parseJSON(value))
elif isinstance(obj, unicode):
newobj = str(obj)
else:
newobj = obj
return newobj

有一个简单的变通办法。

使用ast.literal_eval()代替json.loads()astjson都在标准库中。

虽然这不是一个“完美”的答案,但如果您的计划是完全忽略Unicode,那么它就相当不错了。Python 2.7

import json, ast
d = { 'field' : 'value' }
print "JSON Fail: ", json.loads(json.dumps(d))
print "AST Win:", ast.literal_eval(json.dumps(d))

给:

JSON Fail:  {u'field': u'value'}
AST Win: {'field': 'value'}

当一些对象实际上是Unicode字符串时,这就变得更麻烦了。完整的答案很快就变得棘手起来。

只需使用泡菜而不是json进行转储和加载,如下所示:

    import json
import pickle


d = { 'field1': 'value1', 'field2': 2, }


json.dump(d,open("testjson.txt","w"))


print json.load(open("testjson.txt","r"))


pickle.dump(d,open("testpickle.txt","w"))


print pickle.load(open("testpickle.txt","r"))

它产生的输出是(字符串和整数被正确处理):

    {u'field2': 2, u'field1': u'value1'}
{'field2': 2, 'field1': 'value1'}

我有一个JSON字典作为字符串。键和值是Unicode对象,如下例所示:

myStringDict = "{u'key':u'value'}"

我可以使用上面建议的byteify函数,使用ast.literal_eval(myStringDict)将字符串转换为dict对象。

As Mark (Amery)正确地指出:在JSON转储上使用PyYAML的反序列化器仅在只有ASCII时有效。至少是开箱即用。

关于PyYAML方法的两个简短评论:

  1. Never对字段中的数据使用yaml.load ()。这是YAML的一个特性(!),可以执行隐藏在结构中的任意代码。

  2. 可以使它也适用于非ASCII:

     def to_utf8(loader, node):
    return loader.construct_scalar(node).encode('utf-8')
    yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8)
    

但就性能而言,这与马克·艾默里的答案无法相提并论:

将一些深度嵌套的样本字典扔到这两个方法上,我得到了这个(与dt[j] = json.loads(json.dumps(m))的时间delta):

     dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
dt[byteify recursion(Mark Amery)] =~   5 * dt[j]

因此,反序列化,包括完全遍历树而且编码,完全在JSON基于c的实现的数量级之内。我发现这非常快,而且它也比深嵌套结构的yaml加载更健壮。更少的安全错误,看yaml.load。

=比;虽然我希望指针指向仅基于c语言的转换器,但byteify函数应该是默认答案。

如果JSON结构来自包含用户输入的字段,则尤其如此。因为这样你可能需要遍历你的结构无论如何 -独立于你想要的内部数据结构('unicode三明治'或字节字符串)。

为什么?

Unicode 正常化。对于不知情的人:服用止痛药并阅读

所以使用byteify递归你一石二鸟:

  1. 从嵌套的JSON转储中获取字节串
  2. 让用户输入值正常化,这样你就可以在你的存储中找到东西。

在我的测试中,用unicodedata。正常化(NFC,输入).encode(“utf - 8”)替换input.encode('utf-8')甚至比没有NFC更快——但我猜这在很大程度上依赖于示例数据。

下面是一个用C语言编写的递归编码器: https://github.com/axiros/nested_encode < / p >

“平均”的性能开销;与json.loads ()相比,结构约为10%。

python speed.py
json loads            [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster..
json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster.
time overhead in percent: 9%

使用这个测试结构:

import json, nested_encode, time


s = """
{
"firstName": "Jos\\u0301",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "\\u00d6sterreich",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
],
"children": [],
"spouse": null,
"a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}]
}
"""




t1 = time.time()
for i in xrange(10000):
u = json.loads(s)
dt_json = time.time() - t1


t1 = time.time()
for i in xrange(10000):
b = nested_encode.encode_nested(json.loads(s))
dt_json_enc = time.time() - t1


print "json loads            [%.2fsec]: %s..." % (dt_json, str(u)[:20])
print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20])


print "time overhead in percent: %i%%"  % (100 * (dt_json_enc - dt_json)/dt_json)

我从马克测定这个问题的答案改编了代码,特别是为了摆脱isinstance的优点duck typing

编码是手动完成的,ensure_ascii是禁用的。json.dump的Python文档说:

如果ensure_ascii为True(默认值),输出中的所有非ascii字符将使用\uXXXX序列转义

免责声明:在文档测试中,我使用了匈牙利语。一些著名的与匈牙利语相关的字符编码有:cp852, IBM/OEM编码,例如在DOS(有时被称为美国信息交换标准代码)中使用。我认为不正确,因为它依赖于代码页设置)。例如,在Windows中使用windows - 1250(有时称为ANSI,取决于区域设置),而在HTTP服务器中使用ISO 8859 - 1

测试文本Tüskéshátú kígyóbűvölő归属于Koltai Laszlo(个人姓名形式),并且是从维基百科

# coding: utf-8
"""
This file should be encoded correctly with utf-8.
"""
import json


def encode_items(input, encoding='utf-8'):
u"""original from: https://stackoverflow.com/a/13101776/611007
adapted by SO/u/611007 (20150623)
>>>
>>> ## run this with `python -m doctest <this file>.py` from command line
>>>
>>> txt = u"Tüskéshátú kígyóbűvölő"
>>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"
>>> txt3 = u"uúuutifu"
>>> txt4 = b'u\\xfauutifu'
>>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest:
>>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250')
>>> txt4u = txt4.decode('cp1250')
>>> assert txt4u == u'u\\xfauutifu', repr(txt4u)
>>> txt5 = b"u\\xc3\\xbauutifu"
>>> txt5u = txt5.decode('utf-8')
>>> txt6 = u"u\\u251c\\u2551uutifu"
>>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8')
>>> assert txt == there_and_back_again(txt)
>>> assert txt == there_and_back_again(txt2)
>>> assert txt3 == there_and_back_again(txt3)
>>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852')
>>> assert txt3 == txt4u,(txt3,txt4u)
>>> assert txt3 == there_and_back_again(txt5)
>>> assert txt3 == there_and_back_again(txt5u)
>>> assert txt3 == there_and_back_again(txt4u)
>>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8')
>>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8')
>>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8')
>>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8')
>>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8')
>>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8')
>>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8')
>>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8')
"""
try:
input.iteritems
return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()}
except AttributeError:
if isinstance(input, unicode):
return input.encode(encoding)
elif isinstance(input, str):
return input
try:
iter(input)
return [encode_items(e) for e in input]
except TypeError:
return input


def alt_dumps(obj, **kwargs):
"""
>>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"})
'{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}'
"""
if 'ensure_ascii' in kwargs:
del kwargs['ensure_ascii']
return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs)

我还想强调Jarret难的这个问题的答案,它引用了JSON规范,引用如下:

字符串是零个或多个Unicode字符的集合

在我的用例中,我有带有JSON内容的文件。它们是utf - 8编码的文件。ensure_ascii导致正确转义,但不是非常可读的JSON文件,这就是为什么我已经改编了马克Amery的答案,以满足我的需要。

doctest不是特别周到,但我分享了代码,希望它对某人有用。

object_hook的解决方案

它适用于Python 2.7 而且 3.x。

import json


def json_load_byteified(file_handle):
return _byteify(
json.load(file_handle, object_hook=_byteify),
ignore_dicts=True
)


def json_loads_byteified(json_text):
return _byteify(
json.loads(json_text, object_hook=_byteify),
ignore_dicts=True
)


def _byteify(data, ignore_dicts = False):
if isinstance(data, str):
return data


# If this is a list of values, return list of byteified values
if isinstance(data, list):
return [ _byteify(item, ignore_dicts=True) for item in data ]
# If this is a dictionary, return dictionary of byteified keys and values
# but only if we haven't already byteified it
if isinstance(data, dict) and not ignore_dicts:
return {
_byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
for key, value in data.items() # changed to .items() for Python 2.7/3
}


# Python 3 compatible duck-typing
# If this is a Unicode string, return its string representation
if str(type(data)) == "<type 'unicode'>":
return data.encode('utf-8')


# If it's anything else, return it in its original form
return data

使用示例:

>>> json_loads_byteified('{"Hello": "World"}')
{'Hello': 'World'}
>>> json_loads_byteified('"I am a top-level string"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["I am inside a list"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}

它是如何工作的,我为什么要使用它?

Mark Amery的功能比这些更短更清晰,所以它们的意义是什么?你为什么要用它们?

纯粹用于性能。Mark的回答首先用Unicode字符串完整地解码JSON文本,然后递归地遍历整个解码后的值,将所有字符串转换为字节字符串。这有一些不好的影响:

  • 在内存中创建整个解码结构的副本
  • 如果你的JSON对象是真的深度嵌套(500级或更多),那么你将达到Python的最大递归深度

这个答案通过使用json.loadjson.loadsobject_hook参数缓解了这两个性能问题。从的文档:

object_hook是一个可选函数,将在任何对象文字解码的结果(dict)时调用它。object_hook的返回值将被使用,而不是dict。此特性可用于实现自定义解码器

由于在其他字典中嵌套了许多层的字典被传递给object_hook 当它们被解码的时候,因此我们可以在此时对其中的任何字符串或列表进行字节化,从而避免以后需要进行深度递归。

Mark的答案不适合作为object_hook使用,因为它会递归到嵌套字典中。在这个答案中,我们通过ignore_dicts参数到_byteify来防止这种递归,当object_hook传递一个新的dict来byteify时,该参数始终传递给它除了ignore_dicts标志告诉_byteify忽略__abc4,因为它们已经被字节化了。

最后,我们的json_load_byteifiedjson_loads_byteified实现对json.loadjson.loads返回的结果调用_byteify(带有ignore_dicts=True)来处理被解码的JSON文本在顶层没有dict的情况。

迈克·布伦南的回答是接近的,但没有任何理由重新遍历整个结构。如果使用object_hook_pairs (Python 2.7+)形参:

object_pairs_hook是一个可选函数,将使用任意对象字面量的解码结果调用。object_pairs_hook的返回值将被使用,而不是dict。此特性可用于实现依赖于键和值对解码顺序的自定义解码器(例如,collections.OrderedDict将记住插入的顺序)。如果还定义了object_hook,则object_pairs_hook优先。

有了它,你可以得到每个JSON对象,所以你可以不需要递归地进行解码:

def deunicodify_hook(pairs):
new_pairs = []
for key, value in pairs:
if isinstance(value, unicode):
value = value.encode('utf-8')
if isinstance(key, unicode):
key = key.encode('utf-8')
new_pairs.append((key, value))
return dict(new_pairs)


In [52]: open('test.json').read()
Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}'


In [53]: json.load(open('test.json'))
Out[53]:
{u'1': u'hello',
u'abc': [1, 2, 3],
u'boo': [1, u'hi', u'moo', {u'5': u'some'}],
u'def': {u'hi': u'mom'}}


In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook)
Out[54]:
{'1': 'hello',
'abc': [1, 2, 3],
'boo': [1, 'hi', 'moo', {'5': 'some'}],
'def': {'hi': 'mom'}}

注意,我从来不需要递归地调用钩子,因为当你使用object_pairs_hook时,每个对象都会被传递给钩子。您确实需要关心列表,但是正如您所看到的,列表中的对象将被正确地转换,并且您不必递归来实现它。

一位同事指出Python2.6没有object_hook_pairs。你仍然可以通过做一个很小的改变来使用这个will Python2.6。在上面的钩子中,更改:

for key, value in pairs:

for key, value in pairs.iteritems():

然后使用object_hook代替object_pairs_hook:

In [66]: json.load(open('test.json'), object_hook=deunicodify_hook)
Out[66]:
{'1': 'hello',
'abc': [1, 2, 3],
'boo': [1, 'hi', 'moo', {'5': 'some'}],
'def': {'hi': 'mom'}}

使用object_pairs_hook会为JSON对象中的每个对象少实例化一个字典,如果您正在解析一个巨大的文档,这可能是值得的。

检查这个答案类似的问题,就像这样说

u -前缀仅仅意味着你有一个Unicode字符串。当你真正使用字符串时,它不会出现在你的数据中。不要被打印出来的结果所迷惑。

例如,试试这个:

print mail_accounts[0]["i"]

你不会看到u

使用钩子(来自Mirec Miskuf的回答)支持Python 2和3:

import requests
import six
from six import iteritems


requests.packages.urllib3.disable_warnings()  # @UndefinedVariable
r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False)


def _byteify(data):
# If this is a Unicode string, return its string representation
if isinstance(data, six.string_types):
return str(data.encode('utf-8').decode())


# If this is a list of values, return list of byteified values
if isinstance(data, list):
return [ _byteify(item) for item in data ]


# If this is a dictionary, return dictionary of byteified keys and values,
# but only if we haven't already byteified it
if isinstance(data, dict):
return {
_byteify(key): _byteify(value) for key, value in iteritems(data)
}
# If it's anything else, return it in its original form
return data


w = r.json(object_hook=_byteify)
print(w)

返回:

 {'three': '', 'key': 'value', 'one': 'two'}
使用Python 3.6,有时我仍然会遇到这个问题。例如,当从休息 API获得响应并将响应文本加载到JSON时,我仍然得到Unicode字符串。 使用json.dumps()找到了一个简单的解决方案
response_message = json.loads(json.dumps(response.text))
print(response_message)