From 85fbf3f0284268073fdfe1ed6d9dbfe54dc4d4bb Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 4 Sep 2024 10:17:23 +0200 Subject: Add ability to customize/disable multi-value separator in JSON serializer --- libbutl/json/serializer.cxx | 23 +++++++++++++---------- libbutl/json/serializer.hxx | 45 +++++++++++++++++++++++++++++++++------------ libbutl/json/serializer.ixx | 19 +++++++++++-------- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/libbutl/json/serializer.cxx b/libbutl/json/serializer.cxx index fbd569a..ae6c6b0 100644 --- a/libbutl/json/serializer.cxx +++ b/libbutl/json/serializer.cxx @@ -1,6 +1,6 @@ #include // snprintf #include // va_list -#include // memcpy +#include // memcpy, strlen #include #include @@ -38,23 +38,23 @@ namespace butl } buffer_serializer:: - buffer_serializer (string& s, size_t i) + buffer_serializer (string& s, size_t i, const char* mvs) : buffer_serializer (const_cast (s.data ()), size_, s.size (), dynarray_overflow, dynarray_flush, &s, - i) + i, mvs) { size_ = s.size (); } buffer_serializer:: - buffer_serializer (vector& v, size_t i) + buffer_serializer (vector& v, size_t i, const char* mvs) : buffer_serializer (v.data (), size_, v.size (), dynarray_overflow>, dynarray_flush>, &v, - i) + i, mvs) { size_ = v.size (); } @@ -83,12 +83,12 @@ namespace butl } stream_serializer:: - stream_serializer (ostream& os, size_t i) + stream_serializer (ostream& os, size_t i, const char* mvs) : buffer_serializer (tmp_, sizeof (tmp_), ostream_overflow, ostream_flush, &os, - i) + i, mvs) { } @@ -195,10 +195,13 @@ namespace butl } else if (values_ != 0) // Subsequent top-level value. { - // Top-level value separation. For now we always separate them with - // newlines, which is the most common/sensible way. + // Top-level value separation. // - sep = make_str ("\n", 1); + sep = make_str ( + mv_separator_, + (mv_separator_ == nullptr || mv_separator_[0] == '\0' ? 0 : + mv_separator_[1] == '\0' ? 1 : + strlen (mv_separator_))); } switch (*e) diff --git a/libbutl/json/serializer.hxx b/libbutl/json/serializer.hxx index 5192cb4..29fd63f 100644 --- a/libbutl/json/serializer.hxx +++ b/libbutl/json/serializer.hxx @@ -64,9 +64,9 @@ namespace butl // // Note that unlike the parser, the serializer is always in the multi- // value mode allowing the serialization of zero or more values. Note also - // that while values are separated with newlines, there is no trailing - // newline after the last (or only) value and the user is expected to add - // it manually if needed. + // that while values are by default separated with newlines, there is no + // trailing newline after the last (or only) value and the user is + // expected to add it manually, if needed. // // Also note that while RFC8259 recommends object members to have unique // names, the serializer does not enforce this. @@ -74,19 +74,30 @@ namespace butl class LIBBUTL_SYMEXPORT buffer_serializer { public: - // Serialize to string growing it as necessary. + // Serialize to string growing it as necessary. Note that the result is + // appended to any existing data in the string. // // The indentation argument specifies the number of indentation spaces // that should be used for pretty-printing. If 0 is passed, no // pretty-printing is performed. // + // The multi_value_separator argument specifies the character sequence + // to use to separate multiple top-level values. NULL or empty string + // means no separator. Note that it is kept as a reference and so must + // outlive the serializer instance. + // explicit - buffer_serializer (std::string&, std::size_t indentation = 2); + buffer_serializer (std::string&, + std::size_t indentation = 2, + const char* multi_value_separator = "\n"); - // Serialize to vector of characters growing it as necessary. + // Serialize to vector of characters growing it as necessary. Note that + // the result is appended to any existing data in the vector. // explicit - buffer_serializer (std::vector&, std::size_t indentation = 2); + buffer_serializer (std::vector&, + std::size_t indentation = 2, + const char* multi_value_separator = "\n"); // Serialize to a fixed array. // @@ -98,7 +109,8 @@ namespace butl // template buffer_serializer (std::array&, std::size_t& size, - std::size_t indentation = 2); + std::size_t indentation = 2, + const char* multi_value_separator = "\n"); // Serialize to a fixed buffer. // @@ -109,7 +121,8 @@ namespace butl // next() call that reaches the limit will throw invalid_json_output. // buffer_serializer (void* buf, std::size_t& size, std::size_t capacity, - std::size_t indentation = 2); + std::size_t indentation = 2, + const char* multi_value_separator = "\n"); // The overflow function is called when the output buffer is out of // space. The extra argument is a hint indicating the extra space likely @@ -154,7 +167,8 @@ namespace butl overflow_function*, flush_function*, void* data, - std::size_t indentation = 2); + std::size_t indentation = 2, + const char* multi_value_separator = "\n"); // As above but the length of the output text written is tracked in the // size argument. @@ -163,7 +177,8 @@ namespace butl overflow_function*, flush_function*, void* data, - std::size_t indentation = 2); + std::size_t indentation = 2, + const char* multi_value_separator = "\n"); // Begin/end an object. // @@ -390,6 +405,10 @@ namespace butl // The number of complete top-level values serialized thus far. // std::size_t values_ = 0; + + // Multi-value separator. + // + const char* mv_separator_; }; class LIBBUTL_SYMEXPORT stream_serializer: public buffer_serializer @@ -402,7 +421,9 @@ namespace butl // Otherwise, those are reported as the invalid_json_output exception. // explicit - stream_serializer (std::ostream&, std::size_t indentation = 2); + stream_serializer (std::ostream&, + std::size_t indentation = 2, + const char* multi_value_separator = "\n"); protected: char tmp_[4096]; diff --git a/libbutl/json/serializer.ixx b/libbutl/json/serializer.ixx index a719ef6..ed5178a 100644 --- a/libbutl/json/serializer.ixx +++ b/libbutl/json/serializer.ixx @@ -25,36 +25,39 @@ namespace butl inline buffer_serializer:: buffer_serializer (void* b, std::size_t& s, std::size_t c, overflow_function* o, flush_function* f, void* d, - std::size_t i) + std::size_t i, const char* mvs) : buf_ {b, s, c}, overflow_ (o), flush_ (f), data_ (d), indent_ (i), - sep_ (indent_ != 0 ? ",\n" : "") + sep_ (indent_ != 0 ? ",\n" : ""), + mv_separator_ (mvs) { } template inline buffer_serializer:: - buffer_serializer (std::array& a, std::size_t& s, std::size_t i) + buffer_serializer (std::array& a, std::size_t& s, + std::size_t i, const char* mvs) : buffer_serializer (a.data (), s, a.size (), nullptr, nullptr, nullptr, - i) + i, mvs) { } inline buffer_serializer:: - buffer_serializer (void* b, std::size_t& s, std::size_t c, std::size_t i) - : buffer_serializer (b, s, c, nullptr, nullptr, nullptr, i) + buffer_serializer (void* b, std::size_t& s, std::size_t c, + std::size_t i, const char* mvs) + : buffer_serializer (b, s, c, nullptr, nullptr, nullptr, i, mvs) { } inline buffer_serializer:: buffer_serializer (void* b, std::size_t c, overflow_function* o, flush_function* f, void* d, - std::size_t i) - : buffer_serializer (b, size_, c, o, f, d, i) + std::size_t i, const char* mvs) + : buffer_serializer (b, size_, c, o, f, d, i, mvs) { size_ = 0; } -- cgit v1.1