为了对付表单提交时参数多和 json 结构复杂的情况,我写了一个名为 recursive_json_loads
的处理函数来对请求对象递归调用 json.loads()
以期能够一次性将所有参数转化为更好用的 Python 类型。后来又发现了 web.py 的 Storage
对象,使这个函数越发好用起来。
lang:python
import simplejson as json
def recursive_json_loads(data):
if isinstance(data, list):
return [recursive_json_loads(i) for i in data]
elif isinstance(data, tuple):
return tuple([recursive_json_loads(i) for i in data])
elif isinstance(data, dict):
return Storage({recursive_json_loads(k): recursive_json_loads(data[k]) for k in data.keys()})
else:
try:
obj = json.loads(data)
if obj == data:
return data
except:
return data
return recursive_json_loads(obj)
class Storage(dict):
"""
A Storage object is like a dictionary except `obj.foo` can be used
in addition to `obj['foo']`.
>>> o = storage(a=1)
>>> o.a
1
>>> o['a']
1
>>> o.a = 2
>>> o['a']
2
>>> del o.a
>>> o.a
Traceback (most recent call last):
...
AttributeError: 'a'
"""
def __getattr__(self, key):
try:
return self[key]
except KeyError as k:
raise AttributeError(k)
def __setattr__(self, key, value):
self[key] = value
def __delattr__(self, key):
try:
del self[key]
except KeyError as k:
raise AttributeError(k)
def __repr__(self):
return '<Storage ' + dict.__repr__(self) + '>'
用法如下:
lang:python
>>> request = json.dumps({"foo":["a", 123], "bar": {1:"int", "str":"05"}})
>>> data = recursive_json_loads(request)
>>> data.foo
['a', 123]
>>> data.bar
<Storage {1: 'int', 'str': '05'}>
>>> data.bar.str
'05'
>>> data.bar[1]
'int'
至于是否应该把 Storage 的 self[key]
改成 self.get(k)
,从而避免在访问不存在的值时触发属性异常。想了一下感觉不大好,主要是并没有把参数检查的代码简化多少。
说到参数检查,一般可以做三步:
- 是否传了某个参数 (?k)
- 参数值是否为空 (?k=)
- 参数的类型/值是否符合要求(?k=0)
有一点需要注意的是,对于传了参数而没有传值的情况(?k=),k 的值会是 ''
,而且 isinstance('', str)
会返回 True。
对于必须参数,通常第二和第三步是一起完成的,比如:
lang:python
if not hasattr(data, 'k') or not isinstance(data.k, int):
return error()
但非必须参数就要单独考虑第二种情况了,因为第二种也是合法的:
lang:python
if hasattr(data, 'k') and data.k != '' and not isinstance(data.k, int):
return error()
因为 Python 会把很多种如 len()
为零的对象的布尔值判断为 False
,所以上面始终没有使用 if data.k:
这样的写法,以避免误判。
补充,Storage 类的一个缺点是:他有 __dict__
属性,但该属性永远为空