Initial commit

This commit is contained in:
2026-03-12 19:34:25 +01:00
commit b00e5d9829
22 changed files with 2916 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
# Copyright 2026 jCloud Services GbR
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from typing import *
import collections.abc
__all__ = [
'serialize'
]
_ESCAPE_SEQUENCES = {
'\"': r'\"',
'\\': r'\\',
'\b': r'\b',
'\f': r'\f',
'\n': r'\n',
'\r': r'\r',
'\t': r'\t',
}
class _Mapper:
def __init__(self, mapping = {}):
self._mapping = copy.deepcopy(mapping)
def _set(self, key, value):
self._mapping[key] = value
def set(self, key, value):
self._set(key, value)
return self
def map(self, value):
return self._mapping[value]
_MAPPER = _Mapper().set(True, 'true').set(False, 'false').set(None, 'null')
def _serialize_object(data: dict, level: int, indent: int, indent_char: str, separators: Tuple[str, str]) -> str:
result = '{'
_count = 0
for _key, value in data.items():
_count += 1
key = _serialize(_key, 0, 0, '', separators)
value = _serialize(value, level + 1, indent, indent_char, separators)
if not isinstance(_key, str):
key = '"' + key + '"'
if indent:
result += '\n'
result += (level + 1) * indent * indent_char
result += key
result += separators[1]
result += value
if _count != len(data.keys()):
if indent:
result += separators[0].strip()
else:
result += separators[0]
if indent:
result += '\n'
result += level * indent * indent_char + '}'
return result
def _serialize_array(data: Sequence, level: int, indent: int, indent_char: str, separators: Tuple[str, str]) -> str:
result = '['
_count = 0
for value in data:
_count += 1
value = _serialize(value, level + 1, indent, indent_char, separators)
if indent:
result += '\n'
result += (level + 1) * indent * indent_char
result += value
if _count != len(data):
if indent:
result += separators[0].strip()
else:
result += separators[0]
if indent:
result += '\n'
result += level * indent * indent_char + ']'
return result
def _serialize_string(data: str) -> str:
_data = data
data = ''
for c in _data:
data += _ESCAPE_SEQUENCES.get(c, c)
return '"' + data + '"'
def _serialize_number(data: int | float) -> str:
return str(data)
def _serialize(data: dict | list | str | int | float | bool | None, level: int, indent: int, indent_char: str, separators: Tuple[str, str]) -> str:
if isinstance(data, (bool, type(None))):
return _MAPPER.map(data)
elif isinstance(data, dict):
return _serialize_object(data, level, indent, indent_char, separators)
elif isinstance(data, str):
return _serialize_string(data)
elif isinstance(data, (int, float)):
return _serialize_number(data)
elif isinstance(data, collections.abc.Sequence):
return _serialize_array(data, level, indent, indent_char, separators)
else:
raise TypeError(f'Object of type {type(data).__name__} is not JSON serializable')
def _char_whitespaces(whitespaces: Tuple[int | str, int | str] | int | str) -> Tuple[str, str]:
if isinstance(whitespaces, collections.abc.Sequence) and not isinstance(whitespaces, str):
if len(whitespaces) != 2:
raise ValueError(f'expected exactly two elements in whitespaces, got {len(whitespaces)}')
if isinstance(whitespaces, (str, int)):
whitespaces = whitespaces, whitespaces
if isinstance(whitespaces[0], int):
whitespaces = whitespaces[0] * ' ', whitespaces[1]
if isinstance(whitespaces[1], int):
whitespaces = whitespaces[0], whitespaces[1] * ' '
return whitespaces
def serialize(data: dict | list | str | int | float | bool | None, indent: int = 0, indent_char: str = ' ', separators: Tuple[str, str] = (', ', ': ')) -> str:
'''
Serializes the data to a JSON formatted string.
:param data: The data.
:type data: dict | list | str | int | float | bool | None
:param indent: The indentation
:type indent: int
:param indent_char: The character the indentation will be filled with
:type indent_char: str
:param separators: A tuple with the separators. The first string is the element separator and the second string is the key value separator.
:type separators: tuple
:raises TypeError: If the type is not JSON serializable.
:return: The JSON formatted string.
:rtype: str
'''
if len(separators) != 2:
raise ValueError(f'expected exactly two elements in separators, got {len(separators)}.')
return _serialize(data, 0, indent, indent_char, separators)