Google App Engine 模型的 JSON 序列化
Posted
技术标签:
【中文标题】Google App Engine 模型的 JSON 序列化【英文标题】:JSON serialization of Google App Engine models 【发布时间】:2010-12-04 15:04:23 【问题描述】:我已经搜索了很长时间没有成功。我的项目没有使用 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')
【问题讨论】:
【参考方案1】:即使您不使用 django 作为框架,这些库仍然可供您使用。
from django.core import serializers
data = serializers.serialize("xml", Photo.objects.all())
【讨论】:
您是说 serializers.serialize("json", ...) 吗?这会引发“AttributeError:'Photo' 对象没有属性 '_meta'”。仅供参考 - serializers.serialize("xml", Photo.objects.all()) 抛出“AttributeError: type object 'Photo' has no attribute 'objects'”。 serializers.serialize("xml", Photo.all()) 抛出“SerializationError: Non-model object (您不需要编写自己的“解析器”(解析器可能会将 JSON 转换为 Python 对象),但您仍然可以自己序列化您的 Python 对象。
使用simplejson:
import simplejson as json
serialized = json.dumps(
'filename': self.filename,
'title': self.title,
'date_taken': date_taken.isoformat(),
# etc.
)
【讨论】:
是的,但我不想对每个模型都这样做。我正在尝试找到一种可扩展的方法。 哦,我真的很惊讶我找不到任何最佳实践。我认为应用引擎模型 + rpc + json 是给定的......【参考方案3】:一个简单的递归函数可用于将实体(和任何引用对象)转换为嵌套字典,该字典可以传递给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
【讨论】:
代码中有一个小错误:你有“output[key] = to_dict(model)”的地方应该是:“output[key] = to_dict(value)”。除此之外它是完美的。谢谢! 此代码在遇到 UserProperty 时会失败。我在最后的 else 中使用“output[key] = str(value)”来解决它,而不是引发错误。 好东西。小的改进是使用 iterkeys() 代替,因为你不在那里使用“prop”。 我还没有尝试过所有可能的类型(日期、GeoPt、...),但似乎数据存储区正是这种方法,到目前为止,它一直在为我工作字符串和整数: developers.google.com/appengine/docs/python/datastore/…所以我不确定你是否需要重新发明***才能序列化为 json:json.dumps(db.to_dict(Photo))
@gentimouton 该方法是新增的。它在 2009 年肯定不存在【参考方案4】:
对于简单的案例,我喜欢文末here提倡的做法:
# after obtaining a list of entities in some way, e.g.:
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 丢失的错误,也许是 @987654322 描述的错误@) 具有序列化计算属性/方法的能力。大多数情况下,您的序列化需要介于两者之间,对于那些人来说,@David Wilson 之类的内省方法可能更可取。
【讨论】:
【参考方案5】:如果您使用app-engine-patch,它将自动为您声明_meta
属性,然后您可以像在django 模型上通常那样使用django.core.serializers
(如在sledge 的代码中)。
App-engine-patch 有一些其他很酷的功能,例如混合身份验证(django + google 帐户),并且 django 的管理部分有效。
【讨论】:
app-engine-patch vs google-app-engine-django vs app engine python sdk附带的django版本有什么区别?据我了解,app-engine-patch 更完整? 我还没有在应用引擎上尝试过django的版本,但我认为它是按原样集成的。如果我没记错的话,google-app-engine-django 会尝试让 django 的模型与 app-engine 一起工作(有一些限制)。 app-engine-patch 直接使用 app-engine 模型,他们只是添加了一些次要的东西。他们的网站上有两者之间的比较。【参考方案6】:这是我找到的最简单的解决方案。它只需要 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]))
【讨论】:
嘿,谢谢你的提示。这很好用,除了我似乎无法序列化日期字段。我得到:TypeError: datetime.datetime(2010, 5, 1, 9, 25, 22, 891937) is not JSON serializable 您好,感谢您指出问题。解决方案是将日期对象转换为字符串。例如,您可以使用“unicode()”包装对“getattr(self, p)”的调用。我编辑了代码以反映这一点。 要删除 db.Model 的元字段,请使用:dict([(p, unicode(getattr(self, p))) for p in self.properties() if not p.startswith(" _")]) 对于 ndb,请参阅 fredva 的回答。 self.properties() 对我不起作用。我使用了self._properties。整行:return dict([(p, unicode(getattr(self, p))) for p in self._properties])【参考方案7】:为所有模型类定义了一个方法“Model.properties()”。它返回你寻找的字典。
from django.utils import simplejson
class Photo(db.Model):
# ...
my_photo = Photo(...)
simplejson.dumps(my_photo.properties())
请参阅文档中的 Model properties。
【讨论】:
某些对象不是“JSON 可序列化”:TypeError: <google.appengine.ext.db.StringProperty object at 0x4694550> is not JSON serializable
【参考方案8】:
要序列化模型,请添加自定义 json 编码器,如以下 python 中所示:
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)
这将编码:
作为等格式字符串 (per this suggestion) 的日期, 模型作为其属性的字典, 一个用户作为他的电子邮件。要解码日期,您可以使用此 javascript:
function decodeJsonDate(s)
return new Date( s.slice(0,19).replace('T',' ') + ' GMT' );
// Note that this function truncates milliseconds.
注意:感谢用户pydave 编辑此代码使其更具可读性。我最初使用 python 的 if/else 表达式以更少的行表达jsonEncoder
,如下所示:(我添加了一些 cmets 并使用了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)
【讨论】:
【参考方案9】:上面 Mtgred 的回答对我来说非常有用——我稍微修改了一下,这样我也可以获得条目的密钥。没有那么几行代码,但它给了我唯一的密钥:
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
【讨论】:
【参考方案10】:在最新 (1.5.2) 版本的 App Engine SDK 中,db.py
中引入了将模型实例转换为字典的 to_dict()
函数。请参阅release notes。
目前在文档中还没有提到这个函数,但我自己试过了,它可以按预期工作。
【讨论】:
不知道这是否已被删除?当我from google.appengine.ext import db
并使用 simplejson.dumps(db.to_dict(r))
(其中 r 是 db.Model 子类的实例)时,我得到 AttributeError: 'module' object has no attribute 'to_dict'
。我在 google_appengine/google/appengine/ext/db/* 中没有看到“to_dict”
它必须像“db.to_dict(ObjectOfClassModel)”一样使用
对于 ndb 对象, self.to_dict() 完成了这项工作。如果你想让类被标准的 json 模块序列化,添加 'def default(self, o): return o.to_dict()` 到类【参考方案11】:
我已经扩展了dpatru写的JSON Encoder类来支持:
查询结果属性(例如 car.owner_set) ReferenceProperty - 递归地将其转换为 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)
【讨论】:
【参考方案12】:正如https://***.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 上还有一个“to_dict”……这个函数是让整个问题变得尽可能简单的关键。它甚至适用于具有“结构化”和“重复”属性的 NDB!【参考方案13】:要序列化数据存储模型实例,您不能使用 json.dumps(尚未测试,但 Lorenzo 指出了这一点)。也许在未来以下会起作用。
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)
【讨论】:
问题是关于将 AppEngine 数据存储模型实例转换为 JSON。您的解决方案只是将 Python 字典转换为 JSON @tunedconsulting 我没有尝试使用 json.dumps 序列化数据存储模型实例,但假设它适用于任何对象。如果文档中没有说明 json.dumps 将对象作为参数,则应提交错误报告。它被添加为评论,仅重新评论它在 2009 年不存在。添加此答案是因为它似乎有点过时,但如果它不起作用,那么我很乐意将其删除。 如果你尝试 json.dumps 一个 entity 对象或 model 类,你会得到 TypeError: 'is not JSON serializable' @tunedconsulting 感谢您对此的意见,我会更新我的答案。【参考方案14】:不再推荐使用这些 API (google.appengine.ext.db)。使用这些 API 的应用只能在 App Engine Python 2 运行时中运行,并且需要先迁移到其他 API 和服务,然后才能迁移到 App Engine Python 3 运行时。 了解更多:click here
【讨论】:
以上是关于Google App Engine 模型的 JSON 序列化的主要内容,如果未能解决你的问题,请参考以下文章
在 python Google App Engine 中,如何将模型的所有实体导出到 Google Storage 中的文件以供开发人员使用?
使用搜索 API Python - Google App Engine 大表
当一个人创建一个新模型时,应该在哪里放置代码以在 Google App Engine/Django 上自动增加一个分片计数器?
将Google App Engine与Javascript客户端连接
在Google App Engine中,Python DataStore模型是get_by_key_name的结果( )以与输入相同的顺序?