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


当前回答

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

其他回答

使用Python 3.6,有时我仍然会遇到这个问题。例如,当从REST API获取响应并将响应文本加载到JSON时,我仍然得到Unicode字符串。 使用json.dumps()找到了一个简单的解决方案。

response_message = json.loads(json.dumps(response.text))
print(response_message)

我也遇到了这个问题,不得不处理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键)。

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

有一个简单的变通办法。

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字符串时,这就变得更麻烦了。完整的答案很快就变得棘手起来。

迈克·布伦南的答案很接近,但没有任何理由重新审视整个结构。如果使用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对象中的每个对象少实例化一个字典,如果您正在解析一个巨大的文档,那么这样做可能是值得的。

我也遇到了同样的问题。

因为我需要将所有数据传递给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