Convert Python objects to JSON-serializable format with type markers. Args: obj: Object to convert. Returns: JSON-serializable representation. Raises: SerializationError: If the object cannot be safely serialized.
(obj: Any)
| 74 | |
| 75 | @staticmethod |
| 76 | def to_json_safe(obj: Any) -> Any: |
| 77 | """Convert Python objects to JSON-serializable format with type markers. |
| 78 | |
| 79 | Args: |
| 80 | obj: Object to convert. |
| 81 | |
| 82 | Returns: |
| 83 | JSON-serializable representation. |
| 84 | |
| 85 | Raises: |
| 86 | SerializationError: If the object cannot be safely serialized. |
| 87 | """ |
| 88 | # Fast path: use exact type check for primitives (most common case) |
| 89 | obj_type = type(obj) |
| 90 | if obj_type is str or obj_type is int or obj_type is float or obj_type is bool or obj is None: |
| 91 | return obj |
| 92 | |
| 93 | # Fast path: list (very common for return values) |
| 94 | if obj_type is list: |
| 95 | return [SafeSerializer.to_json_safe(item) for item in obj] |
| 96 | |
| 97 | # Fast path: tuple (common for multiple return values) |
| 98 | if obj_type is tuple: |
| 99 | return {"__type__": "tuple", "data": [SafeSerializer.to_json_safe(item) for item in obj]} |
| 100 | |
| 101 | # Fast path: dict (common, check string keys) |
| 102 | if obj_type is dict: |
| 103 | if all(type(k) is str for k in obj): |
| 104 | return {k: SafeSerializer.to_json_safe(v) for k, v in obj.items()} |
| 105 | return { |
| 106 | "__type__": "dict_with_complex_keys", |
| 107 | "data": [[SafeSerializer.to_json_safe(k), SafeSerializer.to_json_safe(v)] for k, v in obj.items()], |
| 108 | } |
| 109 | |
| 110 | # Other builtin types - exact type checks |
| 111 | if obj_type is set: |
| 112 | return {"__type__": "set", "data": [SafeSerializer.to_json_safe(item) for item in obj]} |
| 113 | if obj_type is frozenset: |
| 114 | return {"__type__": "frozenset", "data": [SafeSerializer.to_json_safe(item) for item in obj]} |
| 115 | if obj_type is bytes: |
| 116 | return {"__type__": "bytes", "data": base64.b64encode(obj).decode()} |
| 117 | if obj_type is complex: |
| 118 | return {"__type__": "complex", "real": obj.real, "imag": obj.imag} |
| 119 | |
| 120 | # Use type module/name for lazy-loaded types (avoids import until needed) |
| 121 | type_module = getattr(obj_type, "__module__", "") |
| 122 | type_name = obj_type.__name__ |
| 123 | |
| 124 | # datetime module types (check module first to skip unrelated types quickly) |
| 125 | if type_module == "datetime": |
| 126 | if type_name == "datetime": |
| 127 | return {"__type__": "datetime", "data": obj.isoformat()} |
| 128 | if type_name == "date": |
| 129 | return {"__type__": "date", "data": obj.isoformat()} |
| 130 | if type_name == "time": |
| 131 | return {"__type__": "time", "data": obj.isoformat()} |
| 132 | if type_name == "timedelta": |
| 133 | return {"__type__": "timedelta", "total_seconds": obj.total_seconds()} |
no test coverage detected