1from __future__ import annotations
2
3import json as _json
4import typing as t
5
6from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
7
8from ..globals import current_app
9from .provider import _default
10
11if t.TYPE_CHECKING: # pragma: no cover
12 from ..app import Flask
13 from ..wrappers import Response
14
15
16class JSONEncoder(_json.JSONEncoder):
17 """The default JSON encoder. Handles extra types compared to the
18 built-in :class:`json.JSONEncoder`.
19
20 - :class:`datetime.datetime` and :class:`datetime.date` are
21 serialized to :rfc:`822` strings. This is the same as the HTTP
22 date format.
23 - :class:`decimal.Decimal` is serialized to a string.
24 - :class:`uuid.UUID` is serialized to a string.
25 - :class:`dataclasses.dataclass` is passed to
26 :func:`dataclasses.asdict`.
27 - :class:`~markupsafe.Markup` (or any object with a ``__html__``
28 method) will call the ``__html__`` method to get a string.
29
30 Assign a subclass of this to :attr:`flask.Flask.json_encoder` or
31 :attr:`flask.Blueprint.json_encoder` to override the default.
32
33 .. deprecated:: 2.2
34 Will be removed in Flask 2.3. Use ``app.json`` instead.
35 """
36
37 def __init__(self, **kwargs) -> None:
38 import warnings
39
40 warnings.warn(
41 "'JSONEncoder' is deprecated and will be removed in"
42 " Flask 2.3. Use 'Flask.json' to provide an alternate"
43 " JSON implementation instead.",
44 DeprecationWarning,
45 stacklevel=3,
46 )
47 super().__init__(**kwargs)
48
49 def default(self, o: t.Any) -> t.Any:
50 """Convert ``o`` to a JSON serializable type. See
51 :meth:`json.JSONEncoder.default`. Python does not support
52 overriding how basic types like ``str`` or ``list`` are
53 serialized, they are handled before this method.
54 """
55 return _default(o)
56
57
58class JSONDecoder(_json.JSONDecoder):
59 """The default JSON decoder.
60
61 This does not change any behavior from the built-in
62 :class:`json.JSONDecoder`.
63
64 Assign a subclass of this to :attr:`flask.Flask.json_decoder` or
65 :attr:`flask.Blueprint.json_decoder` to override the default.
66
67 .. deprecated:: 2.2
68 Will be removed in Flask 2.3. Use ``app.json`` instead.
69 """
70
71 def __init__(self, **kwargs) -> None:
72 import warnings
73
74 warnings.warn(
75 "'JSONDecoder' is deprecated and will be removed in"
76 " Flask 2.3. Use 'Flask.json' to provide an alternate"
77 " JSON implementation instead.",
78 DeprecationWarning,
79 stacklevel=3,
80 )
81 super().__init__(**kwargs)
82
83
84def dumps(obj: t.Any, *, app: Flask | None = None, **kwargs: t.Any) -> str:
85 """Serialize data as JSON.
86
87 If :data:`~flask.current_app` is available, it will use its
88 :meth:`app.json.dumps() <flask.json.provider.JSONProvider.dumps>`
89 method, otherwise it will use :func:`json.dumps`.
90
91 :param obj: The data to serialize.
92 :param kwargs: Arguments passed to the ``dumps`` implementation.
93
94 .. versionchanged:: 2.2
95 Calls ``current_app.json.dumps``, allowing an app to override
96 the behavior.
97
98 .. versionchanged:: 2.2
99 The ``app`` parameter will be removed in Flask 2.3.
100
101 .. versionchanged:: 2.0.2
102 :class:`decimal.Decimal` is supported by converting to a string.
103
104 .. versionchanged:: 2.0
105 ``encoding`` will be removed in Flask 2.1.
106
107 .. versionchanged:: 1.0.3
108 ``app`` can be passed directly, rather than requiring an app
109 context for configuration.
110 """
111 if app is not None:
112 import warnings
113
114 warnings.warn(
115 "The 'app' parameter is deprecated and will be removed in"
116 " Flask 2.3. Call 'app.json.dumps' directly instead.",
117 DeprecationWarning,
118 stacklevel=2,
119 )
120 else:
121 app = current_app
122
123 if app:
124 return app.json.dumps(obj, **kwargs)
125
126 kwargs.setdefault("default", _default)
127 return _json.dumps(obj, **kwargs)
128
129
130def dump(
131 obj: t.Any, fp: t.IO[str], *, app: Flask | None = None, **kwargs: t.Any
132) -> None:
133 """Serialize data as JSON and write to a file.
134
135 If :data:`~flask.current_app` is available, it will use its
136 :meth:`app.json.dump() <flask.json.provider.JSONProvider.dump>`
137 method, otherwise it will use :func:`json.dump`.
138
139 :param obj: The data to serialize.
140 :param fp: A file opened for writing text. Should use the UTF-8
141 encoding to be valid JSON.
142 :param kwargs: Arguments passed to the ``dump`` implementation.
143
144 .. versionchanged:: 2.2
145 Calls ``current_app.json.dump``, allowing an app to override
146 the behavior.
147
148 .. versionchanged:: 2.2
149 The ``app`` parameter will be removed in Flask 2.3.
150
151 .. versionchanged:: 2.0
152 Writing to a binary file, and the ``encoding`` argument, will be
153 removed in Flask 2.1.
154 """
155 if app is not None:
156 import warnings
157
158 warnings.warn(
159 "The 'app' parameter is deprecated and will be removed in"
160 " Flask 2.3. Call 'app.json.dump' directly instead.",
161 DeprecationWarning,
162 stacklevel=2,
163 )
164 else:
165 app = current_app
166
167 if app:
168 app.json.dump(obj, fp, **kwargs)
169 else:
170 kwargs.setdefault("default", _default)
171 _json.dump(obj, fp, **kwargs)
172
173
174def loads(s: str | bytes, *, app: Flask | None = None, **kwargs: t.Any) -> t.Any:
175 """Deserialize data as JSON.
176
177 If :data:`~flask.current_app` is available, it will use its
178 :meth:`app.json.loads() <flask.json.provider.JSONProvider.loads>`
179 method, otherwise it will use :func:`json.loads`.
180
181 :param s: Text or UTF-8 bytes.
182 :param kwargs: Arguments passed to the ``loads`` implementation.
183
184 .. versionchanged:: 2.2
185 Calls ``current_app.json.loads``, allowing an app to override
186 the behavior.
187
188 .. versionchanged:: 2.2
189 The ``app`` parameter will be removed in Flask 2.3.
190
191 .. versionchanged:: 2.0
192 ``encoding`` will be removed in Flask 2.1. The data must be a
193 string or UTF-8 bytes.
194
195 .. versionchanged:: 1.0.3
196 ``app`` can be passed directly, rather than requiring an app
197 context for configuration.
198 """
199 if app is not None:
200 import warnings
201
202 warnings.warn(
203 "The 'app' parameter is deprecated and will be removed in"
204 " Flask 2.3. Call 'app.json.loads' directly instead.",
205 DeprecationWarning,
206 stacklevel=2,
207 )
208 else:
209 app = current_app
210
211 if app:
212 return app.json.loads(s, **kwargs)
213
214 return _json.loads(s, **kwargs)
215
216
217def load(fp: t.IO[t.AnyStr], *, app: Flask | None = None, **kwargs: t.Any) -> t.Any:
218 """Deserialize data as JSON read from a file.
219
220 If :data:`~flask.current_app` is available, it will use its
221 :meth:`app.json.load() <flask.json.provider.JSONProvider.load>`
222 method, otherwise it will use :func:`json.load`.
223
224 :param fp: A file opened for reading text or UTF-8 bytes.
225 :param kwargs: Arguments passed to the ``load`` implementation.
226
227 .. versionchanged:: 2.2
228 Calls ``current_app.json.load``, allowing an app to override
229 the behavior.
230
231 .. versionchanged:: 2.2
232 The ``app`` parameter will be removed in Flask 2.3.
233
234 .. versionchanged:: 2.0
235 ``encoding`` will be removed in Flask 2.1. The file must be text
236 mode, or binary mode with UTF-8 bytes.
237 """
238 if app is not None:
239 import warnings
240
241 warnings.warn(
242 "The 'app' parameter is deprecated and will be removed in"
243 " Flask 2.3. Call 'app.json.load' directly instead.",
244 DeprecationWarning,
245 stacklevel=2,
246 )
247 else:
248 app = current_app
249
250 if app:
251 return app.json.load(fp, **kwargs)
252
253 return _json.load(fp, **kwargs)
254
255
256def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str:
257 """Serialize an object to a string of JSON with :func:`dumps`, then
258 replace HTML-unsafe characters with Unicode escapes and mark the
259 result safe with :class:`~markupsafe.Markup`.
260
261 This is available in templates as the ``|tojson`` filter.
262
263 The returned string is safe to render in HTML documents and
264 ``<script>`` tags. The exception is in HTML attributes that are
265 double quoted; either use single quotes or the ``|forceescape``
266 filter.
267
268 .. deprecated:: 2.2
269 Will be removed in Flask 2.3. This is built-in to Jinja now.
270
271 .. versionchanged:: 2.0
272 Uses :func:`jinja2.utils.htmlsafe_json_dumps`. The returned
273 value is marked safe by wrapping in :class:`~markupsafe.Markup`.
274
275 .. versionchanged:: 0.10
276 Single quotes are escaped, making this safe to use in HTML,
277 ``<script>`` tags, and single-quoted attributes without further
278 escaping.
279 """
280 import warnings
281
282 warnings.warn(
283 "'htmlsafe_dumps' is deprecated and will be removed in Flask"
284 " 2.3. Use 'jinja2.utils.htmlsafe_json_dumps' instead.",
285 DeprecationWarning,
286 stacklevel=2,
287 )
288 return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
289
290
291def htmlsafe_dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
292 """Serialize an object to JSON written to a file object, replacing
293 HTML-unsafe characters with Unicode escapes. See
294 :func:`htmlsafe_dumps` and :func:`dumps`.
295
296 .. deprecated:: 2.2
297 Will be removed in Flask 2.3.
298 """
299 import warnings
300
301 warnings.warn(
302 "'htmlsafe_dump' is deprecated and will be removed in Flask"
303 " 2.3. Use 'jinja2.utils.htmlsafe_json_dumps' instead.",
304 DeprecationWarning,
305 stacklevel=2,
306 )
307 fp.write(htmlsafe_dumps(obj, **kwargs))
308
309
310def jsonify(*args: t.Any, **kwargs: t.Any) -> Response:
311 """Serialize the given arguments as JSON, and return a
312 :class:`~flask.Response` object with the ``application/json``
313 mimetype. A dict or list returned from a view will be converted to a
314 JSON response automatically without needing to call this.
315
316 This requires an active request or application context, and calls
317 :meth:`app.json.response() <flask.json.provider.JSONProvider.response>`.
318
319 In debug mode, the output is formatted with indentation to make it
320 easier to read. This may also be controlled by the provider.
321
322 Either positional or keyword arguments can be given, not both.
323 If no arguments are given, ``None`` is serialized.
324
325 :param args: A single value to serialize, or multiple values to
326 treat as a list to serialize.
327 :param kwargs: Treat as a dict to serialize.
328
329 .. versionchanged:: 2.2
330 Calls ``current_app.json.response``, allowing an app to override
331 the behavior.
332
333 .. versionchanged:: 2.0.2
334 :class:`decimal.Decimal` is supported by converting to a string.
335
336 .. versionchanged:: 0.11
337 Added support for serializing top-level arrays. This was a
338 security risk in ancient browsers. See :ref:`security-json`.
339
340 .. versionadded:: 0.2
341 """
342 return current_app.json.response(*args, **kwargs)