Add sort_keys keyword for deterministic JSON output#442
Merged
Conversation
Adds `sort_keys::Bool=false` to `JSON.json` and `JSON.print` that produces output with alphabetically sorted dictionary keys. This is a standard feature in JSON libraries across languages (Python's json.dumps(sort_keys=True), Go's encoding/json, etc.) and enables reproducible output for snapshot testing, diffing, and caching. The implementation sorts dict keys by their lowered string representation and works recursively for nested dicts. It correctly handles String, Symbol, and Integer key types, JSON.Object, and composes with all existing options (pretty, omit_null, jsonlines, buffered IO, etc.). Struct and NamedTuple field order is unaffected since it is already deterministic. Closes #437 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #442 +/- ##
==========================================
+ Coverage 90.26% 90.30% +0.04%
==========================================
Files 7 7
Lines 1366 1372 +6
==========================================
+ Hits 1233 1239 +6
Misses 133 133 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Change sort_keys from Bool to Union{Bool, Nothing} with default
nothing. The three-valued semantics:
- nothing (default): sort non-Object AbstractDicts, preserve Object order
- true: sort all AbstractDicts including Object
- false: no sorting for any AbstractDict
This matches Go/Rust behavior (sorted by default) while respecting
the explicit ordering contract of JSON.Object.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
sort_keys::Union{Bool, Nothing}=nothingkeyword argument toJSON.json/JSON.printfor controlling dictionary key orderingnothing): non-ObjectAbstractDictkeys are sorted alphabetically;JSON.Objectpreserves insertion ordersort_keys=true: allAbstractDicttypes sorted, includingObjectsort_keys=false: no sorting for anyAbstractDictStructUtils.lowerkey), so works correctly withString,Symbol,Integer, and custom key typespretty,omit_null,omit_empty,jsonlines, buffered IO, etc.)This is a standard feature across JSON libraries: Python's
json.dumps(sort_keys=True), Go'sencoding/json(always sorted), Rust'sserde_json(sorted by default), Java Jackson'sORDER_MAP_ENTRIES_BY_KEYS, etc.Rationale for default sorting
Julia's
Dicthas non-deterministic iteration order that varies across versions, platforms, and runs. The defaultsort_keys=nothingfollows Go and Rust's lead by sortingDictoutput by default, which eliminates a class of bugs in snapshot testing, diffing, and caching.JSON.Objectis exempted because it was specifically designed for insertion-order preservation — users who chooseObjecthave explicitly opted into a particular key ordering.Usage
Implementation
Three small changes in
src/write.jl:sort_keys::Union{Bool, Nothing} = nothingfield toWriteOptionsjson!, when sorting is active and the value is anAbstractDict, collect and sort keys bylowerkey, then iterate in sorted ordersort_keysthrough the jsonlinesWriteOptionsreconstructionCloses #437
Test plan
Dictsorted,Objectpreserves insertion ordersort_keys=true: all AbstractDicts sorted includingObjectsort_keys=false: no sortingDictinsideObjectand vice versa (mixed behavior)pretty,omit_null,jsonlines🤖 Generated with Claude Code