我使用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对象之间没有区别。它们都是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']
Mark (Amery)正确地指出:在JSON转储上使用PyYAML的反序列化器仅在只有ASCII时有效。至少是开箱即用。
关于PyYAML方法的两个简短评论:
永远不要对来自字段的数据使用yaml.load()。这是YAML的一个特性(!),可以执行隐藏在结构中的任意代码。
你也可以通过以下方法使它适用于非ASCII:
Def to_utf8(加载器,节点):
返回loader.construct_scalar(节点).encode(“utf - 8”)
yaml.add_constructor (u 'tag: yaml.org, 2002: str ', to_utf8)
但就性能而言,这与马克·艾默里的答案无法相提并论:
将一些深度嵌套的样本字典扔到这两个方法上,我得到了这个(与dt[j] = json.loads(json.dumps(m))的时间delta):
dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
dt[byteify recursion(Mark Amery)] =~ 5 * dt[j]
因此,反序列化(包括完全遍历树和编码)完全在基于c语言的JSON实现的数量级之内。我发现这非常快,而且在深度嵌套结构上比yaml加载更健壮。更少的安全错误,看yaml.load。
虽然我很喜欢一个指向c语言的转换器的指针,但byteify函数应该是默认答案。
如果JSON结构来自包含用户输入的字段,则尤其如此。因为这样你可能需要遍历你的结构——独立于你想要的内部数据结构(“unicode三明治”或字节字符串)。
Why?
Unicode正常化。给不知情的人:吃片止痛药,看看这篇文章。
所以使用byteify递归你一石二鸟:
从嵌套的JSON转储中获取字节串
让用户输入值正常化,这样你就可以在你的存储中找到东西。
在我的测试中,结果是将input.encode('utf-8')替换为unicodedata。normalize('NFC', input).encode('utf-8')甚至比没有NFC时还要快——但我猜这在很大程度上依赖于样本数据。
我构建了这个递归施法者。它符合我的需要,我认为它是相对完整的。
def _parseJSON(self, obj):
newobj = {}
for key, value in obj.iteritems():
key = str(key)
if isinstance(value, dict):
newobj[key] = self._parseJSON(value)
elif isinstance(value, list):
if key not in newobj:
newobj[key] = []
for i in value:
newobj[key].append(self._parseJSON(i))
elif isinstance(value, unicode):
val = str(value)
if val.isdigit():
val = int(val)
else:
try:
val = float(val)
except ValueError:
val = str(val)
newobj[key] = val
return newobj
只需要像这样传递一个JSON对象:
obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)
我把它作为一个类的私有成员,但您可以根据需要重新使用该方法。