Json unicode escape (#2147)

Json unicode escape (#2142)
This commit is contained in:
Aleksandar Fabijanic
2018-02-08 18:42:30 -06:00
committed by GitHub
parent c1f841472a
commit 2be0661a0b
12 changed files with 190 additions and 92 deletions

View File

@@ -24,16 +24,64 @@
namespace Poco {
void Foundation_API toJSON(const std::string& value, std::ostream& out, bool wrap = true, bool escapeAllUnicode = false);
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,
/// If specified, when the object is stringified, all
/// unicode characters will be escaped in the resulting
/// string.
JSON_WRAP_STRINGS = 4
/// If specified, the object will preserve the items
/// insertion order. Otherwise, items will be sorted
/// by keys.
};
//@ 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.
///
/// This function is deprecated, please use
///
/// void Poco::toJSON(const std::string&, std::ostream&, int)
//@ 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
///
/// Returns formatted string.
///
/// This function is deprecated, please use
///
/// std::string Poco::toJSON(const std::string&, int)
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 wrap is true, the resulting string is enclosed in double quotes
/// If escapeAllUnicode is true, all unicode characters will be escaped, otherwise only the compulsory ones.
/// If JSON_WRAP_STRINGS is in options, the resulting strings is enclosed in double quotes
/// If JSON_ESCAPE_UNICODE is in options, all unicode characters will be escaped, otherwise
/// only the compulsory ones.
std::string Foundation_API toJSON(const std::string& value, bool wrap = true, bool escapeAllUnicode = false);
std::string Foundation_API toJSON(const std::string& value, int options);
/// Formats string value by escaping control characters.
/// If wrap is true, the resulting string is enclosed in double quotes
/// 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
/// only the compulsory ones.
///
/// Returns formatted string.
/// If escapeAllUnicode is true, all unicode characters will be escaped, otherwise only the compulsory ones.

View File

@@ -15,54 +15,87 @@
#include "Poco/UTF8String.h"
#include <ostream>
namespace Poco {
void toJSON(const std::string& value, std::ostream& out, bool wrap, bool escapeAllUnicode)
namespace {
template<typename T, typename S>
struct WriteFunc
{
if (wrap) out << '"';
if (escapeAllUnicode)
typedef T& (T::*Type)(const char* s, S n);
};
template<typename T, typename S>
void writeString(const std::string &value, T& obj, typename WriteFunc<T, S>::Type write, int options)
{
bool wrap = ((options & Poco::JSON_WRAP_STRINGS) != 0);
bool escapeAllUnicode = ((options & Poco::JSON_ESCAPE_UNICODE) != 0);
if (value.size() == 0)
{
out << UTF8::escape(value.begin(), value.end());
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)
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 == '/'))
if((*it >= 0 && *it <= 31) || (*it == '"') || (*it == '\\') || (*it == '/'))
{
out << UTF8::escape(it, it+1);
}
else out << *it;
std::string str = Poco::UTF8::escape(it, it + 1);
(obj.*write)(str.c_str(), str.size());
}else (obj.*write)(&(*it), 1);
}
}
if (wrap) out << '"';
if(wrap) (obj.*write)("\"", 1);
};
}
namespace Poco {
void toJSON(const std::string& value, std::ostream& out, bool wrap)
{
int options = (wrap ? Poco::JSON_WRAP_STRINGS : 0);
writeString<std::ostream,
std::streamsize>(value, out, &std::ostream::write, options);
}
std::string toJSON(const std::string& value, bool wrap, bool escapeAllUnicode)
{
int options = (wrap ? Poco::JSON_WRAP_STRINGS : 0);
std::string ret;
if (wrap) ret.append(1, '"');
if (escapeAllUnicode)
{
ret.append(UTF8::escape(value.begin(), value.end()));
}
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 == '/'))
{
ret.append(UTF8::escape(it, it+1));
}
else ret.append(1, *it);
}
}
if (wrap) ret.append(1, '"');
writeString<std::string,
std::string::size_type>(value, ret, &std::string::append, options);
return ret;
}
void toJSON(const std::string& value, std::ostream& out, int options)
{
writeString<std::ostream, std::streamsize>(value, out, &std::ostream::write, options);
}
std::string toJSON(const std::string& value, int options)
{
std::string ret;
writeString<std::string,
std::string::size_type>(value, ret, &std::string::append, options);
return ret;
}

View File

@@ -1203,8 +1203,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 ???
@@ -1217,8 +1217,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);
@@ -1246,7 +1248,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("");
}

View File

@@ -63,11 +63,12 @@ public:
typedef std::vector<Dynamic::Var>::const_iterator ConstIterator;
typedef SharedPtr<Array> 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.

View File

@@ -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"
@@ -66,19 +67,6 @@ public:
typedef ValueMap::const_iterator ConstIterator;
typedef std::vector<std::string> 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.
///
@@ -250,6 +238,9 @@ private:
template <typename C>
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;
@@ -260,10 +251,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 << ',';

View File

@@ -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;
};

View File

@@ -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 <ostream>
@@ -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);
}

View File

@@ -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)
{
}
@@ -149,6 +150,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 << "[";
@@ -159,7 +163,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())
{

View File

@@ -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)
{
}

View File

@@ -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;
}

View File

@@ -15,7 +15,6 @@
#include "Poco/JSON/Stringifier.h"
#include "Poco/JSON/Array.h"
#include "Poco/JSON/Object.h"
#include "Poco/JSONString.h"
#include <iomanip>
@@ -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<std::string>();
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<std::string>();
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);
}

View File

@@ -976,6 +976,7 @@ void JSONTest::testStringElement()
json = "[ \"\\u0017\" ]";
Var v = Parser().parse(json);
Stringifier::condense(v, s);
std::string ss = s.str();
assert(s.str() == "[\"\\u0017\"]");
}
@@ -1525,7 +1526,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);
@@ -2009,7 +2010,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);
@@ -2064,7 +2065,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);
@@ -2105,7 +2106,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);