Google App Engine模型的JSON序列化


86

我已经搜索了很长时间,但没有成功。我的项目未使用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')

Answers:


62

一个简单的递归函数可用于将一个实体(和任何引用对象)转换为可传递给的嵌套字典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

2
代码中有一个小错误:如果您有“ output [key] = to_dict(model)”,则应为:“ output [key] = to_dict(value)”。除此之外,它是完美的。谢谢!
09年

1
遇到UserProperty时,此代码将失败。最后,我通过做“ output [key] = str(value)”来解决此问题,而不是引发错误。
Boris Terzic

1
好东西。小改进是使用iterkeys(),因为您在那里没有使用“ prop”。
PEZ 2010年

7
我尚未尝试所有可能的类型(日期,GeoPt等),但似乎数据存储区正是使用此方法,到目前为止,它对我来说一直在处理字符串和整数:developers.google.com/appengine/ docs / python / datastore / ...所以我不确定您是否需要重新发明轮子以序列化为json:json.dumps(db.to_dict(Photo))
gentimouton 2012年

@gentimouton该方法是新添加的。它肯定在2009
-dmw

60

这是我找到的最简单的解决方案。它只需要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)不是JSON可序列化的
givp,2010年

您好,感谢您指出问题。解决方案是将日期对象转换为字符串。例如,您可以使用“ unicode()”包装对“ getattr(self,p)”的调用。我编辑了代码以反映这一点。
mtgr

1
要删除db.Model的元字段,请使用以下命令:dict([((p,unicode(getattr(self,p))))for self.properties()中的p,如果不是p.startswith(“ _”)])
Wonil

对于ndb,请参阅fredva的答案。
野口健二

self.properties()对我不起作用。我使用了self._properties。全行:返回dict([[(p,unicode(getattr(self,p,)))for self._properties]中的p))
Eyal Levin

15

在最新版本(1.5.2)的App Engine SDK中,to_dict()引入了将模型实例转换为词典的功能db.py。请参阅发行说明

到目前为止,在文档中还没有对该功能的引用,但是我自己尝试过该功能,并且可以正常使用。


我想知道是否已删除?我拿到AttributeError: 'module' object has no attribute 'to_dict'的时候我from google.appengine.ext import db和使用simplejson.dumps(db.to_dict(r))(其中r是db.Model子类的实例)。我在google_appengine / google / appengine / ext / db / *
中看

1
它必须像“ db.to_dict(ObjectOfClassModel)”一样使用
Dmitry Dushkin

2
对于一个ndb对象,self.to_dict()可以完成这项工作。如果您想通过标准json模块使该类可序列化,请添加'def default(self,o):将o.to_dict()`返回给该类
Noguchi Kenji,

7

要序列化模型,请在以下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)

这将编码:

  • 日期作为isoformat字符串(根据此建议),
  • 一个模型作为其属性的决定,
  • 用户作为他的电子邮件。

要解码日期,您可以使用以下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更少的行表示如下:(我添加了一些注释,并使用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)

4

您不需要编写自己的“解析器”(解析器可能会将JSON转换为Python对象),但是您仍然可以自己序列化Python对象。

使用simplejson

import simplejson as json
serialized = json.dumps({
    'filename': self.filename,
    'title': self.title,
    'date_taken': date_taken.isoformat(),
    # etc.
})

1
是的,但我不想为每个模型都这样做。我正在尝试找到一种可扩展的方法。
2009年

哦,我对此感到找不到任何最佳做法,真让我感到惊讶。我认为应用引擎模型+ rpc + json是给定的……
user111677,2009年

4

对于简单的情况下,我喜欢这种方法主张在这里在文章的结尾:

  # 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缺失的错误,也许是此处描述的错误)以及对计算进行序列化的能力。属性/方法。在大多数情况下,序列化需求介于两者之间,对于那些内省的方法(例如@David Wilson's)可能更可取。


3

即使您没有使用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:类型对象'Photo'没有属性'objects'”。serializers.serialize(“ xml”,Photo.all())引发“序列化过程中遇到的SerializationError:非模型对象(<class'model.Photo'>)”。
user111677

2

如果您使用app-engine-patch,它将自动_meta为您声明属性,然后您可以使用django.core.serializers像在Django模型上一样使用(如在sledge的代码中一样)。

App-engine-patch具有其他一些很酷的功能,例如具有混合身份验证(django + google帐户),以及django作品的管理部分。


app-engine-patch与google-app-engine-django与app引擎python sdk随附的django版本之间有什么区别?据我了解,app-engine-patch更完整吗?
user111677

我尚未在应用程序引擎上尝试过django的版本,但我认为它已集成。如果我没记错的话,google-app-engine-django会尝试使django的模型与app-engine一起使用(有一些限制)。app-engine-patch直接使用app-engine模型,它们只是向其中添加一些次要的东西。他们的网站上有两者之间的比较。
mtourne

2

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

2

我扩展了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)
    

2

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

是的,并且在模型上还有一个“ to_dict” ...此功能是使整个问题尽可能平凡的关键。它甚至适用于具有“结构化”和“重复”属性的NDB!
Nick Perkins

1

有一个为所有Model类定义的方法“ Model.properties()”。它返回您要查找的字典。

from django.utils import simplejson
class Photo(db.Model):
  # ...

my_photo = Photo(...)
simplejson.dumps(my_photo.properties())

请参阅文档中的模型属性


某些对象不是“ JSON可序列化”的:TypeError: <google.appengine.ext.db.StringProperty object at 0x4694550> is not JSON serializable
idbrii,2011年

1

不再建议使用这些API(google.appengine.ext.db)。使用这些API的应用只能在App Engine Python 2运行时中运行,并且需要先迁移到其他API和服务,然后才能迁移到App Engine Python 3运行时。要了解更多信息:单击此处


0

要序列化数据存储模型实例,您不能使用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年不存在。添加了这个答案,因为它似乎有些过时了,但是如果它不起作用,那么我很乐意将其删除。
HMR 2014年

1
如果您尝试json.dumps实体对象或模型类,则会得到TypeError:“不是JSON可序列化的” <对象位于0x0xxxxxx>。GAE的数据存储区拥有自己的数据类型(例如,日期)。dmw公司目前的正确答案(已测试且有效)是dmw的答案,它将某些有问题的数据类型转换为可序列化的数据类型。
2014年

@tunedconsulting谢谢您的输入,我将更新我的答案。
HMR 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.