Python 的完美 json loads

为了对付表单提交时参数多和 json 结构复杂的状况,我写了一个名为 recursive_json_loads 的处理函数来对请求对象递归调用 json.loads() 以期可以一次性将全部参数转化为更好用的 Python 类型。后来又发现了 web.py 的 Storage 对象,使这个函数愈加好用起来。python

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) + '>'

用法以下:web

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),从而避免在访问不存在的值时触发属性异常。想了一下感受不大好,主要是并无把参数检查的代码简化多少。json


说到参数检查,通常能够作三步:函数

  1. 是否传了某个参数 (?k)
  2. 参数值是否为空 (?k=)
  3. 参数的类型/值是否符合要求(?k=0)

有一点须要注意的是,对于传了参数而没有传值的状况(?k=),k 的值会是 '',并且 isinstance('', str) 会返回 True。code

对于必须参数,一般第二和第三步是一块儿完成的,好比:对象

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: 这样的写法,以免误判。get


补充,Storage 类的一个缺点是:他有 __dict__ 属性,但该属性永远为空it

相关文章
相关标签/搜索