spb/ultra-sharp-agent-skills Public
Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.
Python 100%
1<!--2Author: Simon-Pierre Boucher3Contact: contact@spboucher.ai4-->56# JSON Recipes78## Contents9- Create10- Read / query11- Modify12- Convert (json ↔ jsonl, json → CSV)13- Validate14- Gotchas1516## Create1718**New file with non-ASCII content kept readable:**19```python20import json21data = {"name": "Café Müller", "items": [1, 2, 3]}22with open("out.json", "w", encoding="utf-8") as f:23 json.dump(data, f, indent=2, ensure_ascii=False)24 f.write("\n") # trailing newline: POSIX-friendly diffs25```2627**Compact machine-to-machine output:**28```python29json.dumps(data, separators=(",", ":"), ensure_ascii=False)30```3132## Read / query3334**Safe nested access:**35```python36value = data.get("config", {}).get("db", {}).get("host") # None if any level missing37```3839**Filter a list of records:**40```python41active = [r for r in data["users"] if r.get("active")]42```4344**jq equivalents for large files (jq must be installed):**45```bash46jq '.users[] | select(.active) | .email' big.json # filter + project47jq 'length' big.json # count48jq -r '.items[].id' big.json # raw strings, no quotes49```5051**Detect duplicate keys while parsing:**52```python53def no_dupes(pairs):54 keys = [k for k, _ in pairs]55 dupes = {k for k in keys if keys.count(k) > 1}56 if dupes:57 raise ValueError(f"duplicate keys: {sorted(dupes)}")58 return dict(pairs)5960data = json.load(open("in.json", encoding="utf-8"), object_pairs_hook=no_dupes)61```6263## Modify6465**Atomic in-place update (the only sanctioned write pattern for existing files):**66```python67import json, os, tempfile6869def update_json(path, mutate):70 with open(path, encoding="utf-8") as f:71 data = json.load(f)72 mutate(data)73 fd, tmp = tempfile.mkstemp(dir=os.path.dirname(os.path.abspath(path)))74 with os.fdopen(fd, "w", encoding="utf-8") as f:75 json.dump(data, f, indent=2, ensure_ascii=False)76 f.write("\n")77 os.replace(tmp, path) # atomic on POSIX7879update_json("config.json", lambda d: d.setdefault("features", {}).update(dark_mode=True))80```8182**Deep merge (dict-over-dict, right side wins):**83```python84def deep_merge(base, override):85 out = dict(base)86 for k, v in override.items():87 out[k] = deep_merge(out[k], v) if isinstance(out.get(k), dict) and isinstance(v, dict) else v88 return out89```9091**Shallow diff of two objects:**92```python93def diff(a, b):94 keys = a.keys() | b.keys()95 return {k: (a.get(k), b.get(k)) for k in keys if a.get(k) != b.get(k)}96```9798## Convert99100**jsonl → json array:**101```python102records = [json.loads(l) for l in open("in.jsonl", encoding="utf-8") if l.strip()]103json.dump(records, open("out.json", "w", encoding="utf-8"), indent=2, ensure_ascii=False)104```105106**json array → jsonl (streams better, appends safely):**107```python108with open("out.jsonl", "w", encoding="utf-8") as f:109 for r in json.load(open("in.json", encoding="utf-8")):110 f.write(json.dumps(r, ensure_ascii=False) + "\n")111```112113**Flat records → CSV** (nested values must be flattened or stringified first):114```python115import csv116rows = json.load(open("in.json", encoding="utf-8"))117fields = sorted({k for r in rows for k in r})118with open("out.csv", "w", newline="", encoding="utf-8") as f:119 w = csv.DictWriter(f, fieldnames=fields)120 w.writeheader(); w.writerows(rows)121```122123**Flatten nested keys with dots:**124```python125def flatten(d, prefix=""):126 out = {}127 for k, v in d.items():128 key = f"{prefix}{k}"129 out.update(flatten(v, key + ".")) if isinstance(v, dict) else out.setdefault(key, v)130 return out131```132133## Validate134135**Syntax:** `python3 -m json.tool file.json > /dev/null` — prints the error with line/column, exit 1 on failure.136137**Schema:**138```python139# pip install jsonschema140from jsonschema import validate, ValidationError141try:142 validate(instance=data, schema=schema)143except ValidationError as e:144 print(f"invalid at {list(e.absolute_path)}: {e.message}")145```146147## Gotchas148- **Trailing commas, single quotes, comments** are the top three causes of `JSONDecodeError` — they are JavaScript habits, not JSON. Fix the exact character the error points at.149- **`NaN`, `Infinity`**: `json.dumps` emits them by default but no strict parser accepts them. Use `allow_nan=False` to force the error at write time, then substitute `null`.150- **Encoding:** JSON files are UTF-8 by spec; always pass `encoding="utf-8"` — Windows defaults to cp1252 and corrupts round-trips.151- **Float precision:** `json.load` gives you binary floats (`0.1 + 0.2 != 0.3`); for money, parse with `json.load(f, parse_float=decimal.Decimal)`.152- **Large ints** round-trip fine in Python but break JavaScript beyond 2^53 − 1; stringify IDs above that when the consumer is JS.153- **`sort_keys=True` rewrites the whole file's order** — a huge diff for a one-key change. Leave order alone unless asked.154- **BOM:** files from Windows tools may start with U+FEFF; open with `encoding="utf-8-sig"` if `json.load` fails on character 0.155