Preserve list order when it has domain meaning
To preserve list order when it has domain meaning in Python, do not run a whole JSON document through a recursive canonicalizer that sorts every list. Keep JSON arrays in their incoming order; if you need deterministic JSON text, restrict canonicalization to object-key ordering with sort_keys=True.
This fixture shows a concrete boundary: a request normalizer receives JSON for a workflow. Its faulty canonicalize_everything() function sorts every list before emitting the normalized request. That changes the steps array from validate, write, verify to validate, verify, write; the simulated executor therefore records a different execution order. This is not a decoder failure—json.loads() first produces the correct Python list. The failure happens at the normalizing interface, where a broad determinism rule is applied to data whose order is contractual.
The repaired path serializes the decoded request with json.dumps(..., sort_keys=True). That makes object-key order stable in the JSON text while leaving list elements untouched. The assertions check both properties: the corrected JSON has its top-level keys alphabetized, and the decoded steps value exactly matches the original three-step sequence. They do not prove that another service will treat these names as workflow instructions; that remains the API contract.
The Python json documentation maps JSON arrays to Python lists and documents sort_keys; the list documentation describes lists as ordered collections. These APIs are long-standing; Python 3.8+ is a practical minimum for this example.
AI assistance disclosure: this article was drafted with AI assistance and uses a synthetic in-memory request fixture.
import json
request_text = (
'{"workflow":"release","steps":["validate","write","verify"],'
'"metadata":{"run_id":7,"team":"docs"}}'
)
request = json.loads(request_text)
def canonicalize_everything(value):
if isinstance(value, dict):
return {key: canonicalize_everything(value[key]) for key in sorted(value)}
if isinstance(value, list):
return sorted(canonicalize_everything(item) for item in value)
return value
def execute(workflow_request):
return " -> ".join(workflow_request["steps"])
wrong_request = canonicalize_everything(request)
wrong_execution = execute(wrong_request)
assert wrong_request["steps"] == ["validate", "verify", "write"]
assert wrong_execution == "validate -> verify -> write"
corrected_text = json.dumps(request, sort_keys=True, separators=(",", ":"))
corrected_request = json.loads(corrected_text)
corrected_execution = execute(corrected_request)
assert corrected_text.startswith('{"metadata":')
assert corrected_request["steps"] == ["validate", "write", "verify"]
assert corrected_execution == "validate -> write -> verify"
print(wrong_execution)
print(corrected_text)
print(corrected_execution)
validate -> verify -> write
{"metadata":{"run_id":7,"team":"docs"},"steps":["validate","write","verify"],"workflow":"release"}
validate -> write -> verify