我目前正在使用python并在Google App Engine上创建一个简单的API,允许用户通过使用命令行进行API调用来使用GET,POST,DELETE和PUT谓词。我使用curl来处理API调用的数据。我已经能够成功添加数据并将其显示回来,但问题是我有一个属性为'价格'并且此价格作为浮点数存储在GAE(Google App Engine)的数据存储区中,并且数字在提交时格式化,但是当JSON显示数据存储区中的数据时,它具有不同的格式。例如,存储价格8.99 JSON响应显示8.9900000000000002。我很确定这不是GAE问题,而是JSON问题。
问题示例:
curl --data "name=t-shirt&description=Star Trek&price=8.99&user=test1" -H "Accept: application/json" https://mywebsite.appspot.com/product
正确存储在数据存储区中但返回JSON,如下所示:
{"description": "Star Trek", "price": 8.9900000000000002, "name": "t-shirt", "user": "test1", "key": 5206065687822336}
以下是我的models.py
from google.appengine.ext import ndb
class Model(ndb.Model):
def to_dict(self):
d = super(Model, self).to_dict()
d['key'] = self.key.id()
return d
class User(Model):
username = ndb.StringProperty(required=True)
password = ndb.StringProperty(required=True)
class Product(Model):
name = ndb.StringProperty(required=True)
description = ndb.StringProperty(required=True)
price = ndb.FloatProperty()
color = ndb.StringProperty()
size = ndb.StringProperty()
user = ndb.StringProperty(required=True)
class Sales(Model):
products = ndb.KeyProperty(repeated=True)
datetime = ndb.DateTimeProperty(required=True)
quantity = ndb.IntegerProperty(repeated=True)
product_cost = ndb.FloatProperty(repeated=True)
total_cost = ndb.FloatProperty()
latitude = ndb.FloatProperty()
longitude = ndb.FloatProperty()
user = ndb.KeyProperty(required=True)
def to_dict(self):
d = super(Sales, self).to_dict()
d['products'] = [i.id() for i in d['products']]
return d
这是product.py:
import webapp2
from google.appengine.ext import ndb
import models
import json
class Product(webapp2.RequestHandler):
#Create a Product entity
def post(self):
if 'application/json' not in self.request.accept:
self.response.status = 406
self.response.status_message = 'Not acceptable, API only supports application/json MIME type.'
return
new_product = models.Product()
name = self.request.get('name', default_value=None)
description = self.request.get('description', default_value=None)
price = self.request.get('price', default_value=0)
color = self.request.get('color', default_value=None)
size = self.request.get('size', default_value=None)
user = self.request.get('user', default_value=None)
if name:
new_product.name = name
else:
self.response.status = 400
self.response.status_message = 'Invalid request, name required'
if description:
new_product.description = description
else:
self.response.status = 400
self.response.status_message = 'Invalid request, description required'
if price:
new_product.price = float(price)
if color:
new_product.color = color
if size:
new_product.size = size
if user:
new_product.user = user
else:
self.response.status = 400
self.response.status_message = 'Invalid request, username required'
key = new_product.put()
out = new_product.to_dict()
self.response.write(json.dumps(out))
return
#Return an Product entity
def get(self, **kwargs):
if 'application/json' not in self.request.accept:
self.response.status = 406
self.response.status_message = 'Not acceptable, API only supports application/json MIME type.'
self.response.write(self.response.status_message)
return
#Return selected product details
if 'id' in kwargs:
out = ndb.Key(models.Product, int(kwargs['id'])).get().to_dict()
self.response.write(json.dumps(out))
#Return all product ids
else:
q = models.Product.query()
keys = q.fetch(keys_only=False)
results = {x.key.id() : x.to_dict() for x in keys}
self.response.write(json.dumps(results))
我为矫枉过正道歉,但我想确保一切都可以看到。任何帮助将非常感激。我看过一些类似的项目,但我在这里使用字典的方式似乎无法使这种格式正确。
提前谢谢!
答案 0 :(得分:1)
我认为这实际上不是问题。如果您想了解为何存在这种不满,您可能需要阅读floating point numbers上的维基百科文章。 TLDR是在0和1之间已经存在无限多个有理数,但计算机只能存储有限数量的数据。因此,您通常希望将实数存储在32(单个)或64(双)位数据中,以实现高效计算。因此,机器编号只是实数的一个子集,您通常必须舍入到最接近的机器编号。如果点总是在同一个地方并且您使用的是基数为10的数字,那么这将不会成为一个问题,因此您可能知道舍入“8.99”时没有错误,但这不是正常浮点数的定义,如IEEE 754。
基本上8.99 = 8.9900000000000002在这里。
您可以通过多种方式解决问题:
警告:这可能不是您想要的 通常可以在显示时将错误四舍五入,因为通常错误非常小,例如在游戏中这可能是可以接受的。但是在这种情况下,您似乎正在创建一个商店,如果在不同的地方有不同的舍入,则可能会出现问题。例如,在某些情况下,它可能会在订单上产生0.01的差异,这可能会导致各种问题。不要将价格/金额存储在浮点数中。
而不是存储8.99,而是将899作为整数存储在数据库中。如果仅使用加法/减法,则整数算术不会出现舍入问题。
这可能有点不方便,因为它涉及更新数据库和显示数字(即你必须在正确的位置插入一个点)。但是,如果您无法访问专门的数字,这可能是您最安全的赌注,通常被认为是一个不错的选择。
如果您使用这样的数字进行大量算术运算,那么使用定点数库可能会更方便,并且基本上使用量身定制的有理数表示来解决您的问题。它与选项2基本相同,但您不会自己实现所有内容并具有更大的灵活性。但是我个人会选择2,因为我不知道是否有这样的库可以在Python和Javascript中使用,而且它可能只涉及对项目的不必要的依赖。