目录

在使用flask进行开发的时候发现 Decimal 类型的数据无法在作为 JSON 通过相应消息体序列化返回,出现 jsonschema.exceptions.ValidationError 错误:

jsonschema.exceptions.ValidationError: '1000.00' is not of type 'number'

在使用flask进行开发的时候发现 Decimal 类型的数据无法在作为 JSON 通过相应消息体序列化返回,出现 jsonschema.exceptions.ValidationError 错误:

jsonschema.exceptions.ValidationError: '1000.00' is not of type 'number'

研究 flask_restful/representations/json.py 代码发现问题出现在 json.dump()

       7 def output_json(data, code, headers=None):
       8     """Makes a Flask response with a JSON encoded body"""
       9
      10     settings = current_app.config.get('RESTFUL_JSON', {})
      11
             ...
      21     dumped = dumps(data, **settings) + "\n"

通过下面实验也可以对问题得到验证。

>>> import json
>>> import decimal
>>> json.dumps(decimal.Decimal('99.99'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/3.6.5_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/local/Cellar/python/3.6.5_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/Cellar/python/3.6.5_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/Cellar/python/3.6.5_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'Decimal' is not JSON serializable

查看json.dumps()的文档知道可以通过参数 ‘cls’ 指定JSONEncoder进行序列化。

    dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
        Serialize ``obj`` to a JSON formatted ``str``.

        To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
        ``.default()`` method to serialize additional types), specify it with
        the ``cls`` kwarg; otherwise ``JSONEncoder`` is used.

结合flask_restful,可以自行定义从JSONEncoder继承下来的序列化类,通过在config中的’RESTFUL_JSON’进行定义。

  3 import json
  4 import decimal

    ...

  9 class DecimalEncoder(json.JSONEncoder):
 10     def default(self, obj):
 11         if isinstance(obj, decimal.Decimal):
 12             return float(obj)
 13         return super(DecimalEncoder, self).default(obj)
 14
 15 class BaseConfig(object):
 16     RESTFUL_JSON = {'cls':DecimalEncoder}