json_loader.py
1 """JSON file loading utilities.""" 2 3 from __future__ import annotations 4 5 import json 6 import logging 7 from pathlib import Path 8 from typing import Any 9 10 logger = logging.getLogger(__name__) 11 12 13 def load_json_file(file_path: Path | str | None) -> dict[str, Any] | None: 14 """Load a JSON file safely with proper error handling. 15 16 Parameters 17 ---------- 18 file_path 19 Path to the JSON file. Can be None, in which case None is returned. 20 21 Returns 22 ------- 23 dict[str, Any] | None 24 Parsed JSON data as a dictionary, or None if: 25 - file_path is None 26 - File doesn't exist 27 - File can't be parsed as JSON 28 - JSON root is not a dictionary 29 """ 30 if file_path is None: 31 return None 32 33 try: 34 path = Path(file_path).expanduser().resolve() 35 36 if not path.exists(): 37 logger.warning(f"JSON file not found: {path}") 38 return None 39 40 with path.open() as f: 41 data = json.load(f) 42 43 if not isinstance(data, dict): 44 logger.error(f"Invalid JSON format: expected dict, got {type(data).__name__}") 45 return None 46 47 return data 48 49 except json.JSONDecodeError as e: 50 raise ValueError(f"Failed to parse JSON file '{file_path}': {e}") from e 51 except Exception as e: 52 raise ValueError(f"Failed to load JSON file '{file_path}': {e}") from e