aboutsummaryrefslogtreecommitdiffstats
path: root/python/werkzeug/wrappers/json.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/werkzeug/wrappers/json.py')
-rw-r--r--python/werkzeug/wrappers/json.py145
1 files changed, 145 insertions, 0 deletions
diff --git a/python/werkzeug/wrappers/json.py b/python/werkzeug/wrappers/json.py
new file mode 100644
index 0000000..6d5dc33
--- /dev/null
+++ b/python/werkzeug/wrappers/json.py
@@ -0,0 +1,145 @@
+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))