Как сериализовать объекты db.Model в json? - PullRequest
19 голосов
/ 22 января 2010

При использовании

from django.utils import simplejson

для объектов типов, производных от db.Model, он генерирует исключения. Как это обойти?

Ответы [ 5 ]

14 голосов
/ 24 января 2010

Хорошо - мой python не очень хороший, поэтому любая помощь будет полезна. Вам не нужно писать анализатор - вот решение:

добавить этот класс utlity http://code.google.com/p/google-app-engine-samples/source/browse/trunk/geochat/json.py?r=55

 import datetime  
 import time 

 from google.appengine.api import users 
 from google.appengine.ext import db 

#this is a mod on the orinal file for some reason it includes its own simplejson files i have ref django!
 from django.utils import simplejson  

 class GqlEncoder(simplejson.JSONEncoder): 

   """Extends JSONEncoder to add support for GQL results and properties. 

   Adds support to simplejson JSONEncoders for GQL results and properties by 
   overriding JSONEncoder's default method. 
   """ 

   # TODO Improve coverage for all of App Engine's Property types. 

   def default(self, obj): 

     """Tests the input object, obj, to encode as JSON.""" 

     if hasattr(obj, '__json__'): 
       return getattr(obj, '__json__')() 

     if isinstance(obj, db.GqlQuery): 
       return list(obj) 

     elif isinstance(obj, db.Model): 
       properties = obj.properties().items() 
       output = {} 
       for field, value in properties: 
         output[field] = getattr(obj, field) 
       return output 

     elif isinstance(obj, datetime.datetime): 
       output = {} 
       fields = ['day', 'hour', 'microsecond', 'minute', 'month', 'second', 
           'year'] 
       methods = ['ctime', 'isocalendar', 'isoformat', 'isoweekday', 
           'timetuple'] 
       for field in fields: 
         output[field] = getattr(obj, field) 
       for method in methods: 
         output[method] = getattr(obj, method)() 
       output['epoch'] = time.mktime(obj.timetuple()) 
       return output 

     elif isinstance(obj, time.struct_time): 
       return list(obj) 

     elif isinstance(obj, users.User): 
       output = {} 
       methods = ['nickname', 'email', 'auth_domain'] 
       for method in methods: 
         output[method] = getattr(obj, method)() 
       return output 

     return simplejson.JSONEncoder.default(self, obj) 


 def encode(input): 
   """Encode an input GQL object as JSON 

     Args: 
       input: A GQL object or DB property. 

     Returns: 
       A JSON string based on the input object.  

     Raises: 
       TypeError: Typically occurs when an input object contains an unsupported 
         type. 
     """ 
   return GqlEncoder().encode(input)   

сохранить как json.py

ДЛЯ ИСПОЛЬЗОВАНИЯ

import cgi
import os
import json 

from google.appengine.ext.webapp import template
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db


class Greeting(db.Model):
    author = db.UserProperty()
    content = db.StringProperty(multiline=True)
    date = db.DateTimeProperty(auto_now_add=True)

class MainPage(webapp.RequestHandler):
    def get(self):
        greetings_query = Greeting.all().order('-date')
        greetings = greetings_query.fetch(5)

        if users.get_current_user():
            url = users.create_logout_url(self.request.uri)
            url_linktext = 'Logout'
        else:
            url = users.create_login_url(self.request.uri)
            url_linktext = 'Login'

        template_values = {
            'greetings': greetings,
            'url': url,
            'url_linktext': url_linktext,
            }

        path = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(path, template_values))


class Guestbook(webapp.RequestHandler):
    def post(self):
        greeting = Greeting()

        if users.get_current_user():
            greeting.author = users.get_current_user()

        greeting.content = self.request.get('content')
        greeting.put()
        self.redirect('/')



#here i return my json feed - simple implementaion for example
class FeedHandler(webapp.RequestHandler):

  def get(self):
    """Retrieve a feed"""
    user = None

    greetings_query = Greeting.all().order('-date')
    rs= greetings_query.fetch(5)
#this is the part that calls the encoder - dosnt cause an exception
    data = json.encode(rs)



#roll out to browser -might need to check my headers etc
    self.response.headers['Content-Type'] = 'application/json; charset=utf-8'  
    self.response.out.write(data)




application = webapp.WSGIApplication(
                                       [
                                       ('/', MainPage),
                                       ('/sign',Guestbook),
                                       ('/feed',FeedHandler),
                                       ], debug=True
                                    )

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()

Это ответ браузера:

[{"content": "", "date": {"ctime": "Sat Jan 23 02:40:22 2010", "hour": 2, "isoweekday": 6, "month": 1 , «second»: 22, «microsecond»: 434000, «isocalendar»: [2010, 3, 6], «timetuple»: [2010, 1, 23, 2, 40, 22, 5, 23, -1], "год": 2010, "эпоха": 1264214422.0, "изоформат": "2010-01-23T02: 40: 22.434000", "день": 23, "минута": 40}, "автор": {"псевдоним": "test@example.com", "email": "test@example.com", "auth_domain": "gmail.com"}}, {"content": "", "date": {"ctime": " Сб 23 января 01:12:43 2010 "," час ": 1," isoweekday ": 6," месяц ": 1," секунда ": 43," микросекунда ": 972000," isocalendar ": [2010, 3, 6], «timetuple»: [2010, 1, 23, 1, 12, 43, 5, 23, -1], «year»: 2010, «epoch»: 1264209163.0, «isoformat»: «2010-01-23T01 : 12: 43.972000 "," день ": 23," минута ": 12}," автор ": {" псевдоним ":" test@example.com "," электронная почта ":" test@example.com "," auth_domain " ":" gmail.com "}}, {" content ":" test "," date ": {" ctime ":" Fri Jan 22 22:32:13 2010 "," hour ": 22," isoweekday ": 5, «месяц»: 1, «секунда»: 13, «микросекунда»: 65900 0, «isocalendar»: [2010, 3, 5], «timetuple»: [2010, 1, 22, 22, 32, 13, 4, 22, -1], «year»: 2010, «epoch»: 1264199533.0 , "isoformat": "2010-01-22T22: 32: 13.659000", "day": 22, "minutes": 32}, "author": {"nickname": "test@example.com", "email" : "test@example.com", "auth_domain": "gmail.com"}}, {"content": "", "date": {"ctime": "пт 22 января 22:29:49 2010", "час": 22, "isoweekday": 5, "месяц": 1, "секунда": 49, "микросекунда": 358000, "isocalendar": [2010, 3, 5], "timetuple": [2010, 1 , 22, 22, 29, 49, 4, 22, -1], "год": 2010, "эпоха": 1264199389.0, "изоформат": "2010-01-22T22: 29: 49.358000", "день": 22 , "minute": 29}, "author": {"nickname": "test@example.com", "email": "test@example.com", "auth_domain": "gmail.com"}}, { "content": "ах это работает! \ r \ n", "date": {"ctime": "пт 22 января 22:29:22 2010", "hour": 22, "isoweekday": 5, "month ": 1," секунда ": 22," микросекунда ": 995000," isocalendar ": [2010, 3, 5]," timetuple ": [2010, 1, 22, 22, 29, 22, 4, 22, - 1], «год»: 2010, «эпоха»: 1264199362.0, «изоформа» at ":" 2010-01-22T22: 29: 22.995000 "," day ": 22," minute ": 29}," author ": {" nickname ":" test@example.com "," email ":" test@example.com "," auth_domain ":" gmail.com "}}]

10 голосов
/ 25 февраля 2010

Пример, предоставленный Jader Dias, прекрасно работает для меня после некоторой настройки. Удалите метод кодирования, поскольку он содержит циклическую ссылку. Настроенный класс должен выглядеть так:

import datetime  
import time 

from google.appengine.api import users 
from google.appengine.ext import db 
from django.utils import simplejson  


class GqlEncoder(simplejson.JSONEncoder): 

    """Extends JSONEncoder to add support for GQL results and properties. 

    Adds support to simplejson JSONEncoders for GQL results and properties by 
    overriding JSONEncoder's default method. 
    """ 

    # TODO Improve coverage for all of App Engine's Property types. 

    def default(self, obj): 

        """Tests the input object, obj, to encode as JSON.""" 

        if hasattr(obj, '__json__'): 
            return getattr(obj, '__json__')() 

        if isinstance(obj, db.GqlQuery): 
            return list(obj) 

        elif isinstance(obj, db.Model): 
            properties = obj.properties().items() 
            output = {} 
            for field, value in properties: 
                output[field] = getattr(obj, field) 
            return output 

        elif isinstance(obj, datetime.datetime): 
            output = {} 
            fields = ['day', 'hour', 'microsecond', 'minute', 'month', 'second', 'year'] 
            methods = ['ctime', 'isocalendar', 'isoformat', 'isoweekday', 'timetuple'] 
            for field in fields: 
                output[field] = getattr(obj, field) 
            for method in methods: 
                output[method] = getattr(obj, method)() 
            output['epoch'] = time.mktime(obj.timetuple()) 
            return output

        elif isinstance(obj, datetime.date): 
            output = {} 
            fields = ['year', 'month', 'day'] 
            methods = ['ctime', 'isocalendar', 'isoformat', 'isoweekday', 'timetuple'] 
            for field in fields: 
                output[field] = getattr(obj, field) 
            for method in methods: 
                output[method] = getattr(obj, method)() 
            output['epoch'] = time.mktime(obj.timetuple()) 
            return output 

        elif isinstance(obj, time.struct_time): 
            return list(obj) 

        elif isinstance(obj, users.User): 
            output = {} 
            methods = ['nickname', 'email', 'auth_domain'] 
            for method in methods: 
                output[method] = getattr(obj, method)() 
            return output 

        return simplejson.JSONEncoder.default(self, obj) 

Поскольку я сохранил этот класс в файле с именем utils.py, и при необходимости я импортирую его, используя

import utils

Тогда я просто вызываю utils.GqlEncoder (). Encode (results), например:

query = User.all()
results = query.fetch(10)

self.response.headers['Content-Type'] = "text/plain" # Alt. application/json
self.response.out.write( utils.GqlEncoder().encode(results) )

Результат должен выглядеть примерно так (я добавил некоторые переводы строк, чтобы его было легче читать):

[
{"date": {"ctime": "Tue Feb 23 10:41:21 2010", "hour": 10, "isoweekday": 2, "month": 2, 
        "second": 21, "microsecond": 495535, "isocalendar": [2010, 8, 2], "timetuple": [2010, 2, 23, 10, 41, 21, 1, 54, -1], 
        "year": 2010, "epoch": 1266921681.0, "isoformat": "2010-02-23T10:41:21.495535", "day": 23, "minute": 41}, 
"claimed_id": "https:\/\/www.google.com\/accounts\/o8\/id?id=abcdefghijklmnopqrstuvxyz", 
"display_name": "Alfred E Neumann", 
"email": null, 
"full_name": "Alfred E Neumann"
}, 
{"date": {"ctime": "Tue Feb 23 11:00:54 2010", "hour": 11, "isoweekday": 2, "month": 2, 
        "second": 54, "microsecond": 805261, "isocalendar": [2010, 8, 2], "timetuple": [2010, 2, 23, 11, 0, 54, 1, 54, -1], 
        "year": 2010, "epoch": 1266922854.0, "isoformat": "2010-02-23T11:00:54.805261", "day": 23, "minute": 0}, 
"claimed_id": "http:\/\/openid.domain.net\/john", 
"display_name": "", 
"email": "jp@domain.net", 
"full_name": "John Parnefjord"
}
]
3 голосов
/ 22 января 2010

json нельзя использовать для сериализации чего-либо, кроме базовых типов, таких как dicts, list, int / long и строки (это не является исчерпывающим). Например, даже эти простые команды не работают:

import json
json.dumps(object())

Если вы хотите сериализовать объекты django, вам следует обратиться к документации django по сериализации , в которой будут использоваться собственные библиотеки, но они поддерживают json.

0 голосов
/ 24 января 2010

Из того, что я могу понять - и я новичок в python - с механизмом приложений Google можно обойти сериализацию объекта модели до объекта python dictioanry, а затем использовать простой json, чтобы вывести его в виде строки json - это не дает смысл для меня - может быть, кто-то знает, чтобы сериализовать в Ditionary (Pickel?) Любая помощь в этом будет ПОМОЩЬ! не впечатлило, что у движка Google нет встроенного решения для этого.

0 голосов
/ 23 января 2010

Так как я не мог найти подходящее решение, я написал свое собственное, которое не является точно сериализатором JSON, а сериализатором Javascript

from google.appengine.ext import db
from google.appengine.api.datastore_types import *

def dumpStr(obj):
    return "'" + obj + "'"

def dumps(obj):
    if isinstance(obj, str):
        return dumpStr(obj)
    elif obj == None:
        return None
    elif isinstance(obj, list):
        items = [];
        for item in obj:
            items.append(dumps(item))
        return '[' + ','.join(items) + ']'
    elif isinstance(obj, datetime.datetime):
        return "new Date('%s')" % obj.ctime()
    properties = [];
    for property in dir(obj):
        if property[0] != '_':
            value = obj.__getattribute__(property)
            valueClass = str(value.__class__)
            if not(('function' in valueClass) or ('built' in valueClass) or ('method' in valueClass)):
                value = dumps(value)
                if value != None:
                    properties.append("'" + property + "':" + value)
    if len(properties) == 0:
        return str(obj)
    else:
        return '{' + ','.join(properties) + '}'
...