From df567eff902ed3c550dbdabff6e4c69d858ccbe1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 2 Apr 2018 12:18:29 +0100 Subject: Refactor Refactor to reduce constant creation of new serializer object. Use standard C++ stream control for formatting. --- libjsonpp/jsonpp.h | 9 -- libjsonpp/serialize.cpp | 230 ++++++++++++++++++++------------------------ libjsonpp/testParse.cpp | 2 +- libjsonpp/testSerialise.cpp | 15 +++ 4 files changed, 120 insertions(+), 136 deletions(-) diff --git a/libjsonpp/jsonpp.h b/libjsonpp/jsonpp.h index 45c7415..df628ae 100644 --- a/libjsonpp/jsonpp.h +++ b/libjsonpp/jsonpp.h @@ -32,21 +32,12 @@ namespace json { Value(const X & x) : VT(x) { } }; - Object parseObject(Glib::ustring::const_iterator &); - Object parseObject(const Glib::ustring &); Value parseValue(std::istream &); Value parseValue(std::istream &, const std::string & encoding); Value parseValue(const Glib::ustring & s); Value parseValue(Glib::ustring::const_iterator & s); - void serializeObject(const Object &, std::ostream & s, const std::string & encoding); void serializeValue(const Value &, std::ostream & s, const std::string & encoding); - void serializeArray(const Array &, std::ostream & s, const std::string & encoding); - void serializeString(const String &, std::ostream & s, const std::string & encoding); - void serializeNumber(const Number &, std::ostream & s, const std::string & encoding); - void serializeBoolean(const Boolean &, std::ostream & s, const std::string & encoding); - void serializeNull(const Null &, std::ostream & s, const std::string & encoding); - Glib::ustring serializeObject(const Object &, const std::string & encoding); } #pragma GCC visibility pop diff --git a/libjsonpp/serialize.cpp b/libjsonpp/serialize.cpp index 97cf7ba..7c5cd06 100644 --- a/libjsonpp/serialize.cpp +++ b/libjsonpp/serialize.cpp @@ -1,149 +1,127 @@ #include "jsonpp.h" #include #include +#include namespace json { + const std::string null("null"); + const std::string utf8("utf-8"); + class JsonSerialize : public boost::static_visitor<> { public: JsonSerialize(std::ostream & out, const std::string & encoding) : - o(out), + s(out), e(encoding) { + s << std::boolalpha // for Boolean + << std::defaultfloat // for Number + << std::setfill('0') // for String \uNNNN + ; + } + void operator()(const Value & v) const { + boost::apply_visitor(*this, v); } - void operator()(const String & s) const { - serializeString(s, o, e); - } - void operator()(const Number & s) const { - serializeNumber(s, o, e); - } - void operator()(const Array & s) const { - serializeArray(s, o, e); + void operator()(const std::string & str) const { + (*this)(str, e); } - void operator()(const Object & s) const { - serializeObject(s, o, e); + void operator()(const String & str) const { + (*this)(str, e); } - void operator()(const Null & s) const { - serializeNull(s, o, e); + void operator()(const String & str, const std::string & enc) const { + if (!enc.empty()) { + serializeString(Glib::convert(str, enc, utf8)); + } + else { + serializeString(str); + } } - void operator()(const Boolean & s) const { - serializeBoolean(s, o, e); + void serializeString(const String & str) const { + s << '"'; + for (auto i = str.begin(); i != str.end(); ) { + const auto start = i; + while (i != str.end() && *i >= 32 && *i != '/' && *i != '"' && *i != '\\') { + i++; + } + if (start == str.begin() && i == str.end()) { + s << str.raw(); + break; + } + else if (start != i) { + s << Glib::ustring(start, i).raw(); + } + while (i != str.end() && (*i < 32 || *i == '/' || *i == '"' || *i == '\\')) { + const gunichar & c = *i; + switch (c) { + case '\f': + s << "\\f"; + break; + case '\t': + s << "\\t"; + break; + case '\n': + s << "\\n"; + break; + case '\b': + s << "\\b"; + break; + case '\r': + s << "\\r"; + break; + case '/': + s << "\\/"; + break; + case '\\': + s << "\\\\"; + break; + case '"': + s << "\\\""; + break; + default: + s << "\\u" << std::setw(4) << std::hex << c << std::setw(1); + break; + } + i++; + } + } + s << '"'; } - private: - std::ostream & o; - std::string e; - }; - - void serializeObject(const Object & o, std::ostream & s, const std::string & renc) { - std::string enc(renc == "utf-8" ? std::string() : renc); - s << std::boolalpha; - s << std::fixed; - s << '{'; - for (const Object::value_type & v : o) { - if (&v != &*o.begin()) { - s << ','; + void operator()(const Number & n) const { + s << std::dec << n; } - serializeString(v.first, s, enc); - s << ':'; - serializeValue(*v.second, s, enc); - } - s << '}'; - } - - void serializeValue(const Value & v, std::ostream & s, const std::string & enc) { - boost::apply_visitor(JsonSerialize(s, enc), v); - } - - void serializeArray(const Array & a, std::ostream & s, const std::string & enc) { - s << '['; - for (const Array::value_type & v : a) { - if (&v != &*a.begin()) { - s << ','; + void operator()(const Array & a) const { + s << '['; + for (const Array::value_type & v : a) { + if (&v != &*a.begin()) { + s << ','; + } + (*this)(*v); + } + s << ']'; } - serializeValue(*v, s, enc); - } - s << ']'; - } - - void serializeString(const String & str, std::ostream & s) { - s << '"'; - for (auto i = str.begin(); i != str.end(); ) { - auto start = i; - while (i != str.end() && *i >= 32 && *i != '/' && *i != '"' && *i != '\\') { - i++; + void operator()(const Object & o) const { + s << '{'; + for (const Object::value_type & v : o) { + if (&v != &*o.begin()) { + s << ','; + } + (*this)(v.first); + s << ':'; + (*this)(*v.second); + } + s << '}'; } - if (start == str.begin() && i == str.end()) { - s << str.raw(); - break; + void operator()(const Null &) const { + s << null; } - else if (start != i) { - s << Glib::ustring(start, i).raw(); + void operator()(const Boolean & b) const { + s << b; } - while (i != str.end() && (*i < 32 || *i == '/' || *i == '"' || *i == '\\')) { - gunichar c = *i; - switch (c) { - case '\f': - s << "\\f"; - break; - case '\t': - s << "\\t"; - break; - case '\n': - s << "\\n"; - break; - case '\b': - s << "\\b"; - break; - case '\r': - s << "\\r"; - break; - case '/': - s << "\\/"; - break; - case '\\': - s << "\\\\"; - break; - case '"': - s << "\\\""; - break; - default: - char buf[7]; - snprintf(buf, sizeof(buf), "\\u%04x", c); - s << buf; - break; - } - i++; - } - } - s << '"'; - } - - void serializeString(const String & str, std::ostream & o, const std::string & encoding) { - if (!encoding.empty()) { - std::stringstream s; - serializeString(str, s); - o << Glib::convert(s.str(), encoding, "utf-8"); - } - else { - serializeString(str, o); - } - } - - void serializeNumber(const Number & n, std::ostream & s, const std::string & ) { - s.unsetf(std::ios::fixed); - s << n; - } - - void serializeBoolean(const Boolean & b, std::ostream & s, const std::string & ) { - s << (b ? "true" : "false"); - } - - void serializeNull(const Null &, std::ostream & s, const std::string & ) { - s << "null"; - } + private: + std::ostream & s; + const std::string & e; + }; - Glib::ustring serializeObject(const Object & o, const std::string & enc) { - std::stringstream out; - serializeObject(o, out, enc); - return out.str(); + void serializeValue(const Value & v, std::ostream & s, const std::string & enc) { + JsonSerialize(s, enc)(v); } } diff --git a/libjsonpp/testParse.cpp b/libjsonpp/testParse.cpp index 77fe975..30cf85e 100644 --- a/libjsonpp/testParse.cpp +++ b/libjsonpp/testParse.cpp @@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE( parse_string_simple ) BOOST_AUTO_TEST_CASE( parse_object_withStringContainingQuote ) { const Glib::ustring val(" { \"key1\": \"value1\", \"key2\": \"value\\\"2\\\"\", \"key3\": 3 } "); - auto obj = json::parseObject(val); + auto obj = boost::get(json::parseValue(val)); BOOST_REQUIRE_EQUAL(3, obj.size()); BOOST_REQUIRE_EQUAL("value1", boost::get(*obj["key1"])); BOOST_REQUIRE_EQUAL("value\"2\"", boost::get(*obj["key2"])); diff --git a/libjsonpp/testSerialise.cpp b/libjsonpp/testSerialise.cpp index fb90930..e1d7af2 100644 --- a/libjsonpp/testSerialise.cpp +++ b/libjsonpp/testSerialise.cpp @@ -81,6 +81,21 @@ BOOST_AUTO_TEST_CASE( serialise_whitespace ) BOOST_REQUIRE_EQUAL("\"\\r\\n\\t\"", writeString(Glib::ustring("\r\n\t"))); } +BOOST_AUTO_TEST_CASE( serialise_control ) +{ + BOOST_REQUIRE_EQUAL("\"\\b\\f\"", writeString(Glib::ustring("\b\f"))); +} + +BOOST_AUTO_TEST_CASE( serialise_slashes ) +{ + BOOST_REQUIRE_EQUAL("\"\\\\\\/\"", writeString(Glib::ustring("\\/"))); +} + +BOOST_AUTO_TEST_CASE( serialise_other ) +{ + BOOST_REQUIRE_EQUAL("\"\\u000b\\u0007\"", writeString(Glib::ustring("\v\a"))); +} + BOOST_AUTO_TEST_CASE( serialise_quotes ) { BOOST_REQUIRE_EQUAL("\"string with \\\" in\"", writeString(Glib::ustring("string with \" in"))); -- cgit v1.2.3