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 48ef486ebc87d3f17195f6d15c77d95726371121..867392f45c97eeaef1dc57bf7dd7491adcd5f5e3 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 87a907886a3305512465494b9b0c1388d020ed19..788587a8aa6defbabf2b8ef69b91f07f5ceee278 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 df2831347456e183571c51720b22ebf260e74d76..f6dfc354da35a86a266df1660d346c3a3a5f5e9b 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) {