Allow custom sorting function for JSON serializer
authorDylan Yudaken <dylany@fb.com>
Tue, 28 Mar 2017 15:02:42 +0000 (08:02 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Tue, 28 Mar 2017 15:06:59 +0000 (08:06 -0700)
Summary: In some situations it is useful to be able to sort the JSON keys according to some different scheme for readability, so allow the sorting function to be changed by the serializer_opts

Reviewed By: yfeldblum

Differential Revision: D4782077

fbshipit-source-id: 032fa60a38804452bd1c22c67ba897521cb2cd1d

folly/json.cpp
folly/json.h
folly/test/JsonTest.cpp

index 48ef486..867392f 100644 (file)
@@ -113,12 +113,17 @@ private:
     out_ += '{';
     indent();
     newline();
-    if (opts_.sort_keys) {
+    if (opts_.sort_keys || opts_.sort_keys_by) {
       using ref = std::reference_wrapper<decltype(o.items())::value_type const>;
       std::vector<ref> refs(o.items().begin(), o.items().end());
-      std::sort(refs.begin(), refs.end(), [](ref a, ref b) {
+
+      using SortByRef = FunctionRef<bool(dynamic const&, dynamic const&)>;
+      auto const& sort_keys_by = opts_.sort_keys_by
+          ? SortByRef(opts_.sort_keys_by)
+          : SortByRef(std::less<dynamic>());
+      std::sort(refs.begin(), refs.end(), [&](ref a, ref b) {
         // Only compare keys.  No ordering among identical keys.
-        return a.get().first < b.get().first;
+        return sort_keys_by(a.get().first, b.get().first);
       });
       printKVPairs(refs.cbegin(), refs.cend());
     } else {
index 87a9078..788587a 100644 (file)
@@ -43,8 +43,9 @@
 #include <iosfwd>
 #include <string>
 
-#include <folly/dynamic.h>
+#include <folly/Function.h>
 #include <folly/Range.h>
+#include <folly/dynamic.h>
 
 namespace folly {
 
@@ -95,8 +96,14 @@ namespace json {
     bool allow_trailing_comma;
 
     // Sort keys of all objects before printing out (potentially slow)
+    // using dynamic::operator<.
+    // Has no effect if sort_keys_by is set.
     bool sort_keys;
 
+    // Sort keys of all objects before printing out (potentially slow)
+    // using the provided less functor.
+    Function<bool(dynamic const&, dynamic const&) const> sort_keys_by;
+
     // Replace invalid utf8 characters with U+FFFD and continue
     bool skip_invalid_utf8;
 
index df28313..f6dfc35 100644 (file)
@@ -438,10 +438,17 @@ TEST(Json, ParseNumbersAsStrings) {
 }
 
 TEST(Json, SortKeys) {
-  folly::json::serialization_opts opts_on, opts_off;
+  folly::json::serialization_opts opts_on, opts_off, opts_custom_sort;
   opts_on.sort_keys = true;
   opts_off.sort_keys = false;
 
+  opts_custom_sort.sort_keys = false; // should not be required
+  opts_custom_sort.sort_keys_by = [](
+      folly::dynamic const& a, folly::dynamic const& b) {
+    // just an inverse sort
+    return b < a;
+  };
+
   dynamic value = dynamic::object
     ("foo", "bar")
     ("junk", 12)
@@ -462,10 +469,18 @@ TEST(Json, SortKeys) {
     R"({"a":[{"a":"b","c":"d"},12.5,"Yo Dawg",["heh"],null],)"
     R"("another":32.2,"foo":"bar","junk":12})";
 
+  std::string inverse_sorted_keys =
+      R"({"junk":12,"foo":"bar","another":32.2,)"
+      R"("a":[{"c":"d","a":"b"},12.5,"Yo Dawg",["heh"],null]})";
+
   EXPECT_EQ(value, parseJson(folly::json::serialize(value, opts_on)));
   EXPECT_EQ(value, parseJson(folly::json::serialize(value, opts_off)));
+  EXPECT_EQ(value, parseJson(folly::json::serialize(value, opts_custom_sort)));
 
   EXPECT_EQ(sorted_keys, folly::json::serialize(value, opts_on));
+  EXPECT_NE(sorted_keys, folly::json::serialize(value, opts_off));
+  EXPECT_EQ(
+      inverse_sorted_keys, folly::json::serialize(value, opts_custom_sort));
 }
 
 TEST(Json, PrintTo) {