lodum.json
1# SPDX-FileCopyrightText: 2025-present Michael R. Bernstein <zopemaven@gmail.com> 2# 3# SPDX-License-Identifier: Apache-2.0 4import json 5from typing import Any, Dict, Iterator, Type, TypeVar 6 7from .core import Loader, BaseDumper, BaseLoader 8from .exception import DeserializationError 9from .internal import dump, load, generate_schema, DEFAULT_MAX_SIZE 10 11T = TypeVar("T") 12 13# --- Public API --- 14 15 16def dumps(obj: Any) -> str: 17 """ 18 Encodes a Python object to a JSON string. 19 20 Args: 21 obj: The object to encode. Must be lodum-enabled or a supported type. 22 23 Returns: 24 A JSON string representation of the object. 25 """ 26 dumper = JsonDumper() 27 dumped_data = dump(obj, dumper) 28 return json.dumps(dumped_data) 29 30 31def loads(cls: Type[T], json_string: str, max_size: int = DEFAULT_MAX_SIZE) -> T: 32 """ 33 Decodes a JSON string into a Python object of the specified type. 34 35 Args: 36 cls: The class to instantiate. 37 json_string: The JSON data to decode. 38 max_size: Maximum allowed size of the input string in bytes. 39 40 Returns: 41 An instance of cls populated with the decoded data. 42 43 Raises: 44 DeserializationError: If the input is invalid or exceeds max_size. 45 """ 46 if len(json_string) > max_size: 47 raise DeserializationError( 48 f"Input size ({len(json_string)}) exceeds maximum allowed ({max_size})" 49 ) 50 data = json.loads(json_string) 51 loader = JsonLoader(data) 52 return load(cls, loader) 53 54 55def schema(cls: Type[Any]) -> Dict[str, Any]: 56 """Generates a JSON Schema for a given lodum-enabled class.""" 57 return generate_schema(cls) 58 59 60# --- JSON Dumper Implementation --- 61 62 63class JsonDumper(BaseDumper): 64 def dump_bytes(self, value: bytes) -> Any: 65 import base64 66 67 return base64.b64encode(value).decode("ascii") 68 69 70# --- JSON Loader Implementation --- 71 72 73class JsonLoader(BaseLoader): 74 def load_list(self) -> Iterator["Loader"]: 75 if not isinstance(self._data, list): 76 raise DeserializationError( 77 f"Expected list, got {type(self._data).__name__}" 78 ) 79 return (JsonLoader(item) for item in self._data) 80 81 def load_dict(self) -> Iterator[tuple[str, "Loader"]]: 82 if not isinstance(self._data, dict): 83 raise DeserializationError( 84 f"Expected dict, got {type(self._data).__name__}" 85 ) 86 return ((k, JsonLoader(v)) for k, v in self._data.items()) 87 88 def load_bytes_value(self, value: Any) -> bytes: 89 if not isinstance(value, str): 90 raise DeserializationError(f"Expected str, got {type(value).__name__}") 91 import base64 92 93 try: 94 return base64.b64decode(value) 95 except Exception as e: 96 raise DeserializationError(f"Failed to decode base64: {e}")
def
dumps(obj: Any) -> str:
17def dumps(obj: Any) -> str: 18 """ 19 Encodes a Python object to a JSON string. 20 21 Args: 22 obj: The object to encode. Must be lodum-enabled or a supported type. 23 24 Returns: 25 A JSON string representation of the object. 26 """ 27 dumper = JsonDumper() 28 dumped_data = dump(obj, dumper) 29 return json.dumps(dumped_data)
Encodes a Python object to a JSON string.
Args: obj: The object to encode. Must be lodum-enabled or a supported type.
Returns: A JSON string representation of the object.
def
loads(cls: Type[~T], json_string: str, max_size: int = 10485760) -> ~T:
32def loads(cls: Type[T], json_string: str, max_size: int = DEFAULT_MAX_SIZE) -> T: 33 """ 34 Decodes a JSON string into a Python object of the specified type. 35 36 Args: 37 cls: The class to instantiate. 38 json_string: The JSON data to decode. 39 max_size: Maximum allowed size of the input string in bytes. 40 41 Returns: 42 An instance of cls populated with the decoded data. 43 44 Raises: 45 DeserializationError: If the input is invalid or exceeds max_size. 46 """ 47 if len(json_string) > max_size: 48 raise DeserializationError( 49 f"Input size ({len(json_string)}) exceeds maximum allowed ({max_size})" 50 ) 51 data = json.loads(json_string) 52 loader = JsonLoader(data) 53 return load(cls, loader)
Decodes a JSON string into a Python object of the specified type.
Args: cls: The class to instantiate. json_string: The JSON data to decode. max_size: Maximum allowed size of the input string in bytes.
Returns: An instance of cls populated with the decoded data.
Raises: DeserializationError: If the input is invalid or exceeds max_size.
def
schema(cls: Type[Any]) -> Dict[str, Any]:
56def schema(cls: Type[Any]) -> Dict[str, Any]: 57 """Generates a JSON Schema for a given lodum-enabled class.""" 58 return generate_schema(cls)
Generates a JSON Schema for a given lodum-enabled class.
class
JsonDumper(lodum.core.BaseDumper):
64class JsonDumper(BaseDumper): 65 def dump_bytes(self, value: bytes) -> Any: 66 import base64 67 68 return base64.b64encode(value).decode("ascii")
Base implementation of the Dumper protocol to reduce duplication.
class
JsonLoader(lodum.core.BaseLoader):
74class JsonLoader(BaseLoader): 75 def load_list(self) -> Iterator["Loader"]: 76 if not isinstance(self._data, list): 77 raise DeserializationError( 78 f"Expected list, got {type(self._data).__name__}" 79 ) 80 return (JsonLoader(item) for item in self._data) 81 82 def load_dict(self) -> Iterator[tuple[str, "Loader"]]: 83 if not isinstance(self._data, dict): 84 raise DeserializationError( 85 f"Expected dict, got {type(self._data).__name__}" 86 ) 87 return ((k, JsonLoader(v)) for k, v in self._data.items()) 88 89 def load_bytes_value(self, value: Any) -> bytes: 90 if not isinstance(value, str): 91 raise DeserializationError(f"Expected str, got {type(value).__name__}") 92 import base64 93 94 try: 95 return base64.b64decode(value) 96 except Exception as e: 97 raise DeserializationError(f"Failed to decode base64: {e}")
Base implementation of the Loader protocol to reduce duplication.
def
load_bytes_value(self, value: Any) -> bytes:
89 def load_bytes_value(self, value: Any) -> bytes: 90 if not isinstance(value, str): 91 raise DeserializationError(f"Expected str, got {type(value).__name__}") 92 import base64 93 94 try: 95 return base64.b64decode(value) 96 except Exception as e: 97 raise DeserializationError(f"Failed to decode base64: {e}")