146 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from __future__ import absolute_import
 | |
| 
 | |
| import datetime
 | |
| import uuid
 | |
| 
 | |
| from .._compat import text_type
 | |
| from ..exceptions import BadRequest
 | |
| from ..utils import detect_utf_encoding
 | |
| 
 | |
| try:
 | |
|     import simplejson as _json
 | |
| except ImportError:
 | |
|     import json as _json
 | |
| 
 | |
| 
 | |
| class _JSONModule(object):
 | |
|     @staticmethod
 | |
|     def _default(o):
 | |
|         if isinstance(o, datetime.date):
 | |
|             return o.isoformat()
 | |
| 
 | |
|         if isinstance(o, uuid.UUID):
 | |
|             return str(o)
 | |
| 
 | |
|         if hasattr(o, "__html__"):
 | |
|             return text_type(o.__html__())
 | |
| 
 | |
|         raise TypeError()
 | |
| 
 | |
|     @classmethod
 | |
|     def dumps(cls, obj, **kw):
 | |
|         kw.setdefault("separators", (",", ":"))
 | |
|         kw.setdefault("default", cls._default)
 | |
|         kw.setdefault("sort_keys", True)
 | |
|         return _json.dumps(obj, **kw)
 | |
| 
 | |
|     @staticmethod
 | |
|     def loads(s, **kw):
 | |
|         if isinstance(s, bytes):
 | |
|             # Needed for Python < 3.6
 | |
|             encoding = detect_utf_encoding(s)
 | |
|             s = s.decode(encoding)
 | |
| 
 | |
|         return _json.loads(s, **kw)
 | |
| 
 | |
| 
 | |
| class JSONMixin(object):
 | |
|     """Mixin to parse :attr:`data` as JSON. Can be mixed in for both
 | |
|     :class:`~werkzeug.wrappers.Request` and
 | |
|     :class:`~werkzeug.wrappers.Response` classes.
 | |
| 
 | |
|     If `simplejson`_ is installed it is preferred over Python's built-in
 | |
|     :mod:`json` module.
 | |
| 
 | |
|     .. _simplejson: https://simplejson.readthedocs.io/en/latest/
 | |
|     """
 | |
| 
 | |
|     #: A module or other object that has ``dumps`` and ``loads``
 | |
|     #: functions that match the API of the built-in :mod:`json` module.
 | |
|     json_module = _JSONModule
 | |
| 
 | |
|     @property
 | |
|     def json(self):
 | |
|         """The parsed JSON data if :attr:`mimetype` indicates JSON
 | |
|         (:mimetype:`application/json`, see :meth:`is_json`).
 | |
| 
 | |
|         Calls :meth:`get_json` with default arguments.
 | |
|         """
 | |
|         return self.get_json()
 | |
| 
 | |
|     @property
 | |
|     def is_json(self):
 | |
|         """Check if the mimetype indicates JSON data, either
 | |
|         :mimetype:`application/json` or :mimetype:`application/*+json`.
 | |
|         """
 | |
|         mt = self.mimetype
 | |
|         return (
 | |
|             mt == "application/json"
 | |
|             or mt.startswith("application/")
 | |
|             and mt.endswith("+json")
 | |
|         )
 | |
| 
 | |
|     def _get_data_for_json(self, cache):
 | |
|         try:
 | |
|             return self.get_data(cache=cache)
 | |
|         except TypeError:
 | |
|             # Response doesn't have cache param.
 | |
|             return self.get_data()
 | |
| 
 | |
|     # Cached values for ``(silent=False, silent=True)``. Initialized
 | |
|     # with sentinel values.
 | |
|     _cached_json = (Ellipsis, Ellipsis)
 | |
| 
 | |
|     def get_json(self, force=False, silent=False, cache=True):
 | |
|         """Parse :attr:`data` as JSON.
 | |
| 
 | |
|         If the mimetype does not indicate JSON
 | |
|         (:mimetype:`application/json`, see :meth:`is_json`), this
 | |
|         returns ``None``.
 | |
| 
 | |
|         If parsing fails, :meth:`on_json_loading_failed` is called and
 | |
|         its return value is used as the return value.
 | |
| 
 | |
|         :param force: Ignore the mimetype and always try to parse JSON.
 | |
|         :param silent: Silence parsing errors and return ``None``
 | |
|             instead.
 | |
|         :param cache: Store the parsed JSON to return for subsequent
 | |
|             calls.
 | |
|         """
 | |
|         if cache and self._cached_json[silent] is not Ellipsis:
 | |
|             return self._cached_json[silent]
 | |
| 
 | |
|         if not (force or self.is_json):
 | |
|             return None
 | |
| 
 | |
|         data = self._get_data_for_json(cache=cache)
 | |
| 
 | |
|         try:
 | |
|             rv = self.json_module.loads(data)
 | |
|         except ValueError as e:
 | |
|             if silent:
 | |
|                 rv = None
 | |
| 
 | |
|                 if cache:
 | |
|                     normal_rv, _ = self._cached_json
 | |
|                     self._cached_json = (normal_rv, rv)
 | |
|             else:
 | |
|                 rv = self.on_json_loading_failed(e)
 | |
| 
 | |
|                 if cache:
 | |
|                     _, silent_rv = self._cached_json
 | |
|                     self._cached_json = (rv, silent_rv)
 | |
|         else:
 | |
|             if cache:
 | |
|                 self._cached_json = (rv, rv)
 | |
| 
 | |
|         return rv
 | |
| 
 | |
|     def on_json_loading_failed(self, e):
 | |
|         """Called if :meth:`get_json` parsing fails and isn't silenced.
 | |
|         If this method returns a value, it is used as the return value
 | |
|         for :meth:`get_json`. The default implementation raises
 | |
|         :exc:`~werkzeug.exceptions.BadRequest`.
 | |
|         """
 | |
|         raise BadRequest("Failed to decode JSON object: {0}".format(e))
 |