jmesflat is a Python library that flattens, unflattens, and merges deeply nested JSON objects using JMESPath-style keys (e.g. a.b[0].c). Build and combine complex nested structures without manual layer-by-layer construction.
jmesflat extends jmespath with four main operations:
| Operation | Description |
|---|---|
| flatten | Turn nested dicts/lists into flat key–value pairs with dotted and bracket notation. |
| unflatten | Rebuild nested objects from flat keys (e.g. build {"a": {"b": [{"c": 1}]}} from {"a.b[0].c": 1}). |
| merge | Deep-merge two nested structures with configurable array behavior (overwrite, extend top/bottom, deduped). |
| clean | Recursively filter nested data with a custom discard function. |
Use it when you need to flatten JSON for storage or APIs, build nested objects from flat configs, or merge nested JSON without hand-written recursion.
>>> import jmesflat as jf
>>> # Build nested object from flat keys
>>> jf.unflatten({"a.b[0].c": "x", "a.b[1].d": "y"})
{'a': {'b': [{'c': 'x'}, {'d': 'y'}]}}
>>> # Flatten for compact, readable output
>>> jf.flatten({"a": {"b": [{"c": "x"}, {"d": "y"}]})
{'a.b[0].c': 'x', 'a.b[1].d': 'y'}
>>> # Deep-merge two nested objects
>>> jf.merge({"a": {"x": 1}}, {"a": {"y": 2}})
{'a': {'x': 1, 'y': 2}}pip install jmesflatRequirements: Python 3.10+, jmespath.
- JMESPath-style keys — Dotted paths and bracket indices:
a.b[0].c,"Outer Key.inner". - Keys with spaces and special chars — Supports keys containing spaces,
@, and-. - Arbitrary nesting — Mixed and multi-level arrays and objects.
- Depth control — Optional
levelparameter to flatten, unflatten, or merge up to a given object depth. - Array merge modes —
overwrite,topdown,bottomup, ordedupedwhen merging. - Filter while transforming — Use
discard_checkin flatten/unflatten/merge, or useclean()alone to scrub nested data. - Atomic types — Empty lists
[]and dicts{}are treated as atomic values in flattened output.
>>> import jmesflat as jf
>>> nest = jf.unflatten({"a.b[0].c[0].d": "e", "a.b[1].f": "g"})
>>> nest
{'a': {'b': [{'c': [{'d': 'e'}]}, {'f': 'g'}]}}>>> nest1 = {"a": {"b": [{"c": [{"d": "e"}]}, {"f": "g"}]}}
>>> nest2 = {"a": {"b": [{"f": "g"}, {"c": [{"d": "e"}]}]}}
>>> jf.merge(nest1, nest2)
{'a': {'b': [{'c': [{'d': 'e'}], 'f': 'g'}, {'c': [{'d': 'e'}], 'f': 'g'}]}}>>> import json
>>> nested = {"a": {"b": [{"c": "x"}, {"d": "y"}]}}
>>> print(json.dumps(jf.flatten(nested), indent=2))
{
"a.b[0].c": "x",
"a.b[1].d": "y"
}>>> import json
>>> import jmesflat as jf
>>>
>>> data = {
... "Outer Key 1": {
... "arr": ["a", {"nested": "val"}, 123],
... },
... "Outer Key 2": {"deep": {"a": [{"b": 1}, {"c": 2}]}},
... }
>>> flat = jf.flatten(data, level=1)
>>> print(json.dumps(flat, indent=2))
{
"Outer Key 1": {
"arr[0]": "a",
"arr[1].nested": "val",
"arr[2]": 123
},
"Outer Key 2": {
"deep.a[0].b": 1,
"deep.a[1].c": 2
}
}
>>> jf.unflatten(flat, level=1) == data
True- overwrite (default) — Second structure overwrites at the first array.
- topdown — Extend arrays at the first array level.
- bottomup — Extend at the innermost array level.
- deduped — Merge arrays with custom matching to avoid duplicates.
Use the array_merge parameter of merge():
>>> jf.merge(nest1, nest2, array_merge="topdown") # extend at first array
>>> jf.merge(nest1, nest2, array_merge="bottomup") # extend at last array>>> # Keep only string and int values, drop keys containing "-"
>>> cleaned = jf.clean(
... data,
... discard_check=lambda key, val: "-" in key or not isinstance(val, (str, int)),
... )Set global defaults via jmesflat.constants:
DISCARD_CHECK— Default filter forclean()and for the second object inmerge().
Example: drop allNonevalues:import jmesflat as jf jf.constants.DISCARD_CHECK = lambda _, val: val is None
MISSING_ARRAY_ENTRY_VALUE— Callable used when unflattening to pad array indices.
It receives the flattened key and the value being set and returns the padding value.
Other constants in the module are for advanced use and may change in future versions.
| Module | Statements | Missing | Coverage |
|---|---|---|---|
| jmesflat/init.py | 7 | 0 | 100% |
| jmesflat/_clean.py | 11 | 0 | 100% |
| jmesflat/_flatten.py | 39 | 0 | 100% |
| jmesflat/_merge.py | 41 | 0 | 100% |
| jmesflat/_unflatten.py | 50 | 0 | 100% |
| jmesflat/constants.py | 8 | 0 | 100% |
| jmesflat/utils.py | 29 | 0 | 100% |
| Total | 185 | 0 | 100% |
Run tests:
pip install pytest pytest-cov
pytest tests/ --cov=jmesflat --cov-report=term-missing