From 717106ecedb9cc42882a3964a5afc4016dae6382 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 14 Apr 2017 19:49:05 +0200 Subject: [PATCH] :hammer: templated output_adapter and used in class serializer --- src/json.hpp | 248 +++++++++++++++++++++------------- test/src/unit-convenience.cpp | 2 +- 2 files changed, 154 insertions(+), 96 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 2fcb368e3..f7fbfd71b 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -2897,8 +2897,8 @@ class basic_json */ string_t dump(const int indent = -1) const { - std::stringstream ss; - serializer s(ss); + string_t result; + serializer s(output_adapter::create(result)); if (indent >= 0) { @@ -2909,7 +2909,7 @@ class basic_json s.dump(*this, false, 0); } - return ss.str(); + return result; } /*! @@ -6554,6 +6554,104 @@ class basic_json /// @} + private: + ///////////////////// + // output adapters // + ///////////////////// + + template + class output_adapter + { + public: + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, size_t length) = 0; + virtual ~output_adapter() {} + + static std::shared_ptr> create(std::vector& vec) + { + return std::shared_ptr(new output_vector_adapter(vec)); + } + + static std::shared_ptr> create(std::ostream& s) + { + return std::shared_ptr(new output_stream_adapter(s)); + } + + static std::shared_ptr> create(std::string& s) + { + return std::shared_ptr(new output_string_adapter(s)); + } + }; + + template + using output_adapter_t = std::shared_ptr>; + + template + class output_vector_adapter : public output_adapter + { + public: + output_vector_adapter(std::vector& vec) + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + void write_characters(const CharType* s, size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; + }; + + template + class output_stream_adapter : public output_adapter + { + public: + output_stream_adapter(std::basic_ostream& s) + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + void write_characters(const CharType* s, size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; + }; + + template + class output_string_adapter : public output_adapter + { + public: + output_string_adapter(std::string& s) + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + void write_characters(const CharType* s, size_t length) override + { + str.append(s, length); + } + + private: + std::basic_string& str; + }; + /////////////////// // serialization // @@ -6576,7 +6674,7 @@ class basic_json /*! @param[in] s output stream to serialize to */ - serializer(std::ostream& s) + serializer(output_adapter_t s) : o(s), loc(std::localeconv()), thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]), decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0]) @@ -6610,13 +6708,13 @@ class basic_json { if (val.m_value.object->empty()) { - o.write("{}", 2); + o->write_characters("{}", 2); return; } if (pretty_print) { - o.write("{\n", 2); + o->write_characters("{\n", 2); // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; @@ -6629,49 +6727,49 @@ class basic_json auto i = val.m_value.object->cbegin(); for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { - o.write(indent_string.c_str(), static_cast(new_indent)); - o.put('\"'); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); dump_escaped(i->first); - o.write("\": ", 3); + o->write_characters("\": ", 3); dump(i->second, true, indent_step, new_indent); - o.write(",\n", 2); + o->write_characters(",\n", 2); } // last element assert(i != val.m_value.object->cend()); - o.write(indent_string.c_str(), static_cast(new_indent)); - o.put('\"'); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); dump_escaped(i->first); - o.write("\": ", 3); + o->write_characters("\": ", 3); dump(i->second, true, indent_step, new_indent); - o.put('\n'); - o.write(indent_string.c_str(), static_cast(current_indent)); - o.put('}'); + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); } else { - o.put('{'); + o->write_character('{'); // first n-1 elements auto i = val.m_value.object->cbegin(); for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { - o.put('\"'); + o->write_character('\"'); dump_escaped(i->first); - o.write("\":", 2); + o->write_characters("\":", 2); dump(i->second, false, indent_step, current_indent); - o.put(','); + o->write_character(','); } // last element assert(i != val.m_value.object->cend()); - o.put('\"'); + o->write_character('\"'); dump_escaped(i->first); - o.write("\":", 2); + o->write_characters("\":", 2); dump(i->second, false, indent_step, current_indent); - o.put('}'); + o->write_character('}'); } return; @@ -6681,13 +6779,13 @@ class basic_json { if (val.m_value.array->empty()) { - o.write("[]", 2); + o->write_characters("[]", 2); return; } if (pretty_print) { - o.write("[\n", 2); + o->write_characters("[\n", 2); // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; @@ -6699,36 +6797,36 @@ class basic_json // first n-1 elements for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { - o.write(indent_string.c_str(), static_cast(new_indent)); + o->write_characters(indent_string.c_str(), new_indent); dump(*i, true, indent_step, new_indent); - o.write(",\n", 2); + o->write_characters(",\n", 2); } // last element assert(not val.m_value.array->empty()); - o.write(indent_string.c_str(), static_cast(new_indent)); + o->write_characters(indent_string.c_str(), new_indent); dump(val.m_value.array->back(), true, indent_step, new_indent); - o.put('\n'); - o.write(indent_string.c_str(), static_cast(current_indent)); - o.put(']'); + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); } else { - o.put('['); + o->write_character('['); // first n-1 elements for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { dump(*i, false, indent_step, current_indent); - o.put(','); + o->write_character(','); } // last element assert(not val.m_value.array->empty()); dump(val.m_value.array->back(), false, indent_step, current_indent); - o.put(']'); + o->write_character(']'); } return; @@ -6736,9 +6834,9 @@ class basic_json case value_t::string: { - o.put('\"'); + o->write_character('\"'); dump_escaped(*val.m_value.string); - o.put('\"'); + o->write_character('\"'); return; } @@ -6746,11 +6844,11 @@ class basic_json { if (val.m_value.boolean) { - o.write("true", 4); + o->write_characters("true", 4); } else { - o.write("false", 5); + o->write_characters("false", 5); } return; } @@ -6775,13 +6873,13 @@ class basic_json case value_t::discarded: { - o.write("", 11); + o->write_characters("", 11); return; } case value_t::null: { - o.write("null", 4); + o->write_characters("null", 4); return; } } @@ -6872,7 +6970,7 @@ class basic_json const auto space = extra_space(s); if (space == 0) { - o.write(s.c_str(), static_cast(s.size())); + o->write_characters(s.c_str(), s.size()); return; } @@ -6998,7 +7096,7 @@ class basic_json } assert(pos == s.size() + space); - o.write(result.c_str(), static_cast(result.size())); + o->write_characters(result.c_str(), result.size()); } /*! @@ -7018,7 +7116,7 @@ class basic_json // special case for "0" if (x == 0) { - o.put('0'); + o->write_character('0'); return; } @@ -7044,7 +7142,7 @@ class basic_json } std::reverse(number_buffer.begin(), number_buffer.begin() + i); - o.write(number_buffer.data(), static_cast(i)); + o->write_characters(number_buffer.data(), i); } /*! @@ -7060,7 +7158,7 @@ class basic_json // NaN / inf if (not std::isfinite(x) or std::isnan(x)) { - o.write("null", 4); + o->write_characters("null", 4); return; } @@ -7069,11 +7167,11 @@ class basic_json { if (std::signbit(x)) { - o.write("-0.0", 4); + o->write_characters("-0.0", 4); } else { - o.write("0.0", 3); + o->write_characters("0.0", 3); } return; } @@ -7114,7 +7212,7 @@ class basic_json } } - o.write(number_buffer.data(), static_cast(len)); + o->write_characters(number_buffer.data(), static_cast(len)); // determine if need to append ".0" const bool value_is_int_like = std::none_of(number_buffer.begin(), @@ -7126,13 +7224,13 @@ class basic_json if (value_is_int_like) { - o.write(".0", 2); + o->write_characters(".0", 2); } } private: /// the output of the serializer - std::ostream& o; + output_adapter_t o = nullptr; /// a (hopefully) large enough character buffer std::array number_buffer{{}}; @@ -7181,7 +7279,7 @@ class basic_json o.width(0); // do the actual serialization - serializer s(o); + serializer s(output_adapter::create(o)); s.dump(j, pretty_print, static_cast(indentation)); return o; } @@ -8778,46 +8876,6 @@ class basic_json const char* start; }; - ///////////////////// - // output adapters // - ///////////////////// - - class output_adapter - { - public: - virtual void write_character(uint8_t c) = 0; - virtual void write_characters(const uint8_t* s, size_t length) = 0; - virtual ~output_adapter() {} - - static std::shared_ptr create(std::vector& vec) - { - return std::shared_ptr(new output_vector_adapter(vec)); - } - }; - - using output_adapter_t = std::shared_ptr; - - class output_vector_adapter : public output_adapter - { - public: - output_vector_adapter(std::vector& vec) - : v(vec) - {} - - void write_character(uint8_t c) override - { - v.push_back(c); - } - - void write_characters(const uint8_t* s, size_t length) override - { - std::copy(s, s + length, std::back_inserter(v)); - } - - private: - std::vector& v; - }; - ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// @@ -9872,7 +9930,7 @@ class basic_json : is_little_endian(little_endianess()) {} - explicit binary_writer(output_adapter_t adapter) + explicit binary_writer(output_adapter_t adapter) : is_little_endian(little_endianess()), oa(adapter) {} @@ -10379,7 +10437,7 @@ class basic_json const bool is_little_endian = true; /// the output - output_adapter_t oa = nullptr; + output_adapter_t oa = nullptr; }; public: @@ -10468,7 +10526,7 @@ class basic_json static std::vector to_cbor(const basic_json& j) { std::vector result; - binary_writer bw(output_adapter::create(result)); + binary_writer bw(output_adapter::create(result)); bw.write_cbor(j); return result; } @@ -10550,7 +10608,7 @@ class basic_json static std::vector to_msgpack(const basic_json& j) { std::vector result; - binary_writer bw(output_adapter::create(result)); + binary_writer bw(output_adapter::create(result)); bw.write_msgpack(j); return result; } diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index 335563118..5e16962d3 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -53,7 +53,7 @@ TEST_CASE("convenience functions") const char* escaped) { std::stringstream ss; - json::serializer s(ss); + json::serializer s(json::output_adapter::create(ss)); s.dump_escaped(original); CHECK(ss.str() == escaped); };