Initial commit
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user