#!/usr/bin/env python3 """ JSON Formatter Utility Validates and pretty-prints JSON files with customizable indentation. Supports file input or stdin piping. Usage: json_formatter.py [indent] # format file echo '{}' | json_formatter.py # format from stdin json_formatter.py invalid.json # validate (exit 1 if invalid) """ import json import sys from pathlib import Path def format_json(input_path: str = None, content: str = None, indent: int = 2) -> str: """ Read JSON from file or string, validate it, and return formatted output. Args: input_path: Path to the JSON file (mutually exclusive with content) content: Raw JSON string content (mutually exclusive with input_path) indent: Number of spaces for indentation (default: 2) Returns: Formatted JSON string Raises: FileNotFoundError: If file doesn't exist json.JSONDecodeError: If file contains invalid JSON """ if content is None: path = Path(input_path) if not path.exists(): raise FileNotFoundError(f"File not found: {input_path}") with open(path, 'r', encoding='utf-8') as f: data = json.load(f) else: data = json.loads(content) # Validate by re-serializing json.dumps(data) # Will raise if data contains non-serializable types return json.dumps(data, indent=indent, ensure_ascii=False, sort_keys=True) def main(): input_file = sys.argv[1] if len(sys.argv) > 1 else None indent = int(sys.argv[2]) if len(sys.argv) > 2 else 2 try: if input_file: formatted = format_json(input_path=input_file, indent=indent) else: # Read from stdin content = sys.stdin.read() if not content.strip(): print("Error: No input provided", file=sys.stderr) sys.exit(1) formatted = format_json(content=content, indent=indent) print(formatted) sys.exit(0) except FileNotFoundError as e: print(f"Error: {e}", file=sys.stderr) sys.exit(2) except json.JSONDecodeError as e: print(f"JSON Error: {e.msg} at line {e.lineno}, column {e.colno}", file=sys.stderr) sys.exit(3) except Exception as e: print(f"Unexpected error: {e}", file=sys.stderr) sys.exit(4) if __name__ == "__main__": main()