2009/05/15

将db.model转换为JSon对象

这几天玩GAE比较多。Java版也看了一下,但是感觉不如Python开发快速,并且,开发Python的代码要比java的代码少的多。
在写代码的时候发现,GAE的db.model并不能直接转换为Json格式,虽说不是什么大事,但是终归觉得这是一个障碍。我想,我不是第一个遇到这个问题的人吧。于是Google了一下。 发现了这个文章,作者使用多重继承来解决这个问题。我觉得这么搞,侵入性太强,并且要多写很多的代码,对于我这样的懒人来说非常不划算(其实,他的代码我也没怎么看懂)。自己动手丰衣足食吧。
毕竟db.model只是各种attribute的组合而已,只要把name和value拿出来组合成一个dict就行了。同样,如果是记录集的话,转成一个dict的list也就可以直接去dumps成json格式了。按照这个思路搞出了以下代码(我是python菜鸟,请高手指正):
def db_to_dict(obj, exclude_key=[]):
    __to_dict = lambda obj:dict((att, obj.__getattribute__(att)) for att in dir(obj) if (not callable(getattr(obj, att))) and (not att.startswith('_')) and (att not in exclude_key))
    if isinstance(obj, list):
        return [__to_dict(o) for o in obj]
    else:
        return __to_dict(obj) 
第一行代码非常长(其实我觉得这么长写的很爽!)。作用就是将obj里的所有attribute,不包括method的,不是以"_"开头的,并且不在exclude_key内的所有attribute使用name作为key,将value放在一个dict中。如果是一个结果集的话,那就再遍历一次结果集并将生成的dict放在一个list里返回。
这样,完全没有侵入性的将db.model或者是db.model的list搞成一个dict或者是一个dict的list了(有点像绕口令)。 这样,如果db.model中全都是python的基本数据类型的话,就可以直接使用simplejson来dumps了。
当然,应用中不可能全都是python的基本类型,比如python的datetime,GAE api中的User,甚至有db.model中会引用其他的db.model。这种情况上边方法生成的dict或者list还是不能直接搞成json格式的。

我是用的是simplejson来解析和生成Json。 
simplejson的例子了解到,只要继承了“simplejson.JSONEncoder”这个类,并在dumps的时候指定为cls参数,在子类中就可以自由的处理simplejson不支持的数据类型了。
代码如下。

class ComplexEncoder(simplejson.JSONEncoder):
    def default(self, obj):
        from datetime import datetime
        from google.appengine.ext import db
#        logging.info('obj.type = %s' % type(obj))
        if isinstance(obj, datetime):
            return str(obj)
        elif isinstance(obj, users.User):
            return model_to_dict(obj)
        elif issubclass(obj.__class__, db.Model):
            return db_to_dict(obj)
        elif isinstance(obj, db.Query):
            pass
        else:
            return super(ComplexEncoder, self).default(obj)
通过上边的代码可以看到,这个encoder类可以解决datetime型,GAE的user型,和所有的db.Model型(只要它集成了这个类)。当然在返回的如果有db.model之间有引用关系的话,obj也有可能是db.Query型,我觉得没有什么必要,就直接把它pass掉了。
上边还有一个model_to_dict这个方法,我现在是用来解决GAE API中提供的一些对象的,比如User。因为想User这样的对象只提供了对应的Method,而没有对应的attribute,所以跟第一段代码一样,也进行了同样的处理,代码如下:
def model_to_dict(obj, exclude_key=[]):
    return dict((att, getattr(obj, att)()) for att in dir(obj) if callable(getattr(obj, att)) and (not att.startswith('_')) and (att not in exclude_key))
同样,也只是一行代码只不过这次换成了只筛选出method并且将其执行后的结果作为value放在dict里而已。一般来说,这样的对象的方法都是不需要参数的,所以我觉的这样做也是没有问题的。至少到目前没有发现问题。
完成了这些后,就可以使用simplejson的dumps方法了。
simplejson.dumps([status for status in result], cls=ComplexEncoder,)
这里,同样有一个地方要注意一下,如果是一个记录集,那么就可以直接使用dumps了,如果是多个记录集,那么要for一下,搞成一个list作为参数,dumps出来的也同样是一个json的list了。
就是这么多,我想,我暂时性的解决这些问题了,因为现在还没有发现有什么问题。
PS:在Python中,for实在是太强大了。

没有评论:

发表评论

Mastodon Daily(2024-04-29)

2024-04-28 从一个简单的字节进制转换来聊聊如何编写 Node.js 包 ...