我使用Python 2从ASCII编码的文本文件解析JSON。
当用json或simplejson加载这些文件时,我的所有字符串值都转换为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和更高版本。)
虽然这里有一些很好的答案,但我最终使用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']
笔记
但有一些事情需要注意:
I get string objects because all my entries are ASCII encoded. If I would use Unicode encoded entries, I would get them back as unicode objects — there is no conversion!
You should (probably always) use PyYAML's safe_load function; if you use it to load JSON files, you don't need the "additional power" of the load function anyway.
If you want a YAML parser that has more support for the 1.2 version of the spec (and correctly parses very low numbers) try Ruamel YAML: pip install ruamel.yaml and import ruamel.yaml as yaml was all I needed in my tests.
转换
如上所述,没有任何转换!如果你不能确定只处理ASCII值(而且大多数时候你不能确定),最好使用转换函数:
我现在用过几次Mark Amery的,效果很好,很容易使用。您还可以使用类似的函数作为object_hook,因为它可以提高大文件的性能。请参阅Mirec Miskuf稍复杂的回答。
这是因为json()在字符串对象和Unicode对象之间没有区别。它们都是JavaScript中的字符串。
我认为JSON返回Unicode对象是正确的。事实上,我不会接受更少的东西,因为JavaScript字符串实际上是unicode对象(即JSON (JavaScript)字符串可以存储任何类型的unicode字符),因此在从JSON转换字符串时创建unicode对象是有意义的。普通字符串不适合,因为库必须猜测您想要的编码。
最好在任何地方都使用unicode字符串对象。因此,最好的选择是更新库,使它们能够处理Unicode对象。
但如果你真的想要字节串,只需将结果编码为你选择的编码:
>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']
下面是一个用C语言编写的递归编码器:
https://github.com/axiros/nested_encode
与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)
没有内置选项让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中获得的输出上调用此函数。加载或json。负载的电话。
几点注意事项:
To support Python 2.6 or earlier, replace return {byteify(key): byteify(value) for key, value in input.iteritems()} with return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]), since dictionary comprehensions weren't supported until Python 2.7.
Since this answer recurses through the entire decoded object, it has a couple of undesirable performance characteristics that can be avoided with very careful use of the object_hook or object_pairs_hook parameters. Mirec Miskuf's answer is so far the only one that manages to pull this off correctly, although as a consequence, it's significantly more complicated than my approach.
使用钩子支持Python 2和3(来自Mirec Miskuf的回答):
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'}