source: trunk/src/allmydata/util/jsonbytes.py

Last change on this file was 3fb0bcf, checked in by Itamar Turner-Trauring <itamar@…>, at 2024-02-27T20:37:53Z

Remove unnecessary imports.

  • Property mode set to 100644
File size: 2.8 KB
Line 
1"""
2A JSON encoder than can serialize bytes.
3
4Ported to Python 3.
5"""
6
7import json
8
9
10def bytes_to_unicode(any_bytes, obj):
11    """Convert bytes to unicode.
12
13    :param any_bytes: If True, also support non-UTF-8-encoded bytes.
14    :param obj: Object to de-byte-ify.
15    """
16    errors = "backslashreplace" if any_bytes else "strict"
17
18    def doit(obj):
19        """Convert any bytes objects to unicode, recursively."""
20        if isinstance(obj, bytes):
21            return obj.decode("utf-8", errors=errors)
22        if isinstance(obj, dict):
23            new_obj = {}
24            for k, v in obj.items():
25                if isinstance(k, bytes):
26                    k = k.decode("utf-8", errors=errors)
27                v = doit(v)
28                new_obj[k] = v
29            return new_obj
30        if isinstance(obj, (list, set, tuple)):
31            return [doit(i) for i in obj]
32        return obj
33
34    return doit(obj)
35
36
37class UTF8BytesJSONEncoder(json.JSONEncoder):
38    """
39    A JSON encoder than can also encode UTF-8 encoded strings.
40    """
41    def default(self, o):
42        return bytes_to_unicode(False, o)
43
44    def encode(self, o, **kwargs):
45        return json.JSONEncoder.encode(
46            self, bytes_to_unicode(False, o), **kwargs)
47
48    def iterencode(self, o, **kwargs):
49        return json.JSONEncoder.iterencode(
50            self, bytes_to_unicode(False, o), **kwargs)
51
52
53class AnyBytesJSONEncoder(json.JSONEncoder):
54    """
55    A JSON encoder than can also encode bytes of any sort.
56
57    Bytes are decoded to strings using UTF-8, if that fails to decode then the
58    bytes are quoted.
59    """
60    def default(self, o):
61        return bytes_to_unicode(True, o)
62
63    def encode(self, o, **kwargs):
64        return json.JSONEncoder.encode(
65            self, bytes_to_unicode(True, o), **kwargs)
66
67    def iterencode(self, o, **kwargs):
68        return json.JSONEncoder.iterencode(
69            self, bytes_to_unicode(True, o), **kwargs)
70
71
72def dumps(obj, *args, **kwargs):
73    """Encode to JSON, supporting bytes as keys or values.
74
75    :param bool any_bytes: If False (the default) the bytes are assumed to be
76        UTF-8 encoded Unicode strings.  If True, non-UTF-8 bytes are quoted for
77        human consumption.
78    """
79    any_bytes = kwargs.pop("any_bytes", False)
80    if any_bytes:
81        cls = AnyBytesJSONEncoder
82    else:
83        cls = UTF8BytesJSONEncoder
84    return json.dumps(obj, cls=cls, *args, **kwargs)
85
86
87def dumps_bytes(obj, *args, **kwargs):
88    """Encode to JSON, then encode as bytes.
89
90    :param bool any_bytes: If False (the default) the bytes are assumed to be
91        UTF-8 encoded Unicode strings.  If True, non-UTF-8 bytes are quoted for
92        human consumption.
93    """
94    return dumps(obj, *args, **kwargs).encode("utf-8")
95
96
97# To make this module drop-in compatible with json module:
98loads = json.loads
99load = json.load
100
101
102__all__ = ["dumps", "loads", "load"]
Note: See TracBrowser for help on using the repository browser.