1# -*- coding: utf-8 -*-
2from django.utils.html import conditional_escape
3from django.utils.encoding import force_text
4from django.utils.functional import Promise
5from django.core.serializers.json import DjangoJSONEncoder
6from django.utils.six import iteritems
7
8
9class SafeJSONEncoder(DjangoJSONEncoder):
10    def _recursive_escape(self, o, esc=conditional_escape):
11        if isinstance(o, dict):
12            return type(o)((esc(k), self._recursive_escape(v)) for (k, v) in iteritems(o))
13        if isinstance(o, (list, tuple)):
14            return type(o)(self._recursive_escape(v) for v in o)
15        if type(o) is bool:
16            return o
17        try:
18            return type(o)(esc(o))
19        except (ValueError, TypeError):
20            return self.default(o)
21
22    def encode(self, o):
23        value = self._recursive_escape(o)
24        return super(SafeJSONEncoder, self).encode(value)
25
26    def default(self, o):
27        if isinstance(o, Promise):
28            return force_text(o)
29        return super(SafeJSONEncoder, self).default(o)
30