python 41 lines · 8 steps

Recursively diffing two JSON structures

A recursive walk over dicts, lists, and scalars that reports every added, removed, and changed value with a path.

Explained by highlit
1from typing import Any
2 
3_MISSING = object()
4 
5 
6def diff_json(old: Any, new: Any, path: str = "") -> dict[str, list[dict]]:
7 result = {"added": [], "removed": [], "changed": []}
8 
9 if isinstance(old, dict) and isinstance(new, dict):
10 for key in old.keys() | new.keys():
11 child_path = f"{path}.{key}" if path else key
12 old_val = old.get(key, _MISSING)
13 new_val = new.get(key, _MISSING)
14 
15 if old_val is _MISSING:
16 result["added"].append({"path": child_path, "value": new_val})
17 elif new_val is _MISSING:
18 result["removed"].append({"path": child_path, "value": old_val})
19 else:
20 _merge(result, diff_json(old_val, new_val, child_path))
21 elif isinstance(old, list) and isinstance(new, list):
22 for index in range(max(len(old), len(new))):
23 child_path = f"{path}[{index}]"
24 old_val = old[index] if index < len(old) else _MISSING
25 new_val = new[index] if index < len(new) else _MISSING
26 
27 if old_val is _MISSING:
28 result["added"].append({"path": child_path, "value": new_val})
29 elif new_val is _MISSING:
30 result["removed"].append({"path": child_path, "value": old_val})
31 else:
32 _merge(result, diff_json(old_val, new_val, child_path))
33 elif old != new:
34 result["changed"].append({"path": path, "from": old, "to": new})
35 
36 return result
37 
38 
39def _merge(target: dict[str, list], source: dict[str, list]) -> None:
40 for kind, entries in source.items():
41 target[kind].extend(entries)
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A unique sentinel object distinguishes 'key absent' from a legitimate None or falsy value.
  2. 2Recursion mirrors the shape of nested data, threading a path string down to describe each leaf.
  3. 3Comparing the union of keys catches additions and removals symmetrically in one pass.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Recursively diffing two JSON structures — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code