Source code for neosqlite.collection.json_helpers

import json
import logging
import re
from datetime import datetime
from typing import Any

from neosqlite.binary import Binary

logger = logging.getLogger(__name__)

# Pre-compile ISO date pattern for performance
ISO_DATE_PATTERN = re.compile(
    r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$"
)


[docs] class NeoSQLiteJSONEncoder(json.JSONEncoder): """ Custom JSON encoder for NeoSQLite that handles Binary, ObjectId, and datetime objects. """
[docs] def default(self, obj): """ Encodes Binary, ObjectId, and datetime objects for JSON serialization. Args: obj: The object to encode. Returns: The encoded object suitable for JSON serialization. """ if isinstance(obj, Binary): return obj.encode_for_storage() # Import here to avoid circular imports try: from neosqlite.objectid import ObjectId if isinstance(obj, ObjectId): return obj.encode_for_storage() except ImportError as e: logger.debug(f"ObjectId module not available for encoding: {e}") pass # ObjectId module not available # Handle datetime objects - convert to ISO format string if isinstance(obj, datetime): return obj.isoformat() return super().default(obj)
[docs] def neosqlite_json_dumps(obj: Any, **kwargs) -> str: """ Custom JSON dumps function that handles Binary objects. Args: obj: Object to serialize **kwargs: Additional arguments to pass to json.dumps Returns: JSON string representation """ return json.dumps(obj, cls=NeoSQLiteJSONEncoder, **kwargs)
[docs] def neosqlite_json_dumps_for_sql(obj: Any, **kwargs) -> str: """ Custom JSON dumps function for SQL query parameters that handles Binary objects using compact formatting to match SQLite's json_extract behavior. Args: obj: Object to serialize **kwargs: Additional arguments to pass to json.dumps Returns: JSON string representation in compact format """ # Use compact JSON formatting to match SQLite's json_extract behavior kwargs.setdefault("separators", (",", ":")) return json.dumps(obj, cls=NeoSQLiteJSONEncoder, **kwargs)
[docs] def neosqlite_json_loads(s: str, **kwargs) -> Any: """ Custom JSON loads function that handles Binary objects and ISO date strings. For MongoDB compatibility, ISO 8601 date strings are automatically converted back to datetime objects, matching MongoDB's behavior where dates are stored as BSON Date type and returned as datetime objects. Args: s: JSON string to deserialize **kwargs: Additional arguments to pass to json.loads Returns: Deserialized object """ def object_hook(dct: dict[str, Any]) -> Any: """ Decodes Binary objects, ObjectId objects, and ISO date strings from JSON deserialization. Args: dct: The dictionary to decode. Returns: The decoded object or the original dictionary if no Binary/ObjectId object is found. """ if isinstance(dct, dict): if "__neosqlite_binary__" in dct: return Binary.decode_from_storage(dct) if "__neosqlite_objectid__" in dct: try: from neosqlite.objectid import ObjectId return ObjectId(dct["id"]) except (ValueError, ImportError, KeyError) as e: logger.debug(f"{e=}") pass # Convert ISO date strings back to datetime for MongoDB compatibility for key, value in dct.items(): if isinstance(value, str) and ISO_DATE_PATTERN.match(value): try: dct[key] = datetime.fromisoformat( value.replace("Z", "+00:00") ) except ValueError as e: logger.debug( f"Failed to parse ISO date string '{value}': {e}" ) pass # Not a valid date string, keep as string return dct kwargs["object_hook"] = object_hook return json.loads(s, **kwargs)