2009-10-07 20 views
86

我一直在尋找相當一段時間,但沒有成功。我的項目沒有使用Django,有沒有簡單的方法將App Engine模型(google.appengine.ext.db.Model)序列化爲JSON,還是需要編寫自己的序列化程序?Google App引擎模型的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') 

回答

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)」。 除此之外它是完美的 Thanks! – arikfr 2009-11-07 22:02:11

+1

這段代碼在遇到UserProperty時會失敗,我用「output [key] = str )「在最後else,而不是提出錯誤 – 2009-12-15 21:03:21

+1

好東西。小改進是使用iterkeys(),因爲你不使用」prop「。 – PEZ 2010-05-06 12:15:38

3

即使你不使用Django作爲一個框架,這些庫仍然可以供您使用。

from django.core import serializers 
data = serializers.serialize("xml", Photo.objects.all()) 
+0

您是不是要找serializers.serialize( 「JSON」,...)?拋出「AttributeError:'照片'對象沒有屬性'_meta'」。 FYI - serializers.serialize(「xml」,Photo.objects.all())拋出「AttributeError:type object'Photo'沒有屬性'objects'」。 serializers.serialize(「xml」,Photo.all())拋出「SerializationError:非模型對象()在序列化過程中遇到「 – user111677 2009-10-07 13:57:26

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

是的,但我不想爲每個模型都做這件事,我試圖找到一個可擴展的方法 – user111677 2009-10-07 14:07:23

+0

哦,我真的很驚訝,我不能我認爲應用程序引擎模型+ rpc + json是給定的... – user111677 2009-10-07 14:13:34

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缺失的錯誤,或許是描述了here的錯誤)序列化計算的屬性/方法。大多數情況下,你的序列化需求介於兩者之間,對於像@David Wilson's這樣的內省方法可能更可取。

2

如果使用app-engine-patch它會自動申報_meta屬性給你,然後你可以使用django.core.serializers,你通常會在Django模型做(如雪橇的代碼)。

應用引擎補丁還有其他一些很酷的功能,例如混合身份驗證(django + Google帳戶)以及django的管理部分。

+0

app-engine-patch與google-app-engine-django相比與應用引擎python sdk一起發佈的django版本有什麼區別?據我所知,app-engine-patch更完整? – user111677 2009-10-08 02:46:52

+0

我還沒有嘗試應用引擎上的Django版本,但我認爲它是按原樣集成的。如果我沒有弄錯,google-app-engine-django會試圖讓django的模型與app-engine一起工作(有一些限制)。應用程序引擎補丁直接使用應用程序引擎模型,他們只是添加一些極少量的東西。他們的網站上有兩個比較。 – mtourne 2009-10-08 17:01:36

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])) 
+0

嘿謝謝你的提示。這工作很好,除了我似乎無法序列化日期字段。我得到: TypeError:datetime.datetime(2010,5,1,9,25,22,891937)不是JSON可序列化 – givp 2010-05-01 09:50:24

+0

嗨,感謝您指出問題。 解決方法是將日期對象轉換爲字符串。例如,您可以用「unicode()」將調用包裝爲「getattr(self,p)」。我編輯了代碼來反映這一點。 – mtgred 2010-05-22 14:07:36

+0

非常酷的答案,仍然在8月'11相關。 :弓: – Sri 2011-08-07 11:21:21

1

有一個方法, 「Model.properties()」,所有定義模型類。它返回你尋求的字典。

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

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

請參閱Model properties在文檔中。

+0

某些對象不是 「JSON序列化的」:'類型錯誤:不是JSON序列化的' – idbrii 2011-08-27 00:13:14

7

要序列模型,添加自定義的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) 

這將編碼:

  • 一個日期作爲isoformat字符串(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如下:(我已經添加了一些意見和使用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) 
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 
15

在App Engine SDK,一個to_dict()函數模型實例轉換爲字典中db.py引入的最新版本(1.5.2)發佈。請參閱release notes

到目前爲止還沒有在文檔中提到這個函數,但我自己試過了,它按預期工作。

+0

我不知道這是否已被刪除?當我從google.appengine.ext導入db'並使用'simplejson.dumps(db.to_dict(r))'時,我得到'AttributeError:'模塊'對象沒有屬性'to_dict'''(其中r是一個db.Model子類)。 我沒有在google_appengine/google/appengine/ext/db/* – idbrii 2011-08-27 00:19:25

+1

中看到「to_dict」,它必須像ndb對象的「db.to_dict(ObjectOfClassModel)」 – Dimitry 2013-04-08 18:57:27

+2

一樣使用,self.to_dict()完成這項工作。如果你想讓這個類可以通過標準的json模塊來序列化,可以在類 – 2013-08-23 05:55:30

2

我已經延長了dpatru寫的JSON編碼器類,以支持:

  • 查詢結果的屬性(如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)) 
+0

上添加'def default(self,o):return o.to_dict()',並且模型中還有一個「to_dict」...這個功能是將整個問題視爲微不足道的關鍵。它甚至適用於具有「結構化」和「重複」屬性的NDB! – 2012-12-29 19:37:58

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

問題是關於將AppEngine數據存儲模型實例轉換爲JSON。您的解決方案僅僅是將Python字典轉換爲JSON – tuned 2014-01-04 12:01:58

+0

@tunedconsulting我還沒有嘗試使用json.dumps序列化數據存儲模型實例,但假設它可以與任何對象一起使用。如果不是文檔聲明json.dumps將對象作爲參數,應該提交錯誤報告。它僅作爲評論添加,僅在2009年不存在。添加此答案是因爲它似乎有點過時,但如果它不起作用,我很樂意將其刪除。 – HMR 2014-01-04 12:10:17

+1

如果你嘗試json.dumps一個_entity_對象或_model_類,你會得到TypeError:'不是JSON serializable'。 GAE的數據存儲具有自己的數據類型(例如日期)。目前正確的答案,測試和工作,是從dmw轉換一些有問題的數據類型爲可序列化的。 – tuned 2014-01-05 20:32:19