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属性才会被编码为JSON

     class 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)