Google AppEngineモデルの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 2009

1
素晴らしいもの。小さな改善点は、そこで「prop」を使用しないため、代わりにiterkeys()を使用することです。
PEZ 2010年

7
考えられるすべてのタイプ(date、GeoPt、...)を試したわけではありませんが、データストアにはまさにこのメソッドがあり、これまでのところ、文字列と整数に対して機能しているようです:developers.google.com/appengine/ docs / python / datastore /…だから、jsonにシリアル化するためにホイールを再発明する必要があるかどうかはjson.dumps(db.to_dict(Photo))
わかりません

@gentimoutonそのメソッドは新しい追加です。それは確かに2009年には存在しませんでした
dmw 2013

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年

こんにちは、問題を指摘していただきありがとうございます。解決策は、日付オブジェクトを文字列に変換することです。たとえば、「getattr(self、p)」の呼び出しを「unicode()」でラップできます。これを反映するようにコードを編集しました。
mtgred 2010年

1
db.Modelのメタフィールドを削除するには、次を使用します。dict([(p、unicode(getattr(self、p)))for p in self.properties()if not p.startswith( "_")])
Wonil

ndbについては、fredvaの回答を参照してください。
野口健二2013

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'ときに取得しfrom google.appengine.ext import dbますsimplejson.dumps(db.to_dict(r))(ここで、rはdb.Modelサブクラスのインスタンスです)。google_appengine / google / appengine / ext / db / *に「to_dict」が表示されない
idbrii 2011

1
「db.to_dict(ObjectOfClassModel)」のように使用する必要があります
Dmitry Dushkin

2
ndbオブジェクトの場合、self.to_dict()がその役割を果たします。標準のjsonモジュールでクラスをシリアル化できるようにする場合は、「def default(self、o):return o.to_dict()」をクラスに追加します
野口健二

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)

これはエンコードします:

  • アイソフォーマット文字列としての日付(この提案による)、
  • その特性の口述としてのモデル、
  • 彼の電子メールとしてのユーザー。

日付をデコードするには、次の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
はい。ただし、すべてのモデルでこれを行う必要はありません。スケーラブルなアプローチを見つけようとしています。
user111677 2009年

ああ、私はこれに関するベストプラクティスを見つけることができないことに本当に驚いています。私は... App Engineのモデル+ 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())})

この記事には、スペクトルの反対側に、計算されたシリアル化機能を備えたdjangoを強化する複雑な_metaシリアライザークラスも含まれています(必要です-_metaの欠落に関するエラーが発生する理由がわかりません、おそらくここで説明されているバグ)。プロパティ/メソッド。ほとんどの場合、シリアル化の必要性はその中間にあり、@ DavidWilsonのような内省的なアプローチが望ましい場合があります。


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:type object'Photo 'has no attribute'objects'"をスローします。serializers.serialize( "xml"、Photo.all())は、「SerializationError:シリアル化中に非モデルオブジェクト(<class'model.Photo '>)が見つかりました」をスローします。
user111677 2009年

2

app-engine-patchを使用すると、_meta属性が自動的に宣言され、django.core.serializersdjangoモデルで通常行うように(sledgeのコードのように)使用できます。

App-engine-patchには、ハイブリッド認証(django + googleアカウント)などの他の優れた機能がいくつかあり、djangoの管理部分は機能します。


app-engine-patchとgoogle-app-engine-djangoとappengine python sdkに付属のdjangoバージョンの違いは何ですか?私が理解していることから、app-engine-patchはより完全ですか?
user111677 2009年

アプリエンジンでdjangoのバージョンを試したことはありませんが、そのまま統合されていると思います。google-app-engine-djangoは、私が間違っていなければ、djangoのモデルをapp-engineで動作させようとします(いくつかの制限があります)。app-engine-patchはapp-engineモデルを直接使用し、マイナーなものをいくつか追加するだけです。彼らのウェブサイトで2つの間の比較があります。
mtourne 2009年

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_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でも機能します。
ニックパーキンス

1

すべてのModelクラスに対して定義されたメソッド「Model.properties()」があります。それはあなたが求めるdictを返します。

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ランタイムでのみ実行でき、App Engine Python3ランタイムに移行する前に他のAPIとサービスに移行する必要があります。詳細については、ここをクリックしてください


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からの答えです。
2014年

@tunedconsultingこれについてのご意見ありがとうございます、私は私の答えを更新します。
HMR 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.