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

답변:


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)"여야합니다. 그 외에도 완벽합니다. 감사!
arikfr 2009

1
이 코드는 UserProperty를 만나면 실패합니다. 오류를 발생시키는 대신 마지막 else에서 "output [key] = str (value)"를 수행하여 문제를 해결했습니다.
Boris Terzic

1
좋은 물건. 작은 개선점은 "prop"를 사용하지 않기 때문에 대신 iterkeys ()를 사용하는 것입니다.
PEZ

7
가능한 모든 유형 (날짜, GeoPt, ...)을 시도하지는 않았지만 데이터 저장소에 정확히이 방법이있는 것 같으며 지금까지 문자열과 정수에 대해 작동하고 있습니다. developers.google.com/appengine/ docs / python / datastore /… 그래서 json으로 직렬화하기 위해 바퀴를 재발 명 json.dumps(db.to_dict(Photo))
해야할지 모르겠습니다

@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) is not JSON serializable
givp

안녕하세요, 문제를 지적 해 주셔서 감사합니다. 해결책은 날짜 개체를 문자열로 변환하는 것입니다. 예를 들어 "unicode ()"로 "getattr (self, p)"호출을 래핑 할 수 있습니다. 이것을 반영하기 위해 코드를 편집했습니다.
mtgred

1
db.Model의 메타 필드를 제거하려면 다음을 사용하십시오. dict ([(p, unicode (getattr (self, p))) for p in self.properties () if not p.startswith ( "_")])
Wonil

ndb의 경우 fredva의 답변을 참조하십시오.
켄지 노구치

self.properties ()는 나를 위해 작동하지 않았습니다. self._properties를 사용했습니다. 전체 줄 : return dict ([(p, unicode (getattr (self, p))) for p in self._properties])
Eyal Levin

15

App Engine SDK의 최신 (1.5.2) 릴리스 to_dict()에서는 모델 인스턴스를 사전으로 변환 하는 함수가 db.py. 릴리스 정보를 참조하십시오 .

아직 문서에는이 기능에 대한 참조가 없지만 직접 시도했으며 예상대로 작동합니다.


이것이 제거되었는지 궁금합니다. 나는 내가 사용할 AttributeError: 'module' object has no attribute 'to_dict'때 얻습니다 (여기서 r은 db.Model 하위 클래스의 인스턴스입니다). google_appengine / google / appengine / ext / db / *에 "to_dict"가 표시되지 않습니다from google.appengine.ext import dbsimplejson.dumps(db.to_dict(r))
idbrii

1
"db.to_dict (ObjectOfClassModel)"처럼 사용해야합니다.
Dmitry Dushkin 2013

2
ndb 객체의 경우 self.to_dict ()가 작업을 수행합니다. 표준 json 모듈로 클래스를 직렬화 할 수 있도록하려면 'def default (self, o) : return o.to_dict ()`를 클래스에 추가하십시오
Kenji Noguchi

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 문자열로 날짜 ( 이 제안에 따라 ),
  • 속성의 dict로서의 모델,
  • 사용자를 이메일로

날짜를 디코딩하려면이 자바 스크립트를 사용할 수 있습니다.

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

참고 : 이 코드를 더 읽기 쉽게 편집 한 사용자 pydave 에게 감사드립니다 . 나는 원래 파이썬의 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을 파이썬 객체로 바꿀 것입니다), 여전히 파이썬 객체를 직접 직렬화 할 수 있습니다.

simplejson 사용 :

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

1
예,하지만 모든 모델에 대해이 작업을 수행하고 싶지는 않습니다. 확장 가능한 접근 방식을 찾으려고합니다.
user111677 09 년

오 그리고 이것에 대한 모범 사례를 찾을 수 없다는 것에 정말 놀랐습니다. 나는 생각했다 애플리케이션 엔진 모델 + RPC + JSON 주어진했다 ...
user111677

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

이 기사는 또한 스펙트럼의 다른 쪽 끝에서 포함 풍요롭게이의를 장고 (그리고 필요 않는 복잡한 시리얼 클래스 _meta- 있는지 왜 _meta 아마도 버그가 설명 누락에 대한 오류를 얻고되지 여기 ) 계산 직렬화 할 수있는 능력을 속성 / 방법. 직렬화가 필요한 대부분의 경우 그 중간에 위치하며 @David Wilson과 같은 내성적 접근 방식이 더 바람직 할 수 있습니다.


3

장고를 프레임 워크로 사용하지 않더라도 해당 라이브러리를 계속 사용할 수 있습니다.

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

serializers.serialize ( "json", ...)를 의미 했습니까? 그러면 "AttributeError : 'Photo'object has no attribute '_meta'"가 발생합니다. 참고로-serializers.serialize ( "xml", Photo.objects.all ())에서 "AttributeError : type object 'Photo'has no attribute 'objects'"가 발생합니다. serializers.serialize ( "xml", Photo.all ())에서 "SerializationError : 직렬화 중에 발생한 비 모델 객체 (<class 'model.Photo'>)"가 발생합니다.
user111677 09 년

2

app-engine-patch 를 사용 하면 자동으로 _meta속성을 선언 한 다음 django.core.serializersdjango 모델에서 일반적으로 하는 것처럼 사용할 수 있습니다 (sledge의 코드에서와 같이).

App-engine-patch에는 하이브리드 인증 (django + google 계정)과 같은 다른 멋진 기능이 있으며 django의 관리자 부분이 작동합니다.


app-engine-patch 대 google-app-engine-django 대 app Engine 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 인코더 클래스를 확장했습니다 .

  • 쿼리 결과 속성 (예 : car.owner_set)
  • ReferenceProperty-재귀 적으로 JSON으로 변환
  • 필터링 속성-가있는 속성 만 verbose_nameJSON으로 인코딩됩니다.

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

예, 그리고 Model에 "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

1

이러한 API (google.appengine.ext.db)는 더 이상 권장되지 않습니다. 이러한 API를 사용하는 앱은 App Engine Python 2 런타임에서만 실행할 수 있으며 App Engine Python 3 런타임으로 마이그레이션하기 전에 다른 API 및 서비스로 마이그레이션해야합니다. 자세한 내용은 여기를 클릭하십시오.


0

Datastore 모델 인스턴스를 직렬화하려면 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 Datastore 모델 인스턴스를 JSON으로 변환하는 것입니다. 솔루션은 Python 사전을 JSON으로 변환하는 것뿐입니다
조정 됨

@tunedconsulting json.dumps를 사용하여 데이터 저장소 모델 인스턴스를 직렬화하지 않았지만 모든 개체에서 작동 할 것이라고 가정합니다. 문서에 json.dumps가 객체를 매개 변수로 사용한다고 명시되어 있지 않은 경우 버그 보고서를 제출해야합니다. 그것은 2009 년에 존재하지 않았다는 re comment만으로 주석으로 추가되었습니다.이 답변은 약간 구식으로 보이지만 작동하지 않으면 제거하게되어 기쁩니다.
HMR 2014 년

1
엔티티 객체 또는 모델 클래스 를 json.dumps하려고 하면 TypeError : 'is not JSON serializable'<Object at 0x0xxxxxx>가 표시됩니다. GAE의 데이터 저장소에는 자체 데이터 유형이 있습니다 (예 : 날짜). 테스트되고 작동하는 현재 정답은 문제가있는 데이터 유형을 직렬화 가능한 데이터 유형으로 변환하는 dmw의 답입니다.
조정

@tunedconsulting 이에 대한 귀하의 의견에 감사 드리며, 제 답변을 업데이트하겠습니다.
HMR 2014 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.