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

新手上路,请多包涵

我正在使用 Python 2ASCII 编码 的文本文件中解析 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 及更高版本。)

原文由 Brutus 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 641
2 个回答

解决方案 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 的最大递归深度

该答案通过使用 object_hook 参数 json.loadjson.loads 来缓解这两个性能问题。从 文档中

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

由于在其他词典中嵌套了很多层的词典 在解码 时会传递给 object_hook ,我们可以在此时对其中的任何字符串或列表进行字节化,避免以后进行深度递归。

马克的回答不适合用作 object_hook 就目前而言,因为它递归到嵌套字典中。我们使用 ignore_dicts 参数来防止此答案中的递归 _byteify ,它始终传递给它, 除非 object_hook dict 字节化。 ignore_dicts 标志告诉 _byteify 忽略 dict s 因为它们已经被字节化了。

Finally, our implementations of json_load_byteified and json_loads_byteified call _byteify (with ignore_dicts=True ) on the result returned from json.loadjson.loads 处理被解码的 JSON 文本在顶层没有 dict 的情况。

原文由 Mirec Miskuf 发布,翻译遵循 CC BY-SA 4.0 许可协议

虽然这里有一些很好的答案,但我最终使用 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 函数的“额外功能”。

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

转换

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

我现在用过 Mark Amery 的那个,效果很好,而且非常容易使用。您也可以使用与 object_hook 类似的功能,因为它可能会提高您处理大文件的性能。请参阅 Mirec Miskuf 的稍微复杂的答案

原文由 Brutus 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题