一尘不染

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

json

我正在使用 Python 2ASCII编码的 文本文件中解析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`

更新资料

很久以前 ,当我坚持使用 Python 2 时就问 这个问题。今天一种简单易用的解决方案是使用最新版本的Python,即
Python 3 及更高版本。


阅读 332

收藏
2020-07-27

共1个答案

一尘不染

一个解决方案 object_hook

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 this is a unicode string, return its string representation
    if isinstance(data, unicode):
        return data.encode('utf-8')
    # 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.iteritems()
        }
    # 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'}

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

MarkAmery的功能比这些功能更短,更清晰,那么它们的意义何在?您为什么要使用它们?

纯粹是为了 表现
。Mark的答案首先使用unicode字符串完全解码JSON文本,然后遍历整个解码值以将所有字符串转换为字节字符串。这有一些不良影响:

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

这个答案通过缓解这两方面的性能问题object_hook的参数json.loadjson.loads。从文档

object_hook是一个可选函数,它将被解码的任何对象文字(a
dict)的结果调用。将使用object_hook的返回值代替dict。此功能可用于实现自定义解码器

由于在其他字典中嵌套了许多级别的字典object_hook 在解码时 会传递给 它们
,因此我们可以在那时将其中的任何字符串或列表字节化,并避免以后再进行深度递归。

Mark的答案不适合按object_hook现状使用,因为它会递归为嵌套词典。我们阻止这个答案与该递归ignore_dicts参数_byteify,它被传递给它在任何时候都
只是
object_hook它传递一个新dict来byteify。该ignore_dicts标志指示_byteify忽略dicts,因为它们已经被字节化了。

最后,我们的实现json_load_byteified和在从或返回的结果上json_loads_byteified调用_byteify(with
ignore_dicts=True)来处理被解码的JSON文本在顶层没有的情况。json.load``json.loads``dict

2020-07-27