Google App Engine模型的JSON序列化
我一直在寻找相当一段时间没有成功。 我的项目没有使用Django,有没有一种简单的方法来将App Engine模型(google.appengine.ext.db.Model)序列化为JSON,还是需要编写自己的序列化程序?
模型:
class Photo(db.Model): filename = db.StringProperty() title = db.StringProperty() description = db.StringProperty(multiline=True) date_taken = db.DateTimeProperty() date_uploaded = db.DateTimeProperty(auto_now_add=True) album = db.ReferenceProperty(Album, collection_name='photo')
一个简单的recursion函数可以用来将一个实体(以及任何指示)转换为一个可以传递给simplejson
的嵌套字典:
import datetime import time SIMPLE_TYPES = (int, long, float, bool, dict, basestring, list) def to_dict(model): output = {} for key, prop in model.properties().iteritems(): value = getattr(model, key) if value is None or isinstance(value, SIMPLE_TYPES): output[key] = value elif isinstance(value, datetime.date): # Convert date/datetime to MILLISECONDS-since-epoch (JS "new Date()"). ms = time.mktime(value.utctimetuple()) * 1000 ms += getattr(value, 'microseconds', 0) / 1000 output[key] = int(ms) elif isinstance(value, db.GeoPt): output[key] = {'lat': value.lat, 'lon': value.lon} elif isinstance(value, db.Model): output[key] = to_dict(value) else: raise ValueError('cannot encode ' + repr(prop)) return output
这是我find的最简单的解决scheme。 它只需要3行代码。
只需在模型中添加一个方法来返回字典:
class DictModel(db.Model): def to_dict(self): return dict([(p, unicode(getattr(self, p))) for p in self.properties()])
SimpleJSON现在可以正常工作:
class Photo(DictModel): filename = db.StringProperty() title = db.StringProperty() description = db.StringProperty(multiline=True) date_taken = db.DateTimeProperty() date_uploaded = db.DateTimeProperty(auto_now_add=True) album = db.ReferenceProperty(Album, collection_name='photo') from django.utils import simplejson from google.appengine.ext import webapp class PhotoHandler(webapp.RequestHandler): def get(self): photos = Photo.all() self.response.out.write(simplejson.dumps([p.to_dict() for p in photos]))
在App Engine SDK的最新(1.5.2)版本中, to_dict()
中引入了将模型实例转换为字典的to_dict()
函数。 请参阅发行说明 。
目前在文档中没有提到这个函数,但是我自己尝试过了,并且按照预期工作。
要序列化模型,像下面的python一样添加一个自定义的json编码器:
import datetime from google.appengine.api import users from google.appengine.ext import db from django.utils import simplejson class jsonEncoder(simplejson.JSONEncoder): def default(self, obj): if isinstance(obj, datetime.datetime): return obj.isoformat() elif isinstance(obj, db.Model): return dict((p, getattr(obj, p)) for p in obj.properties()) elif isinstance(obj, users.User): return obj.email() else: return simplejson.JSONEncoder.default(self, obj) # use the encoder as: simplejson.dumps(model, cls=jsonEncoder)
这将编码:
- 一个date,如isoformatstring( 根据这个build议 ),
- 一个模型作为其性质的字典,
- 用户作为他的电子邮件。
要解码date,你可以使用这个javascript:
function decodeJsonDate(s){ return new Date( s.slice(0,19).replace('T',' ') + ' GMT' ); } // Note that this function truncates milliseconds.
注意:感谢编辑此代码的用户pydave ,使其更具可读性。 我本来用python的if / elseexpression式来表示jsonEncoder
的行数较less,如下所示:(我添加了一些注释并使用了google.appengine.ext.db.to_dict
,使它比原来的更清晰)
class jsonEncoder(simplejson.JSONEncoder): def default(self, obj): isa=lambda x: isinstance(obj, x) # isa(<type>)==True if obj is of type <type> return obj.isoformat() if isa(datetime.datetime) else \ db.to_dict(obj) if isa(db.Model) else \ obj.email() if isa(users.User) else \ simplejson.JSONEncoder.default(self, obj)
你不需要编写你自己的“parsing器”(parsing器大概会把JSON变成一个Python对象),但是你仍然可以自己序列化你的Python对象。
使用simplejson :
import simplejson as json serialized = json.dumps({ 'filename': self.filename, 'title': self.title, 'date_taken': date_taken.isoformat(), # etc. })
对于简单的情况,我喜欢这篇文章最后提出的方法:
# after obtaining a list of entities in some way, eg: user = users.get_current_user().email().lower(); col = models.Entity.gql('WHERE user=:1',user).fetch(300, 0) # ...you can make a json serialization of name/key pairs as follows: json = simplejson.dumps(col, default=lambda o: {o.name :str(o.key())})
文章的另一端还包含一个复杂的序列化类,该序列化类丰富了django的(并且确实需要_meta
– 不知道为什么你会得到关于_meta缺失的错误,也许这里描述的错误)计算的属性/方法。 大多数情况下,你的序列化需要介于两者之间,对于像“大卫·威尔逊”这样的内省方法,可能更可取。
即使你不使用django作为框架,这些库仍然可供你使用。
from django.core import serializers data = serializers.serialize("xml", Photo.objects.all())
如果你使用app-engine-patch,它会自动为你声明_meta
属性,然后你可以使用django.core.serializers
就像你在django模型上做的那样(就像雪橇的代码一样)。
App-engine-patch还有一些很酷的function,例如混合身份validation(django + google账户),以及django的pipe理部分。
上面的答案对我来说非常合适 – 我稍微修改了一下,所以我也可以得到这个入口的关键。 不是几行代码,但它给了我唯一的关键:
class DictModel(db.Model): def to_dict(self): tempdict1 = dict([(p, unicode(getattr(self, p))) for p in self.properties()]) tempdict2 = {'key':unicode(self.key())} tempdict1.update(tempdict2) return tempdict1
我已经扩展了由dpatru编写的JSON编码器类来支持:
- 查询结果属性(例如car.owner_set)
- ReferenceProperty – recursion地把它变成JSON
-
筛选属性 – 只有具有
verbose_name
属性才会被编码为JSONclass DBModelJSONEncoder(json.JSONEncoder): """Encodes a db.Model into JSON""" def default(self, obj): if (isinstance(obj, db.Query)): # It's a reference query (holding several model instances) return [self.default(item) for item in obj] elif (isinstance(obj, db.Model)): # Only properties with a verbose name will be displayed in the JSON output properties = obj.properties() filtered_properties = filter(lambda p: properties[p].verbose_name != None, properties) # Turn each property of the DB model into a JSON-serializeable entity json_dict = dict([( p, getattr(obj, p) if (not isinstance(getattr(obj, p), db.Model)) else self.default(getattr(obj, p)) # A referenced model property ) for p in filtered_properties]) json_dict['id'] = obj.key().id() # Add the model instance's ID (optional - delete this if you do not use it) return json_dict else: # Use original JSON encoding return json.JSONEncoder.default(self, obj)
正如https://stackoverflow.com/users/806432/fredva所提到的,to_dict工作得很好。; 这是我使用的代码。
foos = query.fetch(10) prepJson = [] for f in foos: prepJson.append(db.to_dict(f)) myJson = json.dumps(prepJson))
有一个为所有Model类定义的方法“Model.properties()”。 它返回你寻求的字典。
from django.utils import simplejson class Photo(db.Model): # ... my_photo = Photo(...) simplejson.dumps(my_photo.properties())
请参阅文档中的模型属性 。
要序列化数据存储模型实例,你不能使用json.dumps(没有testing,但洛伦佐指出)。 也许在将来,以下将起作用。
http://docs.python.org/2/library/json.html
import json string = json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}]) object = json.loads(self.request.body)
- 您在App Engine上使用了哪些方法进行轻量级Pythonunit testing?
- UnicodeDecodeError:'ascii'编解码器无法解码位置0中的字节0xe0:序号不在范围内(128)
- string值前的'u'符号是什么意思?
- AppEngine bulkloader,高复制存储和python27运行时
- Google云端Bigtable与Google云端数据存储
- 如何在数据存储而不是数据库中思考?
- 使用Python在Google App Engine数据存储中复制实体,而不必在“编译”时知道属性名称
- 无法在Intellij IDEA中设置Java断点
- Google地理编码API – REQUEST_DENIED