From edb6b25569b49d5b907d4df8e24abaf71c6d0dbd Mon Sep 17 00:00:00 2001 From: Evan Driscoll Date: Mon, 4 Jun 2018 21:59:21 -0500 Subject: [PATCH] Fancy printer predicate now takes current JSON object The API is ugly at the moment. Will fix with an ugly implementation soon. --- .../detail/output/fancy_serializer.hpp | 56 +++++++++++--- single_include/nlohmann/json.hpp | 56 +++++++++++--- test/src/unit-fancy-serialization.cpp | 73 ++++++++++++++----- 3 files changed, 147 insertions(+), 38 deletions(-) diff --git a/include/nlohmann/detail/output/fancy_serializer.hpp b/include/nlohmann/detail/output/fancy_serializer.hpp index 80a4c441a..7949db491 100644 --- a/include/nlohmann/detail/output/fancy_serializer.hpp +++ b/include/nlohmann/detail/output/fancy_serializer.hpp @@ -43,19 +43,31 @@ struct fancy_serializer_style bool multiline = false; - void set_old_multiline() - { - space_after_colon = space_after_comma = multiline = true; - } + fancy_serializer_style() = default; + + fancy_serializer_style(bool s_colon, bool s_comma, bool ml) + : space_after_colon(s_colon), space_after_comma(s_comma), multiline(ml) + {} + + static const fancy_serializer_style preset_compact; + static const fancy_serializer_style preset_one_line; + static const fancy_serializer_style preset_multiline; }; +const fancy_serializer_style fancy_serializer_style::preset_compact(false, false, false); +const fancy_serializer_style fancy_serializer_style::preset_one_line(true, true, false); +const fancy_serializer_style fancy_serializer_style::preset_multiline(true, true, true); + template class basic_fancy_serializer_stylizer { public: using string_t = typename BasicJsonType::string_t; using json_pointer_t = json_pointer; - using matcher_predicate = std::function; + + using json_matcher_predicate = std::function; + using context_matcher_predicate = std::function; + using matcher_predicate = std::function; basic_fancy_serializer_stylizer(fancy_serializer_style const& ds) : default_style(ds) @@ -76,11 +88,12 @@ class basic_fancy_serializer_stylizer const fancy_serializer_style* get_new_style_or_active( const json_pointer_t& pointer, + const json& j, const fancy_serializer_style* active_style) const { for (auto const& pair : styles) { - if (pair.first(pointer)) + if (pair.first(pointer, j)) { return &pair.second; } @@ -96,15 +109,40 @@ class basic_fancy_serializer_stylizer return styles.back().second; } + fancy_serializer_style& register_style( + json_matcher_predicate p, + fancy_serializer_style style = fancy_serializer_style()) + { + auto wrapper = [p](const json_pointer_t&, const BasicJsonType & j) + { + return p(j); + }; + styles.emplace_back(wrapper, style); + return styles.back().second; + } + + fancy_serializer_style& register_style( + context_matcher_predicate p, + fancy_serializer_style style = fancy_serializer_style()) + { + auto wrapper = [p](const json_pointer_t& c, const BasicJsonType&) + { + return p(c); + }; + styles.emplace_back(wrapper, style); + return styles.back().second; + } + fancy_serializer_style& register_key_matcher_style( string_t str, fancy_serializer_style style = fancy_serializer_style()) { - return register_style([str](const json_pointer_t& pointer) + using pred = context_matcher_predicate; + return register_style(pred([str](const json_pointer_t& pointer) { return (pointer.cbegin() != pointer.cend()) && (*pointer.crbegin() == str); - }, + }), style); } @@ -180,7 +218,7 @@ class fancy_serializer const fancy_serializer_style* active_style, const json_pointer_t& context) { - active_style = stylizer.get_new_style_or_active(context, active_style); + active_style = stylizer.get_new_style_or_active(context, val, active_style); switch (val.m_type) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 3165d8129..f94f42998 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -10848,19 +10848,31 @@ struct fancy_serializer_style bool multiline = false; - void set_old_multiline() - { - space_after_colon = space_after_comma = multiline = true; - } + fancy_serializer_style() = default; + + fancy_serializer_style(bool s_colon, bool s_comma, bool ml) + : space_after_colon(s_colon), space_after_comma(s_comma), multiline(ml) + {} + + static const fancy_serializer_style preset_compact; + static const fancy_serializer_style preset_one_line; + static const fancy_serializer_style preset_multiline; }; +const fancy_serializer_style fancy_serializer_style::preset_compact(false, false, false); +const fancy_serializer_style fancy_serializer_style::preset_one_line(true, true, false); +const fancy_serializer_style fancy_serializer_style::preset_multiline(true, true, true); + template class basic_fancy_serializer_stylizer { public: using string_t = typename BasicJsonType::string_t; using json_pointer_t = json_pointer; - using matcher_predicate = std::function; + + using json_matcher_predicate = std::function; + using context_matcher_predicate = std::function; + using matcher_predicate = std::function; basic_fancy_serializer_stylizer(fancy_serializer_style const& ds) : default_style(ds) @@ -10881,11 +10893,12 @@ class basic_fancy_serializer_stylizer const fancy_serializer_style* get_new_style_or_active( const json_pointer_t& pointer, + const json& j, const fancy_serializer_style* active_style) const { for (auto const& pair : styles) { - if (pair.first(pointer)) + if (pair.first(pointer, j)) { return &pair.second; } @@ -10901,15 +10914,40 @@ class basic_fancy_serializer_stylizer return styles.back().second; } + fancy_serializer_style& register_style( + json_matcher_predicate p, + fancy_serializer_style style = fancy_serializer_style()) + { + auto wrapper = [p](const json_pointer_t&, const BasicJsonType & j) + { + return p(j); + }; + styles.emplace_back(wrapper, style); + return styles.back().second; + } + + fancy_serializer_style& register_style( + context_matcher_predicate p, + fancy_serializer_style style = fancy_serializer_style()) + { + auto wrapper = [p](const json_pointer_t& c, const BasicJsonType&) + { + return p(c); + }; + styles.emplace_back(wrapper, style); + return styles.back().second; + } + fancy_serializer_style& register_key_matcher_style( string_t str, fancy_serializer_style style = fancy_serializer_style()) { - return register_style([str](const json_pointer_t& pointer) + using pred = context_matcher_predicate; + return register_style(pred([str](const json_pointer_t& pointer) { return (pointer.cbegin() != pointer.cend()) && (*pointer.crbegin() == str); - }, + }), style); } @@ -10985,7 +11023,7 @@ class fancy_serializer const fancy_serializer_style* active_style, const json_pointer_t& context) { - active_style = stylizer.get_new_style_or_active(context, active_style); + active_style = stylizer.get_new_style_or_active(context, val, active_style); switch (val.m_type) { diff --git a/test/src/unit-fancy-serialization.cpp b/test/src/unit-fancy-serialization.cpp index 5b287f33a..2c9954072 100644 --- a/test/src/unit-fancy-serialization.cpp +++ b/test/src/unit-fancy-serialization.cpp @@ -203,7 +203,8 @@ TEST_CASE("serialization") auto str_flat = fancy_to_string({1, {1}}, style); CHECK(str_flat == "[1,[...]]"); - style.set_old_multiline(); + style = fancy_serializer_style::preset_multiline; + style.depth_limit = 1; auto str_lines = fancy_to_string({1, {1}}, style); CHECK(str_lines == dedent(R"( [ @@ -220,7 +221,8 @@ TEST_CASE("serialization") auto str_flat = fancy_to_string({1, {{"one", 1}}}, style); CHECK(str_flat == "[1,{...}]"); - style.set_old_multiline(); + style = fancy_serializer_style::preset_multiline; + style.depth_limit = 1; auto str_lines = fancy_to_string({1, {{"one", 1}}}, style); CHECK(str_lines == dedent(R"( [ @@ -235,8 +237,8 @@ TEST_CASE("serialization") SECTION("can style objects of a key differently") { fancy_serializer_stylizer stylizer; - stylizer.get_default_style().set_old_multiline(); - stylizer.register_key_matcher_style("one line").multiline = false; + stylizer.get_default_style() = fancy_serializer_style::preset_multiline; + stylizer.register_key_matcher_style("one line"); auto str = fancy_to_string( { @@ -262,8 +264,8 @@ TEST_CASE("serialization") SECTION("changes propagate (unless overridden)") { fancy_serializer_stylizer stylizer; - stylizer.get_default_style().set_old_multiline(); - stylizer.register_key_matcher_style("one line").indent_step = 0; + stylizer.get_default_style() = fancy_serializer_style::preset_multiline; + stylizer.register_key_matcher_style("one line"); auto str = fancy_to_string( { @@ -279,18 +281,20 @@ TEST_CASE("serialization") })")); } - SECTION("example of more sophisticated matcher") + SECTION("example of more sophisticated context matcher") { + using pred = fancy_serializer_stylizer::context_matcher_predicate; + fancy_serializer_stylizer stylizer; - stylizer.get_default_style().set_old_multiline(); + stylizer.get_default_style() = fancy_serializer_style::preset_multiline; stylizer.register_style( - [] (const json_pointer& context) + pred([] (const json_pointer& context) { // Matches if context[-2] is "each elem on one line" return (context.cend() - context.cbegin() >= 2) && (*(context.cend() - 2) == "each elem on one line"); - } + }) ).space_after_comma = true; auto str = fancy_to_string( @@ -324,6 +328,40 @@ TEST_CASE("serialization") ] })")); } + + SECTION("example of more sophisticated json matcher") + { + using pred = fancy_serializer_stylizer::json_matcher_predicate; + + fancy_serializer_stylizer stylizer; + stylizer.get_default_style() = fancy_serializer_style::preset_multiline; + + stylizer.register_style( + pred([] (const json & j) + { + return j.type() == json::value_t::array; + }) + ) = fancy_serializer_style::preset_one_line; + + auto str = fancy_to_string( + { + { + "an array", {1, 2, 3} + }, + { + "an object", {{"key", "val"}} + } + }, + stylizer); + + CHECK(str == dedent(R"( + { + "an array": [1, 2, 3], + "an object": { + "key": "val" + } + })")); + } } SECTION("Spaces after commas are controllable separately from multiline") @@ -346,8 +384,7 @@ TEST_CASE("serialization") SECTION("multiline can have no space") { - fancy_serializer_style style; - style.set_old_multiline(); + fancy_serializer_style style = fancy_serializer_style::preset_multiline; style.space_after_colon = false; auto str = fancy_to_string({{"one", 1}}, style); CHECK(str == dedent(R"( @@ -360,8 +397,7 @@ TEST_CASE("serialization") SECTION("given width") { - fancy_serializer_style style; - style.set_old_multiline(); + fancy_serializer_style style = fancy_serializer_style::preset_multiline; auto str = fancy_to_string({"foo", 1, 2, 3, false, {{"one", 1}}}, style); CHECK(str == dedent(R"( [ @@ -378,10 +414,9 @@ TEST_CASE("serialization") SECTION("given fill") { - fancy_serializer_style style; + fancy_serializer_style style = fancy_serializer_style::preset_multiline; style.indent_step = 1; style.indent_char = '\t'; - style.set_old_multiline(); auto str = fancy_to_string({"foo", 1, 2, 3, false, {{"one", 1}}}, style); CHECK(str == @@ -400,10 +435,9 @@ TEST_CASE("serialization") SECTION("indent_char is honored for deep indents in lists") { - fancy_serializer_style style; + fancy_serializer_style style = fancy_serializer_style::preset_multiline; style.indent_step = 300; style.indent_char = 'X'; - style.set_old_multiline(); auto str = fancy_to_string({1, {1}}, style); @@ -419,10 +453,9 @@ TEST_CASE("serialization") SECTION("indent_char is honored for deep indents in objects") { - fancy_serializer_style style; + fancy_serializer_style style = fancy_serializer_style::preset_multiline; style.indent_step = 300; style.indent_char = 'X'; - style.set_old_multiline(); auto str = fancy_to_string({{"key", {{"key", 1}}}}, style);