1
|
|
|
import re |
|
|
|
|
2
|
|
|
|
3
|
|
|
JSON_TYPES = (str, int, float, bool) |
4
|
|
|
RFC3339_DATETIME_PATTERN = '%Y-%m-%dT%H:%M:%S' |
5
|
|
|
CLASSES = list() |
6
|
|
|
SERIALIZERS = dict() |
7
|
|
|
DESERIALIZERS = dict() |
8
|
|
|
|
9
|
|
|
|
10
|
|
|
def dump_impl(obj: object, **kwargs) -> dict: |
11
|
|
|
''' |
12
|
|
|
Serialize the given ``obj`` to a dict. |
13
|
|
|
|
14
|
|
|
The way objects are serialized can be finetuned by setting serializer |
15
|
|
|
functions for the specific type using ``set_serializer``. |
16
|
|
|
:param obj: a Python instance of any sort. |
17
|
|
|
:param kwargs: the keyword args are passed on to the serializer function. |
18
|
|
|
:return: the serialized obj as a dict. |
19
|
|
|
''' |
20
|
|
|
serializer = SERIALIZERS.get(obj.__class__.__name__, None) |
21
|
|
|
if not serializer: |
22
|
|
|
parents = [cls for cls in CLASSES if isinstance(obj, cls)] |
23
|
|
|
if parents: |
24
|
|
|
serializer = SERIALIZERS[parents[0].__name__] |
25
|
|
|
return serializer(obj, **kwargs) |
26
|
|
|
|
27
|
|
|
|
28
|
|
|
def load_impl(json_obj: dict, cls: type = None, **kwargs) -> object: |
29
|
|
|
''' |
30
|
|
|
Deserialize the given ``json_obj`` to an object of type ``cls``. If the |
31
|
|
|
contents of ``json_obj`` do not match the interface of ``cls``, a |
32
|
|
|
TypeError is raised. |
33
|
|
|
|
34
|
|
|
If ``json_obj`` contains a value that belongs to a custom class, there must |
35
|
|
|
be a type hint present for that value in ``cls`` to let this function know |
36
|
|
|
what type it should deserialize that value to. |
37
|
|
|
|
38
|
|
|
|
39
|
|
|
**Example**: |
40
|
|
|
|
41
|
|
|
``class Person:`` |
42
|
|
|
``# No type hint required for name`` |
43
|
|
|
|
44
|
|
|
``class Person:`` |
45
|
|
|
``# No type hint required for name`` |
46
|
|
|
|
47
|
|
|
``def __init__(self, name):`` |
48
|
|
|
``self.name = name`` |
49
|
|
|
```` |
50
|
|
|
``class Family:`` |
51
|
|
|
``# Person is a custom class, use a type hint`` |
52
|
|
|
|
53
|
|
|
``def __init__(self, persons: List[Person]):`` |
54
|
|
|
``self.persons = persons`` |
55
|
|
|
|
56
|
|
|
``jsons.load(some_dict, Family)`` |
57
|
|
|
|
58
|
|
|
If no ``cls`` is given, a dict is simply returned, but contained values |
59
|
|
|
(e.g. serialized ``datetime`` values) are still deserialized. |
60
|
|
|
:param json_obj: the dict that is to be deserialized. |
61
|
|
|
:param cls: a matching class of which an instance should be returned. |
62
|
|
|
:param kwargs: the keyword args are passed on to the deserializer function. |
63
|
|
|
:return: an instance of ``cls`` if given, a dict otherwise. |
64
|
|
|
''' |
65
|
|
|
cls = cls or type(json_obj) |
66
|
|
|
cls_name = cls.__name__ if hasattr(cls, '__name__') \ |
67
|
|
|
else cls.__origin__.__name__ |
68
|
|
|
deserializer = DESERIALIZERS.get(cls_name, None) |
69
|
|
|
if not deserializer: |
70
|
|
|
parents = [cls_ for cls_ in CLASSES if issubclass(cls, cls_)] |
71
|
|
|
if parents: |
72
|
|
|
deserializer = DESERIALIZERS[parents[0].__name__] |
73
|
|
|
return deserializer(json_obj, cls, **kwargs) |
74
|
|
|
|
75
|
|
|
|
76
|
|
|
def camelcase(s: str) -> str: |
|
|
|
|
77
|
|
|
""" |
78
|
|
|
Return `s` in camelCase. |
79
|
|
|
:param s: the string that is to be transformed. |
80
|
|
|
:return: a string in camelCase. |
81
|
|
|
""" |
82
|
|
|
s = s.replace('-', '_') |
83
|
|
|
splitted = s.split('_') |
84
|
|
|
if len(splitted) > 1: |
85
|
|
|
s = ''.join([x.title() for x in splitted]) |
86
|
|
|
return s[0].lower() + s[1:] |
87
|
|
|
|
88
|
|
|
|
89
|
|
|
def snakecase(s: str) -> str: |
|
|
|
|
90
|
|
|
""" |
91
|
|
|
Return `s` in snake_case. |
92
|
|
|
:param s: the string that is to be transformed. |
93
|
|
|
:return: a string in snake_case. |
94
|
|
|
""" |
95
|
|
|
s = s.replace('-', '_') |
96
|
|
|
s = s[0].lower() + s[1:] |
97
|
|
|
return re.sub(r'([a-z])([A-Z])', '\\1_\\2', s).lower() |
98
|
|
|
|
99
|
|
|
|
100
|
|
|
def pascalcase(s: str) -> str: |
|
|
|
|
101
|
|
|
""" |
102
|
|
|
Return `s` in PascalCase. |
103
|
|
|
:param s: the string that is to be transformed. |
104
|
|
|
:return: a string in PascalCase. |
105
|
|
|
""" |
106
|
|
|
camelcase_str = camelcase(s) |
107
|
|
|
return camelcase_str[0].upper() + camelcase_str[1:] |
108
|
|
|
|
109
|
|
|
|
110
|
|
|
def lispcase(s: str) -> str: |
|
|
|
|
111
|
|
|
""" |
112
|
|
|
Return `s` in lisp-case. |
113
|
|
|
:param s: the string that is to be transformed. |
114
|
|
|
:return: a string in lisp-case. |
115
|
|
|
""" |
116
|
|
|
return snakecase(s).replace('_', '-') |
117
|
|
|
|
The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:
If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.