Python 的完美 json loads

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


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

用法如下:


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

说到参数检查,一般可以做三步:

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

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

对于必须参数,通常第二和第三步是一起完成的,比如:


if not hasattr(data, 'k') or not isinstance(data.k, int):
    return error()

但非必须参数就要单独考虑第二种情况了,因为第二种也是合法的:


if hasattr(data, 'k') and data.k != '' and not isinstance(data.k, int):
    return error()

因为 Python 会把很多种如 len() 为零的对象的布尔值判断为 False,所以上面始终没有使用 if data.k: 这样的写法,以避免误判。

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

你可能感兴趣的:(Python 的完美 json loads)