我正在使用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`
解决方案 object_hook
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'}
它是如何工作的,为什么要使用它? Mark Amery的功能比这些功能更短,更清晰,那么它们的意义何在?你为什么要使用它们?
纯粹是为了表现。Mark的答案首先使用unicode字符串完全解码JSON文本,然后遍历整个解码值以将所有字符串转换为字节字符串。这有一些不良影响:
unicode
这个答案通过缓解这两方面的性能问题object_hook的参数json.load和json.loads。从文档:
json.load
json.loads
object_hook是一个可选函数,它将被解码的任何对象文字(a dict)的结果调用。将使用object_hook的返回值代替dict。此功能可用于实现自定义解码器
(a dict)
dict
由于嵌套在其他字典中许多层次的字典object_hook 在解码时会传递给它们,因此我们可以在那时将其中的任何字符串或列表字节化,并避免以后再进行深度递归。
Mark的答案不适合按object_hook现状使用,因为它会递归为嵌套词典。我们阻止这个答案与该递归ignore_dicts参数_byteify,它被传递给它在任何时候都只是当object_hook它传递一个新dict来byteify。该ignore_dicts标志指示_byteify忽略dicts,因为它们已经被字节化了。
ignore_dicts
_byteify
byteify
dicts
最后,我们的实现json_load_byteified和在从或返回的结果上json_loads_byteified调用_byteify(with ignore_dicts=True)来处理被解码的JSON文本在顶层没有的情况。json.loadjson.loadsdict
json_load_byteified
json_loads_byteified
_byteify(with ignore_dicts=True)
json.loadjson.loadsdict
虽然这里有一些不错的答案,但我最终还是使用PyYAML来解析我的JSON文件,因为它以str类型字符串而不是unicode类型的形式提供了键和值。由于JSON是YAML的子集,因此效果很好:
str
>>> 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函数的“附加功能” 。
safe_load
如果你想拥有的1.2版规范更多的支持(和YAML解析器正确地解析非常低的数字)尝试Ruamel YAML:pip install ruamel.yaml和import ruamel.yaml as yaml我在测试时所需要的所有我。
Ruamel YAML:pip install ruamel.yaml
import ruamel.yaml as yaml
转换次数 如前所述,没有转换!如果不能确定只处理ASCII值(并且不能确定大多数时间),最好使用转换函数:
我现在几次使用Mark Amery的产品,效果很好,而且非常易于使用。你也可以使用类似的函数作为object_hook替代函数,因为它可以提高大文件的性能。对此,请参见Mirec Miskuf稍有涉及的答案。
Mark Amery
Mirec Miskuf