Bru*_*tus 272 python unicode serialization json python-2.x
我正在使用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`
Run Code Online (Sandbox Code Playgroud)
很久以前,当我遇到Python 2时,问了这个问题.今天一个简单而干净的解决方案是使用最新版本的Python - 即Python 3和转发版.
Bru*_*tus 178
虽然这里有一些很好的答案,但我最终使用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']
Run Code Online (Sandbox Code Playgroud)
有些事情需要注意:
我得到字符串对象,因为我的所有条目都是ASCII编码的.如果我使用unicode编码的条目,我会把它们作为unicode对象取回- 没有转换!
你应该(可能总是)使用PyYAML的safe_load
功能; 如果你用它来加载JSON文件,你无论如何都不需要该load
功能的"附加功能" .
如果你想要一个YAML解析器,它对 1.2版本的规范有更多的支持(并且正确解析非常低的数字),请尝试Ruamel YAML:pip install ruamel.yaml
并且import ruamel.yaml as yaml
是我在测试中所需要的.
如上所述,没有转换!如果你不能确定只处理ASCII值(并且你大多数时候都不能确定),最好使用转换函数:
我现在使用Mark Amery的几次,效果很好而且非常容易使用.您也可以使用类似的功能object_hook
,因为它可能会提高大文件的性能.请参阅Mirec Miskuf稍微提及的答案.
Mar*_*ery 141
没有内置选项使json模块函数返回字节字符串而不是unicode字符串.但是,这个简短而简单的递归函数会将任何已解码的JSON对象从使用unicode字符串转换为UTF-8编码的字节字符串:
def byteify(input):
if isinstance(input, dict):
return {byteify(key): byteify(value)
for key, value in input.iteritems()}
elif isinstance(input, list):
return [byteify(element) for element in input]
elif isinstance(input, unicode):
return input.encode('utf-8')
else:
return input
Run Code Online (Sandbox Code Playgroud)
只需在您通过json.load
或json.loads
拨打的输出上调用此选项即可.
几个笔记:
return {byteify(key): byteify(value) for key, value in input.iteritems()}
为return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])
,因为在Python 2.7之前不支持字典理解.object_hook
或object_pairs_hook
参数来避免这些特性.到目前为止,Mirec Miskuf的答案是唯一能够正确解决这个问题的答案,尽管如此,它比我的方法复杂得多.小智 100
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
Run Code Online (Sandbox Code Playgroud)
用法示例:
>>> 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'}
Run Code Online (Sandbox Code Playgroud)
Mark Amery的功能比这些功能更短更清晰,那么它们的重点是什么?你为什么要用它们?
纯粹是为了表现.Mark的回答首先使用unicode字符串完全解码JSON文本,然后通过整个解码值进行递归,将所有字符串转换为字节字符串.这有几个不良影响:
这个答案通过缓解这两方面的性能问题object_hook
的参数json.load
和json.loads
.来自文档:
object_hook
是一个可选函数,将使用任何对象文字解码的结果调用(adict
).将使用object_hook的返回值而不是dict
.此功能可用于实现自定义解码器
由于字典在其他字典中嵌套了许多级别object_hook
,因此在它们被解码时会被传递给它们,我们可以在那时对它们内部的任何字符串或列表进行字节化,从而避免以后需要进行深度递归.
Mark的答案不适合object_hook
现场使用,因为它会递归到嵌套的词典中.我们阻止这个答案与该递归ignore_dicts
参数_byteify
,它被传递给它在任何时候都只是当object_hook
它传递一个新dict
来byteify.该ignore_dicts
标志告诉_byteify
忽略dict
s,因为它们已被字节化.
最后,我们对结果的实现json_load_byteified
和json_loads_byteified
调用_byteify
(with ignore_dicts=True
)返回json.load
或json.loads
处理被解码的JSON文本没有dict
顶级的情况.
Mik*_*nan 74
您可以使用object_hook
参数json.loads
来传入转换器.事后你不必进行转换.该json
模块将始终object_hook
仅传递dicts,并且它将递归传递嵌套的dicts,因此您不必自己递归到嵌套的dicts.我不认为我会将unicode字符串转换为Wells节目等数字.如果它是一个unicode字符串,它在JSON文件中被引用为一个字符串,所以它应该是一个字符串(或文件是坏的).
另外,我会尽量避免在对象str(val)
上做类似的事情unicode
.您应该使用value.encode(encoding)
有效的编码,具体取决于外部lib期望的内容.
所以,例如:
def _decode_list(data):
rv = []
for item in data:
if isinstance(item, unicode):
item = item.encode('utf-8')
elif isinstance(item, list):
item = _decode_list(item)
elif isinstance(item, dict):
item = _decode_dict(item)
rv.append(item)
return rv
def _decode_dict(data):
rv = {}
for key, value in data.iteritems():
if isinstance(key, unicode):
key = key.encode('utf-8')
if isinstance(value, unicode):
value = value.encode('utf-8')
elif isinstance(value, list):
value = _decode_list(value)
elif isinstance(value, dict):
value = _decode_dict(value)
rv[key] = value
return rv
obj = json.loads(s, object_hook=_decode_dict)
Run Code Online (Sandbox Code Playgroud)
nos*_*klo 37
那是因为json在字符串对象和unicode对象之间没有区别.它们都是javascript中的所有字符串.
我认为JSON是正确的返回unicode对象.事实上,我不会接受任何更少,因为javascript字符串实际上是unicode
对象(即JSON(javascript)字符串可以存储任何类型的unicode字符)因此unicode
在从JSON转换字符串时创建对象是有意义的.简单的字符串是不适合的,因为库必须猜测你想要的编码.
最好在unicode
各处使用字符串对象.因此,您最好的选择是更新库,以便它们可以处理unicode对象.
但是如果你真的想要字节串,只需将结果编码为你选择的编码:
>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']
Run Code Online (Sandbox Code Playgroud)
Cha*_*iam 14
有一种简单的解决方法.
TL; 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))
Run Code Online (Sandbox Code Playgroud)
得到:
JSON Fail: {u'field': u'value'}
AST Win: {'field': 'value'}
Run Code Online (Sandbox Code Playgroud)
当某些对象确实是Unicode字符串时,这会变得更加毛茸茸.完整的答案很快变得毛茸茸.
Tra*_*sen 11
Mike Brennan的答案很接近,但没有理由重新遍历整个结构.如果您使用object_hook_pairs
(Python 2.7+)参数:
object_pairs_hook
是一个可选函数,将使用对有序列表对解码的任何对象文字的结果进行调用.将使用返回值object_pairs_hook
而不是dict
.此功能可用于实现依赖于键和值对被解码的顺序的自定义解码器(例如,collections.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'}}
Run Code Online (Sandbox Code Playgroud)
请注意,我永远不必递归地调用钩子,因为当你使用时,每个对象都会被传递给钩子object_pairs_hook
.您必须关心列表,但正如您所看到的,列表中的对象将被正确转换,您无需递归即可实现.
编辑:一位同事指出Python2.6没有object_hook_pairs
.您仍然可以通过进行非常小的更改来使用Python2.6.在上面的钩子中,改变:
for key, value in pairs:
Run Code Online (Sandbox Code Playgroud)
至
for key, value in pairs.iteritems():
Run Code Online (Sandbox Code Playgroud)
然后使用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'}}
Run Code Online (Sandbox Code Playgroud)
使用object_pairs_hook
结果为JSON对象中的每个对象实例化一个较少的字典,如果您正在解析一个巨大的文档,那么可能值得.
我担心在simplejson库中无法自动实现这一点.
simplejson中的扫描仪和解码器旨在生成unicode文本.为此,库使用一个名为的函数c_scanstring
(如果它可用,速度),或者py_scanstring
C版本不可用.scanstring
几乎所有simplejson用于解码可能包含文本的结构的例程都会多次调用该函数.你必须scanstring
在simplejson.decoder或子类中使用monkeypatch 值,JSONDecoder
并提供几乎所有可能包含文本的实现.
然而,simplejson输出unicode的原因是json规范特别提到"字符串是零个或多个Unicode字符的集合"......对unicode的支持被假定为格式本身的一部分.Simplejson的scanstring
实现甚至扫描和解释unicode转义(甚至错误检查格式错误的多字节字符集表示),因此它可以可靠地将值返回给你的唯一方法是unicode.
如果您有一个需要的老化库str
,我建议您在解析后仔细搜索嵌套数据结构(我承认您明确表示要避免...抱歉),或者可能将您的库包装在某种类型的库中在门面,您可以在更精细的水平上按下输入参数.如果您的数据结构确实是嵌套的,那么第二种方法可能比第一种方法更易于管理.
归档时间: |
|
查看次数: |
301617 次 |
最近记录: |