#!/usr/bin/env python3 """JSON to CSV converter with field selection and CLI.""" from __future__ import annotations import argparse, csv, json, sys from pathlib import Path def load_rows(path: Path): data = json.loads(path.read_text(encoding='utf-8')) if isinstance(data, dict): # common wrappers for k in ('data','items','results','records'): if isinstance(data.get(k), list): data = data[k] break else: data = [data] if not isinstance(data, list): raise SystemExit('JSON root must be a list or object with a list field') return data def main(): p = argparse.ArgumentParser(description='Convert JSON array to CSV') p.add_argument('input', type=Path) p.add_argument('-o','--output', type=Path, required=True) p.add_argument('-f','--fields', help='Comma-separated fields (default: union of keys)') args = p.parse_args() rows = load_rows(args.input) if not rows: print('empty input', file=sys.stderr); return 1 if args.fields: fields = [x.strip() for x in args.fields.split(',') if x.strip()] else: fields = [] seen=set() for row in rows: if isinstance(row, dict): for k in row: if k not in seen: seen.add(k); fields.append(k) args.output.parent.mkdir(parents=True, exist_ok=True) with args.output.open('w', newline='', encoding='utf-8') as f: w = csv.DictWriter(f, fieldnames=fields, extrasaction='ignore') w.writeheader() for row in rows: if not isinstance(row, dict): raise SystemExit('rows must be objects') w.writerow({k: row.get(k, '') for k in fields}) print(f'wrote {len(rows)} rows -> {args.output}') return 0 if __name__ == '__main__': raise SystemExit(main())