diff --git a/Foundation/include/Poco/JSONString.h b/Foundation/include/Poco/JSONString.h index a9cb82e0f..7016f6cf8 100644 --- a/Foundation/include/Poco/JSONString.h +++ b/Foundation/include/Poco/JSONString.h @@ -24,16 +24,16 @@ namespace Poco { - enum JSONOptions - { - JSON_PRESERVE_KEY_ORDER = 1, +enum JSONOptions +{ + JSON_PRESERVE_KEY_ORDER = 1, /// Applies to JSON::Object. If specified, the Object will /// preserve the items insertion order. Otherwise, items /// will be sorted by keys. /// /// Has no effect on toJSON() function. - JSON_ESCAPE_UNICODE = 2, + JSON_ESCAPE_UNICODE = 2, /// If specified, when the object is stringified, all /// unicode characters will be escaped in the resulting /// string. @@ -45,8 +45,8 @@ namespace Poco { }; - //@ deprecated - void Foundation_API toJSON(const std::string& value, std::ostream& out, bool wrap = true); +//@ deprecated +void Foundation_API toJSON(const std::string& value, std::ostream& out, bool wrap = true); /// Formats string value into the supplied output stream by /// escaping control and ALL Unicode characters. /// If wrap is true, the resulting string is enclosed in double quotes. @@ -56,8 +56,8 @@ namespace Poco { /// void Poco::toJSON(const std::string&, std::ostream&, int) - //@ deprecated - std::string Foundation_API toJSON(const std::string& value, bool wrap = true); +//@ deprecated +std::string Foundation_API toJSON(const std::string& value, bool wrap = true); /// Formats string value by escaping control and ALL Unicode characters. /// If wrap is true, the resulting string is enclosed in double quotes /// @@ -68,7 +68,7 @@ namespace Poco { /// std::string Poco::toJSON(const std::string&, int) - void Foundation_API toJSON(const std::string& value, std::ostream& out, int options); +void Foundation_API toJSON(const std::string& value, std::ostream& out, int options); /// Formats string value into the supplied output stream by /// escaping control characters. /// If JSON_WRAP_STRINGS is in options, the resulting strings is enclosed in double quotes @@ -76,7 +76,7 @@ namespace Poco { /// only the compulsory ones. - std::string Foundation_API toJSON(const std::string& value, int options); +std::string Foundation_API toJSON(const std::string& value, int options); /// Formats string value by escaping control characters. /// If JSON_WRAP_STRINGS is in options, the resulting string is enclosed in double quotes /// If JSON_ESCAPE_UNICODE is in options, all unicode characters will be escaped, otherwise diff --git a/Foundation/src/JSONString.cpp b/Foundation/src/JSONString.cpp index 3acc11b8c..d2591170c 100644 --- a/Foundation/src/JSONString.cpp +++ b/Foundation/src/JSONString.cpp @@ -19,47 +19,46 @@ namespace { - template - struct WriteFunc +template +struct WriteFunc +{ + typedef T& (T::*Type)(const char* s, S n); +}; + + + +template +void writeString(const std::string &value, T& obj, typename WriteFunc::Type write, int options) +{ + bool wrap = ((options & Poco::JSON_WRAP_STRINGS) != 0); + bool escapeAllUnicode = ((options & Poco::JSON_ESCAPE_UNICODE) != 0); + + if (value.size() == 0) { - typedef T& (T::*Type)(const char* s, S n); - }; + if(wrap) (obj.*write)("\"\"", 2); + return; + } - - - template - void writeString(const std::string &value, T& obj, typename WriteFunc::Type write, int options) + if(wrap) (obj.*write)("\"", 1); + if(escapeAllUnicode) { - bool wrap = ((options & Poco::JSON_WRAP_STRINGS) != 0); - bool escapeAllUnicode = ((options & Poco::JSON_ESCAPE_UNICODE) != 0); - - if (value.size() == 0) + std::string str = Poco::UTF8::escape(value.begin(), value.end()); + (obj.*write)(str.c_str(), str.size()); + } + else + { + for(std::string::const_iterator it = value.begin(), end = value.end(); it != end; ++it) { - if (wrap) (obj.*write)("\"\"", 2); - return; - } - - if (wrap) (obj.*write)("\"", 1); - if (escapeAllUnicode) - { - std::string str = Poco::UTF8::escape(value.begin(), value.end()); - (obj.*write)(str.c_str(), str.size()); - } - else - { - for (std::string::const_iterator it = value.begin(), end = value.end(); it != end; ++it) + // Forward slash isn't strictly required by JSON spec, but some parsers expect it + if((*it >= 0 && *it <= 31) || (*it == '"') || (*it == '\\') || (*it == '/')) { - // Forward slash isn't strictly required by JSON spec, but some parsers expect it - if ((*it >= 0 && *it <= 31) || (*it == '"') || (*it == '\\') || (*it == '/')) - { - std::string str = Poco::UTF8::escape(it, it + 1); - (obj.*write)(str.c_str(), str.size()); - } - else (obj.*write)(&(*it), 1); - } + std::string str = Poco::UTF8::escape(it, it + 1); + (obj.*write)(str.c_str(), str.size()); + }else (obj.*write)(&(*it), 1); } - if (wrap) (obj.*write)("\"", 1); - }; + } + if(wrap) (obj.*write)("\"", 1); +}; } @@ -68,37 +67,37 @@ namespace { namespace Poco { - void toJSON(const std::string& value, std::ostream& out, bool wrap) - { - int options = (wrap ? Poco::JSON_WRAP_STRINGS : 0); - writeString(value, out, &std::ostream::write, options); - } +void toJSON(const std::string& value, std::ostream& out, bool wrap) +{ + int options = (wrap ? Poco::JSON_WRAP_STRINGS : 0); + writeString(value, out, &std::ostream::write, options); +} - std::string toJSON(const std::string& value, bool wrap) - { - int options = (wrap ? Poco::JSON_WRAP_STRINGS : 0); - std::string ret; - writeString(value, ret, &std::string::append, options); - return ret; - } +std::string toJSON(const std::string& value, bool wrap, bool escapeAllUnicode) +{ + int options = (wrap ? Poco::JSON_WRAP_STRINGS : 0); + std::string ret; + writeString(value, ret, &std::string::append, options); + return ret; +} - void toJSON(const std::string& value, std::ostream& out, int options) - { - writeString(value, out, &std::ostream::write, options); - } +void toJSON(const std::string& value, std::ostream& out, int options) +{ + writeString(value, out, &std::ostream::write, options); +} - std::string toJSON(const std::string& value, int options) - { - std::string ret; - writeString(value, ret, &std::string::append, options); - return ret; - } +std::string toJSON(const std::string& value, int options) +{ + std::string ret; + writeString(value, ret, &std::string::append, options); + return ret; +} } // namespace Poco diff --git a/Foundation/testsuite/src/StringTest.cpp b/Foundation/testsuite/src/StringTest.cpp index fb97ed958..82466fc7c 100644 --- a/Foundation/testsuite/src/StringTest.cpp +++ b/Foundation/testsuite/src/StringTest.cpp @@ -1093,8 +1093,8 @@ void StringTest::testJSONString() assert (toJSON("\t", false) == "\\t"); assert (toJSON("\v", false) == "\\v"); assert (toJSON("a", false) == "a"); - assert (toJSON("\xD0\x82", false) == "\xD0\x82"); - assert (toJSON("\xD0\x82", false, true) == "\\u0402"); + assert (toJSON("\xD0\x82", 0) == "\xD0\x82"); + assert (toJSON("\xD0\x82", Poco::JSON_ESCAPE_UNICODE) == "\\u0402"); // ??? on MSVC, the assert macro expansion // fails to compile when this string is inline ??? @@ -1107,8 +1107,10 @@ void StringTest::testJSONString() assert (toJSON("bs\b") == "\"bs\\b\""); assert (toJSON("nl\n") == "\"nl\\n\""); assert (toJSON("tb\t") == "\"tb\\t\""); - assert (toJSON("\xD0\x82", true) == "\"\xD0\x82\""); - assert (toJSON("\xD0\x82", true, true) == "\"\\u0402\""); + assert (toJSON("\xD0\x82") == "\"\xD0\x82\""); + assert (toJSON("\xD0\x82", Poco::JSON_WRAP_STRINGS) == "\"\xD0\x82\""); + assert (toJSON("\xD0\x82", + Poco::JSON_WRAP_STRINGS | Poco::JSON_ESCAPE_UNICODE) == "\"\\u0402\""); std::ostringstream ostr; toJSON("foo\\", ostr); @@ -1136,7 +1138,10 @@ void StringTest::testJSONString() toJSON("\xD0\x82", ostr); assert(ostr.str() == "\"\xD0\x82\""); ostr.str(""); - toJSON("\xD0\x82", ostr, true, true); + toJSON("\xD0\x82", ostr, Poco::JSON_WRAP_STRINGS); + assert(ostr.str() == "\"\xD0\x82\""); + ostr.str(""); + toJSON("\xD0\x82", ostr, Poco::JSON_WRAP_STRINGS | Poco::JSON_ESCAPE_UNICODE); assert(ostr.str() == "\"\\u0402\""); ostr.str(""); } diff --git a/JSON/include/Poco/JSON/Array.h b/JSON/include/Poco/JSON/Array.h index f4f62d5c2..84c5f6f86 100644 --- a/JSON/include/Poco/JSON/Array.h +++ b/JSON/include/Poco/JSON/Array.h @@ -63,11 +63,12 @@ public: typedef std::vector::const_iterator ConstIterator; typedef SharedPtr Ptr; - Array(bool escapeUnicode = false); + Array(int options = 0); /// Creates an empty Array. /// - /// If escapeUnicode is true, when the object is stringified, all unicode - /// characters will be escaped in the resulting string. + /// If JSON_ESCAPE_UNICODE is specified, when the object is + /// stringified, all unicode characters will be escaped in the + /// resulting string. Array(const Array& copy); /// Creates an Array by copying another one. diff --git a/JSON/include/Poco/JSON/Object.h b/JSON/include/Poco/JSON/Object.h index 6feaad8e2..f05141159 100644 --- a/JSON/include/Poco/JSON/Object.h +++ b/JSON/include/Poco/JSON/Object.h @@ -21,6 +21,7 @@ #include "Poco/JSON/JSON.h" #include "Poco/JSON/Array.h" #include "Poco/JSON/Stringifier.h" +#include "Poco/JSONString.h" #include "Poco/SharedPtr.h" #include "Poco/Dynamic/Var.h" #include "Poco/Dynamic/Struct.h" @@ -71,19 +72,6 @@ public: typedef ValueMap::const_iterator ConstIterator; typedef std::vector NameList; - enum Options - { - JSON_PRESERVE_KEY_ORDER = 1, - /// If specified, the object will preserve the items - /// insertion order. Otherwise, items will be sorted - /// by keys. - - JSON_ESCAPE_UNICODE = 2 - /// If specified, when the object is stringified, all - /// unicode characters will be escaped in the resulting - /// string. - }; - explicit Object(int options = 0); /// Creates an empty Object. /// @@ -274,6 +262,9 @@ private: template void doStringify(const C& container, std::ostream& out, unsigned int indent, unsigned int step) const { + int options = Poco::JSON_WRAP_STRINGS; + options |= _escapeUnicode ? Poco::JSON_ESCAPE_UNICODE : 0; + out << '{'; if (indent > 0) out << std::endl; @@ -284,10 +275,10 @@ private: { for (unsigned int i = 0; i < indent; i++) out << ' '; - Stringifier::stringify(getKey(it), out, indent, step, _escapeUnicode); + Stringifier::stringify(getKey(it), out, indent, step, options); out << ((indent > 0) ? " : " : ":"); - Stringifier::stringify(getValue(it), out, indent + step, step, _escapeUnicode); + Stringifier::stringify(getValue(it), out, indent + step, step, options); if (++it != container.end()) out << ','; diff --git a/JSON/include/Poco/JSON/PrintHandler.h b/JSON/include/Poco/JSON/PrintHandler.h index 2e37da553..620dc52a9 100644 --- a/JSON/include/Poco/JSON/PrintHandler.h +++ b/JSON/include/Poco/JSON/PrintHandler.h @@ -20,6 +20,7 @@ #include "Poco/JSON/JSON.h" #include "Poco/JSON/Handler.h" +#include "Poco/JSONString.h" namespace Poco { @@ -37,10 +38,10 @@ public: static const unsigned JSON_PRINT_FLAT = 0; - PrintHandler(unsigned indent = 0); + PrintHandler(unsigned indent = 0, int options = Poco::JSON_WRAP_STRINGS); /// Creates the PrintHandler. - PrintHandler(std::ostream& out, unsigned indent = 0); + PrintHandler(std::ostream& out, unsigned indent = 0, int options = Poco::JSON_WRAP_STRINGS); /// Creates the PrintHandler. ~PrintHandler(); @@ -113,6 +114,7 @@ private: std::string _tab; int _array; bool _objStart; + int _options; }; diff --git a/JSON/include/Poco/JSON/Stringifier.h b/JSON/include/Poco/JSON/Stringifier.h index 33d5ff59c..8e3303bec 100644 --- a/JSON/include/Poco/JSON/Stringifier.h +++ b/JSON/include/Poco/JSON/Stringifier.h @@ -18,8 +18,9 @@ #define JSON_JSONStringifier_INCLUDED -#include "Poco/Dynamic/Var.h" #include "Poco/JSON/JSON.h" +#include "Poco/JSONString.h" +#include "Poco/Dynamic/Var.h" #include @@ -31,29 +32,38 @@ class JSON_API Stringifier /// Helper class for creating a string from a JSON object or array. { public: - static void condense(const Dynamic::Var& any, std::ostream& out, bool escapeUnicode = false); - /// Writes a condensed string representation of the value to the output stream while preserving the insertion order. + static void condense(const Dynamic::Var& any, std::ostream& out, int options = Poco::JSON_WRAP_STRINGS); + /// Writes a condensed string representation of the value to the output stream while preserving + /// the insertion order. + /// + /// If JSON_ESCAPE_UNICODE is in options, all unicode characters will be escaped, otherwise + /// only the compulsory ones. /// /// This is just a "shortcut" to stringify(any, out) with name indicating the function effect. - static void stringify(const Dynamic::Var& any, std::ostream& out, unsigned int indent = 0, int step = -1, bool escapeUnicode = false); + static void stringify(const Dynamic::Var& any, std::ostream& out, + unsigned int indent = 0, int step = -1, int options = Poco::JSON_WRAP_STRINGS); /// Writes a string representation of the value to the output stream. /// /// When indent is 0, the string will be created as small as possible. /// Indentation is increased/decreased using number of spaces defined in step. /// The default value -1 for step indicates that step will be equal to the /// indent size. - /// If escapeUnicode is true, all unicode characers will be escaped in the - /// resulting string. + /// + /// If JSON_ESCAPE_UNICODE is in options, all unicode characters will be escaped, otherwise + /// only the compulsory ones. - static void formatString(const std::string& value, std::ostream& out, bool escapeUnicode = false); + static void formatString(const std::string& value, std::ostream& out, int options = Poco::JSON_WRAP_STRINGS); /// Formats the JSON string and streams it into ostream. + /// + /// If JSON_ESCAPE_UNICODE is in options, all unicode characters will be escaped, otherwise + /// only the compulsory ones. }; -inline void Stringifier::condense(const Dynamic::Var& any, std::ostream& out, bool escapeUnicode) +inline void Stringifier::condense(const Dynamic::Var& any, std::ostream& out, int options) { - stringify(any, out, 0, -1, escapeUnicode); + stringify(any, out, 0, -1, options); } diff --git a/JSON/src/Array.cpp b/JSON/src/Array.cpp index 4c377ce34..c23d8fe22 100644 --- a/JSON/src/Array.cpp +++ b/JSON/src/Array.cpp @@ -15,6 +15,7 @@ #include "Poco/JSON/Array.h" #include "Poco/JSON/Object.h" #include "Poco/JSON/Stringifier.h" +#include "Poco/JSONString.h" using Poco::Dynamic::Var; @@ -24,8 +25,8 @@ namespace Poco { namespace JSON { -Array::Array(bool escapeUnicode): _modified(false), - _escapeUnicode(escapeUnicode) +Array::Array(int options): _modified(false), + _escapeUnicode(options & Poco::JSON_ESCAPE_UNICODE) { } @@ -153,6 +154,9 @@ bool Array::isObject(ConstIterator& it) const void Array::stringify(std::ostream& out, unsigned int indent, int step) const { + int options = Poco::JSON_WRAP_STRINGS; + options |= _escapeUnicode ? Poco::JSON_ESCAPE_UNICODE : 0; + if (step == -1) step = indent; out << "["; @@ -163,7 +167,7 @@ void Array::stringify(std::ostream& out, unsigned int indent, int step) const { for (int i = 0; i < indent; i++) out << ' '; - Stringifier::stringify(*it, out, indent + step, step, _escapeUnicode); + Stringifier::stringify(*it, out, indent + step, step, options); if (++it != _values.end()) { diff --git a/JSON/src/Object.cpp b/JSON/src/Object.cpp index 2943b4a0c..3882cf0dc 100644 --- a/JSON/src/Object.cpp +++ b/JSON/src/Object.cpp @@ -26,8 +26,8 @@ namespace JSON { Object::Object(int options): - _preserveInsOrder(options & JSON_PRESERVE_KEY_ORDER), - _escapeUnicode(options & JSON_ESCAPE_UNICODE), + _preserveInsOrder(options & Poco::JSON_PRESERVE_KEY_ORDER), + _escapeUnicode(options & Poco::JSON_ESCAPE_UNICODE), _modified(false) { } diff --git a/JSON/src/PrintHandler.cpp b/JSON/src/PrintHandler.cpp index 09617a6aa..bf735d086 100644 --- a/JSON/src/PrintHandler.cpp +++ b/JSON/src/PrintHandler.cpp @@ -21,20 +21,22 @@ namespace Poco { namespace JSON { -PrintHandler::PrintHandler(unsigned indent): +PrintHandler::PrintHandler(unsigned indent, int options): _out(std::cout), _indent(indent), _array(0), - _objStart(true) + _objStart(true), + _options(options) { } -PrintHandler::PrintHandler(std::ostream& out, unsigned indent): +PrintHandler::PrintHandler(std::ostream& out, unsigned indent, int options): _out(out), _indent(indent), _array(0), - _objStart(true) + _objStart(true), + _options(options) { } @@ -118,10 +120,10 @@ void PrintHandler::key(const std::string& k) { if (!_objStart) comma(); - _objStart = true; + _objStart = true; _out << _tab; - Stringifier::formatString(k, _out); + Stringifier::formatString(k, _out, _options); if (!printFlat()) _out << ' '; _out << ':'; if (!printFlat()) _out << ' '; @@ -173,7 +175,7 @@ void PrintHandler::value(UInt64 v) void PrintHandler::value(const std::string& value) { arrayValue(); - Stringifier::formatString(value, _out); + Stringifier::formatString(value, _out, _options); _objStart = false; } diff --git a/JSON/src/Stringifier.cpp b/JSON/src/Stringifier.cpp index 3a18c9fc1..c82f52149 100644 --- a/JSON/src/Stringifier.cpp +++ b/JSON/src/Stringifier.cpp @@ -15,7 +15,6 @@ #include "Poco/JSON/Stringifier.h" #include "Poco/JSON/Array.h" #include "Poco/JSON/Object.h" -#include "Poco/JSONString.h" #include @@ -26,8 +25,10 @@ namespace Poco { namespace JSON { -void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int indent, int step, bool escapeUnicode) +void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int indent, int step, int options) { + bool escapeUnicode = options & Poco::JSON_ESCAPE_UNICODE; + if (step == -1) step = indent; if (any.type() == typeid(Object)) @@ -61,13 +62,13 @@ void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int inde else if (any.isNumeric() || any.isBoolean()) { std::string value = any.convert(); - if (any.type() == typeid(char)) formatString(value, out, escapeUnicode); + if (any.type() == typeid(char)) formatString(value, out, options); else out << value; } else if (any.isString() || any.isDateTime() || any.isDate() || any.isTime()) { std::string value = any.convert(); - formatString(value, out, escapeUnicode); + formatString(value, out, options); } else { @@ -76,9 +77,9 @@ void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int inde } -void Stringifier::formatString(const std::string& value, std::ostream& out, bool escapeUnicode) +void Stringifier::formatString(const std::string& value, std::ostream& out, int options) { - Poco::toJSON(value, out, true, escapeUnicode); + Poco::toJSON(value, out, options); } diff --git a/JSON/testsuite/src/JSONTest.cpp b/JSON/testsuite/src/JSONTest.cpp index 0f71805fb..d8d5cfac9 100644 --- a/JSON/testsuite/src/JSONTest.cpp +++ b/JSON/testsuite/src/JSONTest.cpp @@ -980,6 +980,7 @@ void JSONTest::testStringElement() json = "[ \"\\u0017\" ]"; Var v = Parser().parse(json); Stringifier::condense(v, s); + std::string ss = s.str(); assert(s.str() == "[\"\\u0017\"]"); } @@ -1533,7 +1534,7 @@ void JSONTest::testStringify() void JSONTest::testStringifyPreserveOrder() { - Object presObj(Object::JSON_PRESERVE_KEY_ORDER); + Object presObj(Poco::JSON_PRESERVE_KEY_ORDER); presObj.set("foo", 0); presObj.set("bar", 0); presObj.set("baz", 0); @@ -2017,7 +2018,7 @@ std::string JSONTest::getTestFilesPath(const std::string& type) void JSONTest::testCopy() { - Object obj1(Object::JSON_PRESERVE_KEY_ORDER); + Object obj1(Poco::JSON_PRESERVE_KEY_ORDER); obj1.set("foo", 0); obj1.set("bar", 0); obj1.set("baz", 0); @@ -2072,7 +2073,7 @@ void JSONTest::testCopy() void JSONTest::testMove() { - Object obj1(Object::JSON_PRESERVE_KEY_ORDER); + Object obj1(Poco::JSON_PRESERVE_KEY_ORDER); obj1.set("foo", 0); obj1.set("bar", 0); obj1.set("baz", 0); @@ -2113,7 +2114,7 @@ void JSONTest::testMove() assert (nl[1] == "baz"); assert (nl[2] == "foo"); - Object obj5(Object::JSON_PRESERVE_KEY_ORDER); + Object obj5(Poco::JSON_PRESERVE_KEY_ORDER); obj5.set("foo", 0); obj5.set("bar", 0); obj5.set("baz", 0);