为什么json.loads只允许object_hook
,parse_float
,parse_int
和parse_constant
?
json.loads(fp[, encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, **kw]]]]]]]])
它们本质上都是回调,在解析过程中使用其返回值。
我的目的是通过修改回调参数来使用这些回调来构建我自己的JSON字符串表示。但是没有数组,字符串或布尔值的回调 - 它似乎相当有限。
举个简单的例子:
import json
def _object_hook(d):
print(d)
return d
json.loads('{"a": [1, [2, [3]]]}', object_hook=_object_hook)
...导致只有一次调用_object_hook(因为只有一个对象)。
>>> {u'a': [1, [2, [3]]]}
给出一个任意深度的嵌套数组,传递数组的工作(递归地,或者可能是广度优先/深度优先遍历)仍然存在。
然后,字符串也是出于某种原因和例外:
import json
def _object_hook(o):
print('_object_hook', o)
return o
def _parse_float(f):
print('_parse_float', f)
return f
def _parse_int(i):
print('_parse_int', i)
return i
def _parse_constant(c):
print('_parse_constant', c)
return c
json.loads('{"a": [1, [2, [3.1], ["4"]]]}',
object_hook=_object_hook,
parse_int=_parse_int,
parse_float=_parse_float,
parse_constant=_parse_constant)
...没有办法处理字符串(下面的结果中省略了“4”):
('_parse_int', '1')
('_parse_int', '2')
('_parse_float', '3.1')
('_object_hook', {u'a': ['1', ['2', ['3.1'], [u'4']]]})
也许我的期望是错误的。但是将JSON字符串解析为Python字典或列表似乎很浪费,只是将其再次解析为自定义格式。
没有字符串的数组挂钩或解析回调,boolean,null等。我使用json.loads
然后将生成的Python表示解析为我自己的Python类吗?
答案 0 :(得分:1)
实际上,json.loads
调用其scanner
来解析输入字符串,并且可以通过在自定义类中重建scanner
来挂钩所有行为。
import json
from json.scanner import py_make_scanner
from json.decoder import JSONArray
class CustomizedDecoder(json.JSONDecoder):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def parse_array(*_args, **_kwargs):
values, end = JSONArray(*_args, **_kwargs)
for item in values:
print(item) # Is it what you want?
return values, end
self.parse_array = parse_array
self.scan_once = py_make_scanner(self)
json.loads('{"a": [1, [2, [3.1], ["4"]]]}', cls=CustomizedDecoder)
输出
3.1
4
2
[3.1]
['4']
1
[2, [3.1], ['4']]
此外,您可以通过执行完全相同的操作来挂接其他几个功能。
self.object_hook = object_hook
self.parse_float = parse_float or float
self.parse_int = parse_int or int
self.parse_constant = parse_constant or _CONSTANTS.__getitem__
self.object_pairs_hook = object_pairs_hook
self.parse_object = JSONObject
self.parse_array = JSONArray
self.parse_string = scanstring
答案 1 :(得分:0)
如果您愿意考虑稍慢的解析,可以使用ruamel.yaml
解析器(免责声明:我是该程序包的作者)。由于YAML 1.2是JSON的超集,用于所有实际目的,您可以继承Constructor
:
import sys
from ruamel.yaml import YAML, SafeConstructor
json_str = '{"a": [1, [2.0, True, [3, null]]]}'
class MyConstructor(SafeConstructor):
def construct_yaml_null(self, node):
print('null')
data = SafeConstructor.construct_yaml_null(self, node)
return data
def construct_yaml_bool(self, node):
print('bool')
data = SafeConstructor.construct_yaml_bool(self, node)
return data
def construct_yaml_int(self, node):
print('int')
data = SafeConstructor.construct_yaml_int(self, node)
return data
def construct_yaml_float(self, node):
print('float')
data = SafeConstructor.construct_yaml_float(self, node)
return data
def construct_yaml_str(self, node):
print('str')
data = SafeConstructor.construct_yaml_str(self, node)
return data
def construct_yaml_seq(self, node):
print('seq')
for data in SafeConstructor.construct_yaml_seq(self, node):
pass
return data
def construct_yaml_map(self, node):
print('map')
for data in SafeConstructor.construct_yaml_map(self, node):
pass
return data
MyConstructor.add_constructor(
u'tag:yaml.org,2002:null',
MyConstructor.construct_yaml_null)
MyConstructor.add_constructor(
u'tag:yaml.org,2002:bool',
MyConstructor.construct_yaml_bool)
MyConstructor.add_constructor(
u'tag:yaml.org,2002:int',
MyConstructor.construct_yaml_int)
MyConstructor.add_constructor(
u'tag:yaml.org,2002:float',
MyConstructor.construct_yaml_float)
MyConstructor.add_constructor(
u'tag:yaml.org,2002:str',
MyConstructor.construct_yaml_str)
MyConstructor.add_constructor(
u'tag:yaml.org,2002:seq',
MyConstructor.construct_yaml_seq)
MyConstructor.add_constructor(
u'tag:yaml.org,2002:map',
MyConstructor.construct_yaml_map)
yaml = YAML(typ='safe')
yaml.Constructor = MyConstructor
data = yaml.load(json_str)
print(data)
只需将每个construct_yaml_XYZ
方法中的代码替换为创建所需对象的代码并将其返回。
有趣的生意"在创建映射/字典时使用for
循环。 sequence / list,用于解包创建这些对象的两步过程("真实" YAML输入以使用锚点/别名来处理递归数据结构)。
以上输出:
map
str
seq
int
seq
float
bool
seq
int
null
{'a': [1, [2.0, True, [3, None]]]}
您也可以在较低级别挂钩YAML解析器,但这并不会使实现更容易,而且可能只是稍微快一点。
答案 2 :(得分:0)
@ martijn-pieters有正确的方法。我正在试图使用钩子来解析JSON。它们的存在是为了增强解析,但不能取代它。