Repository: serde-rs/json (language: rust)

Issue:
Add a `.sort()` method to expose `IndexMap::sort_keys()`
There isn't a good way that I know of to get sorted output when `preserve_order` is enabled (and disabling it isn't an option), which is important for getting canonical JSON and for comparing JSON strings. `IndexMap` has a `sort_keys`, could this be exposed somehow?

I'm imagining `Value::sort(&mut self)` and `Map::sort(&mut self)`, either of which would just walk the tree and call the methods on all nested items. When `preserve_order` is disabled, this would be a nop.


Discussion / hints:
If you're open to the idea, I can submit a PR

Fix patch:
diff --git a/src/map.rs b/src/map.rs
index 90da86fed..7ff325b9d 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -348,6 +348,28 @@ impl Map<String, Value> {
     {
         self.map.retain(f);
     }
+
+    /// Sorts this map's entries in-place using `str`'s usual ordering.
+    ///
+    /// If serde_json's "preserve_order" feature is not enabled, this method
+    /// does no work because all JSON maps are always kept in a sorted state.
+    ///
+    /// If serde_json's "preserve_order" feature is enabled, this method
+    /// destroys the original source order or insertion order of this map in
+    /// favor of an alphanumerical order that matches how a BTreeMap with the
+    /// same contents would be ordered. This takes **O(n log n + c)** time where
+    /// _n_ is the length of the map and _c_ is the capacity.
+    ///
+    /// Other maps nested within the values of this map are not sorted. If you
+    /// need the entire data structure to be sorted at all levels, you must also
+    /// call `map.`[`values_mut`]`().for_each(`[`Value::sort_all_objects`]`)`.
+    ///
+    /// [`values_mut`]: Map::values_mut
+    #[inline]
+    pub fn sort_keys(&mut self) {
+        #[cfg(feature = "preserve_order")]
+        self.map.sort_unstable_keys();
+    }
 }
 
 #[allow(clippy::derivable_impls)] // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7655
diff --git a/src/value/mod.rs b/src/value/mod.rs
index 026f10dcb..6b40f9a52 100644
--- a/src/value/mod.rs
+++ b/src/value/mod.rs
@@ -861,6 +861,32 @@ impl Value {
     pub fn take(&mut self) -> Value {
         mem::replace(self, Value::Null)
     }
+
+    /// Reorders the entries of all `Value::Object` nested within this JSON
+    /// value according to `str`'s usual ordering.
+    ///
+    /// If serde_json's "preserve_order" feature is not enabled, this method
+    /// does no work because all JSON maps are always kept in a sorted state.
+    ///
+    /// If serde_json's "preserve_order" feature is enabled, this method
+    /// destroys the original source order or insertion order of the JSON
+    /// objects in favor of an alphanumerical order that matches how a BTreeMap
+    /// with the same contents would be ordered.
+    pub fn sort_all_objects(&mut self) {
+        #[cfg(feature = "preserve_order")]
+        {
+            match self {
+                Value::Object(map) => {
+                    map.sort_keys();
+                    map.values_mut().for_each(Value::sort_all_objects);
+                }
+                Value::Array(list) => {
+                    list.iter_mut().for_each(Value::sort_all_objects);
+                }
+                _ => {}
+            }
+        }
+    }
 }
 
 /// The default value is `Value::Null`.


Test patch: