I am having a problem with JSON encoding in Python, in particular with decimal.Decimal. I use this to output JSON for a Google App Engine application.
To get around the exception from the default json module in Python saying that it cannot handle decimals. Decimal objects, I use this subclass of code:
class DecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, decimal.Decimal): return float(o) return super(DecimalEncoder, self).default(o)
In other applications, this really works. In this case, this is not so. After many disappointments, I found out that this gives strange results:
print id(decimal.Decimal) print id(type(o))
One would expect the identifier to be identical, because it makes sense that the same class object remains only once in memory. Since the identifier is different, isinstance() does not work.
Could it be that decimal.Decimal has already been imported somewhere else, for example, into App Engine and / or Webapp2 packages?
The following module reproduces the error on my system (OSx 10.10, Python 2.7.6, GAE SDK 1.9.20). Just create a GAE application and put it in main.py:
import webapp2, decimal, json, MySQLdb, sys class DecimalEncoder(json.JSONEncoder): def default(self, o): print id(decimal.Decimal) print id(type(o)) if isinstance(o, decimal.Decimal): return float(o) return super(DecimalEncoder, self).default(o) class MainHandler(webapp2.RequestHandler): def get(self): db = MySQLdb.connect(unix_socket='/var/mysql/mysql.sock', host='localhost', user='root', db='ssss', charset='utf8') cursor = db.cursor(MySQLdb.cursors.DictCursor) cursor.execute("SELECT id, price FROM product WHERE id = 1") record = cursor.fetchone() self.response.headers['Content-Type'] = 'application/json' self.response.write(json.dumps( record, cls=DecimalEncoder, indent=4, separators=(',', ': ') )) app = webapp2.WSGIApplication([ ('/', MainHandler) ], debug=True)
Database Table:
CREATE TABLE `product` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `price` decimal(10,2) unsigned DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; INSERT INTO product VALUES(0, 5.00);