我使用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和更高版本。)


当前回答

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

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

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

其他回答

下面是一个用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.

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

Object_pairs_hook是一个可选函数,它将使用任意对象字面量的解码结果调用。object_pairs_hook的返回值将被使用,而不是字典。此特性可用于实现依赖于键和值对解码顺序的自定义解码器(例如集合)。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:

to

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对象中的每个对象少实例化一个字典,如果您正在解析一个巨大的文档,那么这样做可能是值得的。

这是因为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']

有一个简单的变通办法。

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

虽然这不是一个“完美”的答案,但如果您的计划是完全忽略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字符串时,这就变得更麻烦了。完整的答案很快就变得棘手起来。