Google App Engine modellerinin JSON serileştirmesi


86

Başarısız bir süredir aradım. Projem Django kullanmıyor, App Engine modellerini (google.appengine.ext.db.Model) JSON'a serileştirmenin basit bir yolu var mı yoksa kendi serileştiricimi mi yazmam gerekiyor?

Model:

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

Yanıtlar:


62

Basit bir özyinelemeli fonksiyon, bir varlığı (ve herhangi bir referansı) şunlara iletilebilecek iç içe bir sözlüğe dönüştürmek için kullanılabilir 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
Kodda küçük bir hata var: "output [key] = to_dict (model)" olduğunda, "output [key] = to_dict (value)" olmalıdır. Üstelik mükemmel. Teşekkürler!
arikfr

1
Bu kod, bir UserProperty ile karşılaştığında başarısız olur. Bir hata oluşturmak yerine, finalde "output [key] = str (value)" yaparak bunun etrafında çalıştım.
Boris Terzic

1
Harika şeyler. Küçük bir gelişme, orada "prop" kullanmadığınız için iterkeys () kullanmaktır.
PEZ

7
Mümkün olan tüm türleri (tarih, GeoPt, ...) denemedim, ancak veri deposu tam olarak bu yönteme sahip ve şu ana kadar benim için dizeler ve tamsayılar için çalışıyor gibi görünüyor: developer.google.com/appengine/ docs / python / datastore /… Bu yüzden json.dumps(db.to_dict(Photo))
json'a

@gentimouton Bu yöntem yeni bir eklemedir. 2009'da kesinlikle yoktu
dmw

60

Bulduğum en basit çözüm bu. Yalnızca 3 satır kod gerektirir.

Bir sözlük döndürmek için modelinize bir yöntem eklemeniz yeterlidir:

class DictModel(db.Model):
    def to_dict(self):
       return dict([(p, unicode(getattr(self, p))) for p in self.properties()])

SimpleJSON artık düzgün çalışıyor:

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

hey bahşiş için teşekkürler. tarih alanını seri hale getirememem dışında bu harika çalışıyor. Anladığım: TypeError: datetime.datetime (2010, 5, 1, 9, 25, 22, 891937) JSON serileştirilebilir değil
givp

Merhaba, sorunu işaret ettiğiniz için teşekkürler. Çözüm, tarih nesnesini bir dizeye dönüştürmektir. Örneğin "getattr (self, p)" çağrısını "unicode ()" ile sarmalayabilirsiniz. Bunu yansıtmak için kodu düzenledim.
07'de mtgred

1
Db.Model'in meta alanlarını kaldırmak için şunu kullanın: dict ([(p, unicode (getattr (self, p))) p için self.properties () değilse p.startswith ("_")])
Wonil

ndb için fredva'nın cevabına bakın.
Kenji Noguchi

self.properties () benim için çalışmadı. Self._properties kullandım. Tam satır: dönüş dict ([(p, unicode (getattr (self, p))) p for self._properties])
Eyal Levin

15

App Engine SDK'nın en son (1.5.2) sürümünde, to_dict()model örneklerini sözlüğe dönüştüren bir işlev tanıtıldı db.py. Sürüm notlarına bakın .

Henüz dokümantasyonda bu işleve atıfta bulunulmamaktadır, ancak kendim denedim ve beklendiği gibi çalışıyor.


Bunun kaldırılıp kaldırılmadığını merak ediyorum. Ben ve kullandığımda elde AttributeError: 'module' object has no attribute 'to_dict'ederim (burada r, bir db.Model alt sınıfının bir örneğidir). Google_appengine / google / appengine / ext / db / * içinde "to_dict" görmüyorumfrom google.appengine.ext import dbsimplejson.dumps(db.to_dict(r))
idbrii

1
"db.to_dict (ObjectOfClassModel)" gibi kullanılmalıdır
Dmitry Dushkin

2
bir ndb nesnesi için self.to_dict () işi yapar. Sınıfı standart json modülü tarafından serileştirilebilir hale getirmek istiyorsanız, sınıfa 'def default (self, o): return o.to_dict ()' ekleyin
Kenji Noguchi

7

Modelleri serileştirmek için aşağıdaki python'da olduğu gibi özel bir json kodlayıcı ekleyin:

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)

Bu kodlayacak:

  • izoformat dizesi olarak bir tarih ( bu öneriye göre ),
  • özelliklerinin bir diktesi olarak bir model,
  • e-postası olarak bir kullanıcı.

Tarihin kodunu çözmek için bu javascript'i kullanabilirsiniz:

function decodeJsonDate(s){
  return new Date( s.slice(0,19).replace('T',' ') + ' GMT' );
} // Note that this function truncates milliseconds.

Not: Bu kodu daha okunaklı hale getirmek için düzenleyen kullanıcı pydave'e teşekkürler . Başlangıçta python'un if / else ifadelerini jsonEncoderaşağıdaki gibi daha az satırda ifade etmek için kullanmıştım : (Bazı yorumlar ekledim google.appengine.ext.db.to_dictve orijinalinden daha açık hale getirmek için kullandım .)

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

Kendi "ayrıştırıcınızı" yazmanıza gerek yoktur (bir ayrıştırıcı muhtemelen JSON'u bir Python nesnesine dönüştürecektir), ancak yine de Python nesnenizi kendiniz serileştirebilirsiniz.

Simplejson kullanarak :

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

1
Evet, ama bunu her model için yapmak zorunda kalmak istemiyorum. Ölçeklenebilir bir yaklaşım bulmaya çalışıyorum.
user111677

oh ve bu konuda en iyi uygulamaları bulamadığıma gerçekten şaşırdım. Uygulama motoru modeli + rpc +
json'un

4

Basit durumlar için, makalenin sonunda burada savunulan yaklaşımı beğendim :

  # 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())})

Makale ayrıca, spektrumun diğer ucunda, _metadjango'ları zenginleştiren (ve gerektirir - neden _meta eksikliğiyle ilgili hatalar aldığınızdan emin değilim, belki de burada açıklanan hata ) hesaplanmış serileştirme yeteneği ile karmaşık bir serileştirici sınıfı içerir. özellikler / yöntemler. Serileştirmeye ihtiyaç duyduğunuz çoğu zaman arada bir yerde yatar ve bunlar için @David Wilson gibi introspektif bir yaklaşım tercih edilebilir.


3

Django'yu bir çerçeve olarak kullanmasanız bile, bu kitaplıkları kullanmanız için hala mevcuttur.

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

Serializers.serialize ("json", ...) demek istediniz mi? Bu, "AttributeError:" Photo "nesnesinin" _meta "özniteliği yok" atar. Bilginize - serializers.serialize ("xml", Photo.objects.all ()) "AttributeError: tür nesnesi 'Fotoğraf', 'nesneler' niteliğine sahip değildir". serializers.serialize ("xml", Photo.all ()) "SerializationError: Serileştirme sırasında karşılaşılan model dışı nesne (<class 'model.Photo'>)" atar.
user111677

2

Uygulama motoru yaması kullanırsanız , _metaözniteliği sizin için otomatik olarak bildirir ve sonra kullanabilirsinizdjango.core.serializers normalde django modellerinde yaptığınız gibi (sledge kodunda olduğu gibi) kullanabilirsiniz.

Uygulama motoru yaması, karma bir kimlik doğrulama (django + google hesapları) gibi başka harika özelliklere sahiptir ve django'nun yönetici kısmı çalışır.


app-engine-patch ile google-app-engine-django ile uygulama motoru python sdk ile birlikte gelen django sürümü arasındaki fark nedir? Anladığım kadarıyla uygulama motoru yaması daha eksiksiz mi?
user111677

Django'nun sürümünü uygulama motorunda denemedim, ancak olduğu gibi entegre olduğunu düşünüyorum. google-app-engine-django yanılmıyorsam django'nun modelini app-engine ile çalıştırmaya çalışır (bazı sınırlamalarla). app-engine-patch, doğrudan uygulama motoru modellerini kullanır, sadece ona bazı küçük şeyler eklerler. Web sitelerinde ikisi arasında bir karşılaştırma var.
mtourne

2

Mtgred'in yukarıdaki cevabı benim için harika çalıştı - biraz değiştirdim, böylece girişin anahtarını da alabilirdim. Az sayıda kod satırı değil, ama bana benzersiz anahtarı veriyor:

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 tarafından yazılan JSON Encoder sınıfını şunları desteklemek için genişlettim :

  • Sorgu sonuçları özellikleri (ör. Car.owner_set)
  • ReferenceProperty - özyinelemeli olarak JSON'a dönüştürün
  • Filtreleme özellikleri - yalnızca a içeren özellikler verbose_nameJSON olarak kodlanacaktır

    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'da belirtildiği gibi , to_dict harika çalışıyor. İşte kullandığım kodum.

foos = query.fetch(10)
prepJson = []

for f in foos:
  prepJson.append(db.to_dict(f))

myJson = json.dumps(prepJson))

evet, ve ayrıca Model üzerinde bir "to_dict" vardır ... bu fonksiyon, tüm bu problemi olması gerektiği kadar önemsiz hale getirmenin anahtarıdır. Hatta "yapılandırılmış" ve "tekrarlanan" özelliklere sahip NDB için bile çalışır!
Nick Perkins

1

Tüm Model sınıfları için tanımlanmış "Model.properties ()" adlı bir yöntem vardır. Aradığınız emri verir.

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

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

Belgelerdeki Model özelliklerine bakın .


Bazı nesneler "JSON serileştirilebilir" değildir:TypeError: <google.appengine.ext.db.StringProperty object at 0x4694550> is not JSON serializable
idbrii

1

Bu API'ler (google.appengine.ext.db) artık önerilmemektedir. Bu API'leri kullanan uygulamalar yalnızca App Engine Python 2 çalışma zamanında çalışabilir ve App Engine Python 3 çalışma zamanına geçiş yapmadan önce diğer API'lere ve hizmetlere taşınmaları gerekir. Daha fazlasını öğrenmek için: buraya tıklayın


0

Bir Datastore Modeli örneğini serileştirmek için json.dumps kullanamazsınız (test etmedi ancak Lorenzo bunu gösterdi). Belki gelecekte aşağıdakiler işe yarayacaktır.

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)

soru, bir AppEngine Datastore Modeli örneğini JSON'a dönüştürmekle ilgilidir. Çözümünüz yalnızca bir Python sözlüğünü
ayarlanmıştır

@tunedconsulting Bir Datastore Modeli örneğini json.dumps ile serileştirmeyi denemedim, ancak herhangi bir nesneyle çalışacağını varsayıyorum. Belgelerde json.dumps'ın parametre olarak bir nesneyi aldığını belirtmiyorsa, bir hata raporu gönderilmelidir. Sadece 2009'da var olmadığına dair bir yorum olarak eklenmiştir. Bu yanıtı biraz modası geçmiş göründüğü için ekledim ama işe yaramazsa, o zaman kaldırmaktan mutluluk duyarım.
HMR

1
Bir varlık nesnesini veya bir model sınıfını json.dumps yapmaya çalışırsanız, TypeError: 'JSON serileştirilebilir değil' <Object at 0x0xxxxxx>. GAE'nin Datastore'u kendi veri türlerine sahiptir (örneğin tarihler için). Şu anki doğru cevap, test edilmiş ve çalışıyor, bazı sorunlu veri türlerini serileştirilebilir olanlara dönüştüren dmw'den gelen cevaptır.
ayarlandı

@tunedconsulting Bu konudaki katkınız için teşekkür ederim, cevabımı güncelleyeceğim.
HMR
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.