This commit is contained in:
Aleksandar Fabijanic 2018-02-08 09:08:42 -06:00 committed by Alex Fabijanic
parent 4a7ab78686
commit bd81aec779
13 changed files with 610 additions and 106 deletions

View File

@ -24,15 +24,67 @@
namespace Poco {
void Foundation_API toJSON(const std::string& value, std::ostream& out, bool wrap = true);
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 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);
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.
} // namespace Poco

View File

@ -15,24 +15,90 @@
#include "Poco/UTF8String.h"
#include <ostream>
namespace {
template<typename T, typename S>
struct WriteFunc
{
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)
{
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 == '/'))
{
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);
};
}
namespace Poco {
void toJSON(const std::string& value, std::ostream& out, bool wrap)
{
if (wrap) out << '"';
out << UTF8::escape(value.begin(), value.end());
if (wrap) out << '"';
}
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)
{
std::string ret;
if (wrap) ret.append(1, '"');
ret.append(UTF8::escape(value.begin(), value.end()));
if (wrap) ret.append(1, '"');
return ret;
}
std::string toJSON(const std::string& value, bool wrap)
{
int options = (wrap ? Poco::JSON_WRAP_STRINGS : 0);
std::string ret;
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;
}
} // namespace Poco

View File

@ -14,7 +14,7 @@
#include "Poco/Dynamic/VarHolder.h"
#include "Poco/Dynamic/Var.h"
#include "Poco/UTF8String.h"
#include "Poco/JSONString.h"
namespace Poco {
@ -36,7 +36,7 @@ namespace Impl {
void escape(std::string& target, const std::string& source)
{
target = UTF8::escape(source.begin(), source.end());
target = toJSON(source);
}
@ -53,11 +53,9 @@ bool isJSONString(const Var& any)
void appendJSONString(std::string& val, const Var& any)
{
std::string json(val);
val.append(1, '"');
std::string json;
escape(json, any.convert<std::string>());
val.append(json);
val.append(1, '"');
}

View File

@ -16,6 +16,7 @@
#include "Poco/MemoryStream.h"
#include "Poco/Stopwatch.h"
#include "Poco/Exception.h"
#include "Poco/JSONString.h"
#include <iostream>
#include <iomanip>
#include <cstdio>
@ -57,6 +58,7 @@ using Poco::CILess;
using Poco::MemoryInputStream;
using Poco::Stopwatch;
using Poco::RangeException;
using Poco::toJSON;
StringTest::StringTest(const std::string& name): CppUnit::TestCase(name)
@ -1078,6 +1080,68 @@ void StringTest::benchmarkFloatToStr()
}
void StringTest::testJSONString()
{
assert (toJSON("\\", false) == "\\\\");
assert (toJSON("\"", false) == "\\\"");
assert (toJSON("/", false) == "\\/");
assert (toJSON("\a", false) == "\\a");
assert (toJSON("\b", false) == "\\b");
assert (toJSON("\f", false) == "\\f");
assert (toJSON("\n", false) == "\\n");
assert (toJSON("\r", false) == "\\r");
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");
// ??? on MSVC, the assert macro expansion
// fails to compile when this string is inline ???
std::string str = "\"foo\\\\\"";
assert (toJSON("foo\\") == str);
assert (toJSON("bar/") == "\"bar\\/\"");
assert (toJSON("baz") == "\"baz\"");
assert (toJSON("q\"uote\"d") == "\"q\\\"uote\\\"d\"");
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\"");
std::ostringstream ostr;
toJSON("foo\\", ostr);
assert(ostr.str() == str);
ostr.str("");
toJSON("foo\\", ostr);
assert(toJSON("bar/") == "\"bar\\/\"");
ostr.str("");
toJSON("baz", ostr);
assert(ostr.str() == "\"baz\"");
ostr.str("");
toJSON("q\"uote\"d", ostr);
assert(ostr.str() == "\"q\\\"uote\\\"d\"");
ostr.str("");
toJSON("bs\b", ostr);
assert(ostr.str() == "\"bs\\b\"");
ostr.str("");
toJSON("nl\n", ostr);
assert(ostr.str() == "\"nl\\n\"");
ostr.str("");
toJSON("tb\t", ostr);
assert(ostr.str() == "\"tb\\t\"");
ostr.str("");
toJSON("\xD0\x82", ostr);
assert(ostr.str() == "\"\xD0\x82\"");
ostr.str("");
toJSON("\xD0\x82", ostr, true, true);
assert(ostr.str() == "\"\\u0402\"");
ostr.str("");
}
void StringTest::setUp()
{
}
@ -1118,6 +1182,7 @@ CppUnit::Test* StringTest::suite()
CppUnit_addTest(pSuite, StringTest, testIntToString);
CppUnit_addTest(pSuite, StringTest, testFloatToString);
//CppUnit_addTest(pSuite, StringTest, benchmarkFloatToStr);
CppUnit_addTest(pSuite, StringTest, testJSONString);
return pSuite;
}

View File

@ -55,6 +55,8 @@ public:
void testFloatToString();
void benchmarkFloatToStr();
void testJSONString();
void setUp();
void tearDown();

View File

@ -63,8 +63,11 @@ public:
typedef std::vector<Dynamic::Var>::const_iterator ConstIterator;
typedef SharedPtr<Array> Ptr;
Array();
Array(bool escapeUnicode = false);
/// Creates an empty Array.
///
/// If escapeUnicode is true, 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.
@ -85,6 +88,12 @@ public:
virtual ~Array();
/// Destroys the Array.
void setEscapeUnicode(bool escape = true);
/// Sets the flag for escaping unicode.
bool getEscapeUnicode() const;
/// Returns the flag for escaping unicode.
ValueVec::const_iterator begin() const;
/// Returns the begin iterator for values.
@ -192,12 +201,30 @@ private:
ValueVec _values;
mutable ArrayPtr _pArray;
mutable bool _modified;
// Note:
// The reason we have this flag here (rather than as argument to stringify())
// is because Array can be returned stringified from a Dynamic::Var:toString(),
// so it must know whether to escape unicode or not.
bool _escapeUnicode;
};
//
// inlines
//
inline void Array::setEscapeUnicode(bool escape)
{
_escapeUnicode = true;
}
inline bool Array::getEscapeUnicode() const
{
return _escapeUnicode;
}
inline Array::ValueVec::const_iterator Array::begin() const
{
return _values.begin();

View File

@ -37,8 +37,13 @@ namespace JSON {
class JSON_API Object
<<<<<<< HEAD
/// Represents a JSON object. Object provides a representation
/// based on shared pointers and optimized for performance. It is possible to
=======
/// Represents a JSON object. Object provides a representation based on
/// shared pointers and optimized for performance. It is possible to
>>>>>>> df5968ce1... Json unicode escape && preserveOrder keys sync (#2145)
/// convert Object to DynamicStruct. Conversion requires copying and therefore
/// has performance penalty; the benefit is in improved syntax, eg:
///
@ -56,7 +61,7 @@ class JSON_API Object
/// // copy/convert to Poco::DynamicStruct
/// Poco::DynamicStruct ds = *object;
/// val = ds["test"]["property"]; // val holds "value"
/// ----
///
{
public:
typedef SharedPtr<Object> Ptr;
@ -64,12 +69,36 @@ public:
typedef ValueMap::value_type ValueType;
typedef ValueMap::iterator Iterator;
typedef ValueMap::const_iterator ConstIterator;
typedef std::vector<std::string> NameList;
explicit Object(bool preserveInsertionOrder = false);
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.
///
<<<<<<< HEAD
/// If preserveInsertionOrder, object will preserve the items insertion
/// order. Otherwise, items will be sorted by keys.
=======
/// If JSON_PRESERVE_KEY_ORDER is specified, the object will
/// preserve the items insertion order. Otherwise, items will be
/// sorted by keys.
///
/// If JSON_ESCAPE_UNICODE is specified, when the object is
/// stringified, all unicode characters will be escaped in the
/// resulting string.
>>>>>>> df5968ce1... Json unicode escape && preserveOrder keys sync (#2145)
Object(const Object& copy);
/// Creates an Object by copying another one.
@ -93,25 +122,33 @@ public:
Object &operator =(const Object &other);
// Assignment operator
<<<<<<< HEAD
Iterator begin()
{
return _values.begin();
}
=======
Object &operator =(Object &&other);
// Move asignment operator
ConstIterator begin() const
{
return _values.begin();
}
void setEscapeUnicode(bool escape = true);
/// Sets the flag for escaping unicode.
>>>>>>> df5968ce1... Json unicode escape && preserveOrder keys sync (#2145)
Iterator end()
{
return _values.end();
}
bool getEscapeUnicode() const;
/// Returns the flag for escaping unicode.
ConstIterator end() const
{
return _values.end();
}
Iterator begin();
/// Returns begin iterator for values.
ConstIterator begin() const;
/// Returns const begin iterator for values.
Iterator end();
/// Returns end iterator for values.
ConstIterator end() const;
/// Returns const end iterator for values.
Dynamic::Var get(const std::string& key) const;
/// Retrieves a property. An empty value is
@ -155,7 +192,10 @@ public:
return value.convert<T>();
}
void getNames(std::vector<std::string>& names) const;
void getNames(NameList& names) const;
/// Fills the supplied vector with all property names.
NameList getNames() const;
/// Returns all property names.
bool has(const std::string& key) const;
@ -225,7 +265,11 @@ public:
/// Insertion order preservation property is left intact.
private:
typedef std::deque<ValueMap::const_iterator> KeyList;
typedef Poco::DynamicStruct::Ptr StructPtr;
void resetDynStruct() const;
void syncKeys(const KeyList& keys);
template <typename C>
void doStringify(const C& container, std::ostream& out, unsigned int indent, unsigned int step) const
@ -233,17 +277,17 @@ private:
out << '{';
if (indent > 0) out << std::endl;
typename C::const_iterator it = container.begin();
typename C::const_iterator end = container.end();
for (; it != end;)
{
for (unsigned int i = 0; i < indent; i++) out << ' ';
Stringifier::stringify(getKey(it), out);
Stringifier::stringify(getKey(it), out, indent, step, _escapeUnicode);
out << ((indent > 0) ? " : " : ":");
Stringifier::stringify(getValue(it), out, indent + step, step);
Stringifier::stringify(getValue(it), out, indent + step, step, _escapeUnicode);
if (++it != container.end()) out << ',';
@ -257,17 +301,19 @@ private:
out << '}';
}
typedef std::deque<const std::string*> KeyPtrList;
typedef Poco::DynamicStruct::Ptr StructPtr;
const std::string& getKey(ValueMap::const_iterator& it) const;
const Dynamic::Var& getValue(ValueMap::const_iterator& it) const;
const std::string& getKey(KeyPtrList::const_iterator& it) const;
const Dynamic::Var& getValue(KeyPtrList::const_iterator& it) const;
const std::string& getKey(KeyList::const_iterator& it) const;
const Dynamic::Var& getValue(KeyList::const_iterator& it) const;
ValueMap _values;
KeyPtrList _keys;
KeyList _keys;
bool _preserveInsOrder;
// Note:
// The reason for this flag (rather than as argument to stringify()) is
// because Object can be returned stringified from Dynamic::Var::toString(),
// so it must know whether to escape unicode or not.
bool _escapeUnicode;
mutable StructPtr _pStruct;
mutable bool _modified;
};
@ -276,6 +322,43 @@ private:
//
// inlines
//
inline void Object::setEscapeUnicode(bool escape)
{
_escapeUnicode = true;
}
inline bool Object::getEscapeUnicode() const
{
return _escapeUnicode;
}
inline Object::Iterator Object::begin()
{
return _values.begin();
}
inline Object::ConstIterator Object::begin() const
{
return _values.begin();
}
inline Object::Iterator Object::end()
{
return _values.end();
}
inline Object::ConstIterator Object::end() const
{
return _values.end();
}
inline bool Object::has(const std::string& key) const
{
ValueMap::const_iterator it = _values.find(key);
@ -329,11 +412,11 @@ inline void Object::remove(const std::string& key)
_values.erase(key);
if (_preserveInsOrder)
{
KeyPtrList::iterator it = _keys.begin();
KeyPtrList::iterator end = _keys.end();
KeyList::iterator it = _keys.begin();
KeyList::iterator end = _keys.end();
for (; it != end; ++it)
{
if (key == **it)
if (key == (*it)->first)
{
_keys.erase(it);
break;
@ -356,9 +439,9 @@ inline const Dynamic::Var& Object::getValue(ValueMap::const_iterator& it) const
}
inline const Dynamic::Var& Object::getValue(KeyPtrList::const_iterator& it) const
inline const Dynamic::Var& Object::getValue(KeyList::const_iterator& it) const
{
ValueMap::const_iterator itv = _values.find(**it);
ValueMap::const_iterator itv = _values.find((*it)->first);
if (itv != _values.end())
return itv->second;
else

View File

@ -31,26 +31,29 @@ 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);
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.
///
/// 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);
static void stringify(const Dynamic::Var& any, std::ostream& out, unsigned int indent = 0, int step = -1, bool escapeUnicode = false);
/// Writes a string representation of the value to the output stream.
///
/// When indent is 0, the string will be created as small as possible.
/// When preserveInsertionOrder is true, the original string object members order will be preserved;
/// otherwise, object members are sorted by their names.
/// 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.
static void formatString(const std::string& value, std::ostream& out);
static void formatString(const std::string& value, std::ostream& out, bool escapeUnicode = false);
/// Formats the JSON string and streams it into ostream.
};
inline void Stringifier::condense(const Dynamic::Var& any, std::ostream& out)
inline void Stringifier::condense(const Dynamic::Var& any, std::ostream& out, bool escapeUnicode)
{
stringify(any, out, 0, -1);
stringify(any, out, 0, -1, escapeUnicode);
}

View File

@ -24,7 +24,8 @@ namespace Poco {
namespace JSON {
Array::Array(): _modified(false)
Array::Array(bool escapeUnicode): _modified(false),
_escapeUnicode(escapeUnicode)
{
}
@ -162,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);
Stringifier::stringify(*it, out, indent + step, step, _escapeUnicode);
if (++it != _values.end())
{
@ -175,8 +176,7 @@ void Array::stringify(std::ostream& out, unsigned int indent, int step) const
if (indent >= step) indent -= step;
for (int i = 0; i < indent; i++)
out << ' ';
for (int i = 0; i < indent; i++) out << ' ';
out << "]";
}

View File

@ -24,28 +24,22 @@ namespace Poco {
namespace JSON {
Object::Object(bool preserveInsOrder):
_preserveInsOrder(preserveInsOrder),
Object::Object(int options):
_preserveInsOrder(options & JSON_PRESERVE_KEY_ORDER),
_escapeUnicode(options & JSON_ESCAPE_UNICODE),
_modified(false)
{
}
Object::Object(const Object& copy) : _values(copy._values),
_preserveInsOrder(copy._preserveInsOrder),
_pStruct(!copy._modified ? copy._pStruct : 0),
_modified(copy._modified)
Object::Object(const Object& other) : _values(other._values),
_preserveInsOrder(other._preserveInsOrder),
_escapeUnicode(other._escapeUnicode),
_pStruct(!other._modified ? other._pStruct : 0),
_modified(other._modified)
{
if (_preserveInsOrder)
{
// need to update pointers in _keys to point to copied _values
for (KeyPtrList::const_iterator it = copy._keys.begin(); it != copy._keys.end(); ++it)
{
ValueMap::const_iterator itv = _values.find(**it);
poco_assert (itv != _values.end());
_keys.push_back(&itv->first);
}
}
syncKeys(other._keys);
}
@ -56,9 +50,11 @@ Object::Object(Object&& other) :
_values(std::move(other._values)),
_keys(std::move(other._keys)),
_preserveInsOrder(other._preserveInsOrder),
_escapeUnicode(other._escapeUnicode),
_pStruct(!other._modified ? other._pStruct : 0),
_modified(other._modified)
{
other.clear();
}
@ -66,9 +62,15 @@ Object &Object::operator= (Object &&other)
{
if (&other != this)
{
<<<<<<< HEAD
_values = std::move(other._values);
_keys = std::move(other._keys);
=======
_values = other._values;
>>>>>>> df5968ce1... Json unicode escape && preserveOrder keys sync (#2145)
_preserveInsOrder = other._preserveInsOrder;
syncKeys(other._keys);
_escapeUnicode = other._escapeUnicode;
_pStruct = !other._modified ? other._pStruct : 0;
_modified = other._modified;
}
@ -91,13 +93,30 @@ Object &Object::operator= (const Object &other)
_values = other._values;
_keys = other._keys;
_preserveInsOrder = other._preserveInsOrder;
_escapeUnicode = other._escapeUnicode;
_pStruct = !other._modified ? other._pStruct : 0;
_modified = other._modified;
other.clear();
}
return *this;
}
void Object::syncKeys(const KeyList& keys)
{
if(_preserveInsOrder)
{
// update iterators in _keys to point to copied _values
for(KeyList::const_iterator it = keys.begin(); it != keys.end(); ++it)
{
ValueMap::const_iterator itv = _values.find((*it)->first);
poco_assert (itv != _values.end());
_keys.push_back(itv);
}
}
}
Var Object::get(const std::string& key) const
{
ValueMap::const_iterator it = _values.find(key);
@ -134,13 +153,31 @@ Object::Ptr Object::getObject(const std::string& key) const
}
void Object::getNames(std::vector<std::string>& names) const
void Object::getNames(NameList& names) const
{
names.clear();
for (ValueMap::const_iterator it = _values.begin(); it != _values.end(); ++it)
if (_preserveInsOrder)
{
names.push_back(it->first);
for(KeyList::const_iterator it = _keys.begin(); it != _keys.end(); ++it)
{
names.push_back((*it)->first);
}
}
else
{
for(ValueMap::const_iterator it = _values.begin(); it != _values.end(); ++it)
{
names.push_back(it->first);
}
}
}
Object::NameList Object::getNames() const
{
NameList names;
getNames(names);
return names;
}
@ -155,16 +192,16 @@ void Object::stringify(std::ostream& out, unsigned int indent, int step) const
}
const std::string& Object::getKey(KeyPtrList::const_iterator& iter) const
const std::string& Object::getKey(KeyList::const_iterator& iter) const
{
ValueMap::const_iterator it = _values.begin();
ValueMap::const_iterator end = _values.end();
for (; it != end; ++it)
{
if (it->first == **iter) return it->first;
if (it == *iter) return it->first;
}
throw NotFoundException(**iter);
throw NotFoundException((*iter)->first);
}
@ -174,13 +211,13 @@ void Object::set(const std::string& key, const Dynamic::Var& value)
if (!ret.second) ret.first->second = value;
if (_preserveInsOrder)
{
KeyPtrList::iterator it = _keys.begin();
KeyPtrList::iterator end = _keys.end();
KeyList::iterator it = _keys.begin();
KeyList::iterator end = _keys.end();
for (; it != end; ++it)
{
if (key == **it) return;
if (key == (*it)->first) return;
}
_keys.push_back(&ret.first->first);
_keys.push_back(ret.first);
}
_modified = true;
}

View File

@ -26,28 +26,32 @@ namespace Poco {
namespace JSON {
void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int indent, int step)
void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int indent, int step, bool escapeUnicode)
{
if (step == -1) step = indent;
if (any.type() == typeid(Object))
{
const Object& o = any.extract<Object>();
Object& o = const_cast<Object&>(any.extract<Object>());
o.setEscapeUnicode(escapeUnicode);
o.stringify(out, indent == 0 ? 0 : indent, step);
}
else if (any.type() == typeid(Array))
{
const Array& a = any.extract<Array>();
Array& a = const_cast<Array&>(any.extract<Array>());
a.setEscapeUnicode(escapeUnicode);
a.stringify(out, indent == 0 ? 0 : indent, step);
}
else if (any.type() == typeid(Object::Ptr))
{
const Object::Ptr& o = any.extract<Object::Ptr>();
Object::Ptr& o = const_cast<Object::Ptr&>(any.extract<Object::Ptr>());
o->setEscapeUnicode(escapeUnicode);
o->stringify(out, indent == 0 ? 0 : indent, step);
}
else if (any.type() == typeid(Array::Ptr))
{
const Array::Ptr& a = any.extract<Array::Ptr>();
Array::Ptr& a = const_cast<Array::Ptr&>(any.extract<Array::Ptr>());
a->setEscapeUnicode(escapeUnicode);
a->stringify(out, indent == 0 ? 0 : indent, step);
}
else if (any.isEmpty())
@ -57,13 +61,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);
if (any.type() == typeid(char)) formatString(value, out, escapeUnicode);
else out << value;
}
else if (any.isString() || any.isDateTime() || any.isDate() || any.isTime())
{
std::string value = any.convert<std::string>();
formatString(value, out);
formatString(value, out, escapeUnicode);
}
else
{
@ -72,9 +76,9 @@ void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int inde
}
void Stringifier::formatString(const std::string& value, std::ostream& out)
void Stringifier::formatString(const std::string& value, std::ostream& out, bool escapeUnicode)
{
Poco::toJSON(value, out);
Poco::toJSON(value, out, true, escapeUnicode);
}

View File

@ -481,6 +481,13 @@ void JSONTest::testComplexObject()
Object::Ptr object = result.extract<Object::Ptr>();
assert(object->size() > 0);
Object::NameList names = object->getNames();
assert (names.size() == 4);
assert (names[0] == "id");
assert (names[1] == "jsonrpc");
assert (names[2] == "result");
assert (names[3] == "total");
DynamicStruct ds = *object;
assert (ds.size() > 0);
assert (ds["id"] == 1);
@ -1374,7 +1381,7 @@ void JSONTest::testStringify()
std::string str1 = "\r";
std::string str2 = "\n";
Poco::JSON::Object obj1, obj2;
Object obj1, obj2;
obj1.set("payload", str1);
obj2.set("payload", str2);
std::ostringstream oss1, oss2;
@ -1526,13 +1533,19 @@ void JSONTest::testStringify()
void JSONTest::testStringifyPreserveOrder()
{
Object presObj(true);
Object presObj(Object::JSON_PRESERVE_KEY_ORDER);
presObj.set("foo", 0);
presObj.set("bar", 0);
presObj.set("baz", 0);
std::stringstream ss;
presObj.stringify(ss);
assert(ss.str() == "{\"foo\":0,\"bar\":0,\"baz\":0}");
Object::NameList nl = presObj.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
ss.str("");
Stringifier::stringify(presObj, ss);
assert(ss.str() == "{\"foo\":0,\"bar\":0,\"baz\":0}");
@ -1918,7 +1931,8 @@ void JSONTest::testEscape0()
assert(ss.str().compare("{\"name\":\"B\\u0000b\"}") == 0);
}
void JSONTest::testEscapeUnicode()
void JSONTest::testNonEscapeUnicode()
{
Poco::JSON::Object::Ptr json = new Poco::JSON::Object();
std::string chinese("{ \"name\" : \"\\u4e2d\" }");
@ -1931,16 +1945,43 @@ void JSONTest::testEscapeUnicode()
std::stringstream ss;
object->stringify(ss);
assert(ss.str().compare("{\"name\":\"\\u4E2D\"}") == 0);
assert(ss.str().compare("{\"name\":\"\xE4\xB8\xAD\"}") == 0);
const unsigned char utf8Chars[] = {'{', '"', 'n', 'a', 'm', 'e', '"', ':',
'"', 'g', 195, 188, 'n', 't', 'e', 'r', '"', '}', 0};
'"', 'g', 0xC3, 0xBC, 'n', 't', 'e', 'r', '"', '}', 0};
std::string utf8Text((const char*) utf8Chars);
parser.reset();
result = parser.parse(utf8Text);
object = result.extract<Object::Ptr>();
ss.str(""); object->stringify(ss);
assert (ss.str() == "{\"name\":\"g\xC3\xBCnter\"}");
}
void JSONTest::testEscapeUnicode()
{
Poco::JSON::Object::Ptr json = new Poco::JSON::Object();
std::string chinese("{ \"name\" : \"\\u4e2d\" }");
Poco::JSON::Parser parser(new Poco::JSON::ParseHandler());
Var result = parser.parse(chinese);
assert(result.type() == typeid(Object::Ptr));
Object::Ptr object = result.extract<Object::Ptr>();
object->setEscapeUnicode(true);
std::stringstream ss;
object->stringify(ss, 0, -1);
assert(ss.str().compare("{\"name\":\"\\u4E2D\"}") == 0);
const unsigned char utf8Chars[] = {'{', '"', 'n', 'a', 'm', 'e', '"', ':',
'"', 'g', 0xC3, 0xBC, 'n', 't', 'e', 'r', '"', '}', 0};
std::string utf8Text((const char*) utf8Chars);
parser.reset();
result = parser.parse(utf8Text);
object = result.extract<Object::Ptr>();
object->setEscapeUnicode(true);
ss.str(""); object->stringify(ss, 0, -1);
assert (ss.str() == "{\"name\":\"g\\u00FCnter\"}");
}
@ -1974,6 +2015,125 @@ std::string JSONTest::getTestFilesPath(const std::string& type)
}
void JSONTest::testCopy()
{
Object obj1(Object::JSON_PRESERVE_KEY_ORDER);
obj1.set("foo", 0);
obj1.set("bar", 0);
obj1.set("baz", 0);
Object::NameList nl = obj1.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
Object obj2;
obj2 = obj1;
nl = obj2.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
Object obj3;
obj3.set("foo", 0);
obj3.set("bar", 0);
obj3.set("baz", 0);
nl = obj3.getNames();
assert (nl.size() == 3);
assert (nl[0] == "bar");
assert (nl[1] == "baz");
assert (nl[2] == "foo");
Object obj4;
obj4 = obj3;
nl = obj4.getNames();
assert (nl.size() == 3);
assert (nl[0] == "bar");
assert (nl[1] == "baz");
assert (nl[2] == "foo");
obj4 = obj1;
nl = obj4.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
Object obj5(obj1);
nl = obj5.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
}
void JSONTest::testMove()
{
Object obj1(Object::JSON_PRESERVE_KEY_ORDER);
obj1.set("foo", 0);
obj1.set("bar", 0);
obj1.set("baz", 0);
Object::NameList nl = obj1.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
Object obj2;
obj2 = std::move(obj1);
assert (obj1.getNames().size() == 0);
nl = obj2.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
Object obj3;
obj3.set("foo", 0);
obj3.set("bar", 0);
obj3.set("baz", 0);
nl = obj3.getNames();
assert (nl.size() == 3);
assert (nl[0] == "bar");
assert (nl[1] == "baz");
assert (nl[2] == "foo");
Object obj4;
obj4 = std::move(obj3);
assert (obj3.getNames().size() == 0);
nl = obj4.getNames();
assert (nl.size() == 3);
assert (nl[0] == "bar");
assert (nl[1] == "baz");
assert (nl[2] == "foo");
Object obj5(Object::JSON_PRESERVE_KEY_ORDER);
obj5.set("foo", 0);
obj5.set("bar", 0);
obj5.set("baz", 0);
nl = obj5.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
obj4 = std::move(obj5);
assert (obj5.getNames().size() == 0);
nl = obj4.getNames();
assert (nl.size() == 3);
assert (nl[0] == "foo");
assert (nl[1] == "bar");
assert (nl[2] == "baz");
}
CppUnit::Test* JSONTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("JSONTest");
@ -2020,7 +2180,10 @@ CppUnit::Test* JSONTest::suite()
CppUnit_addTest(pSuite, JSONTest, testUnicode);
CppUnit_addTest(pSuite, JSONTest, testSmallBuffer);
CppUnit_addTest(pSuite, JSONTest, testEscape0);
CppUnit_addTest(pSuite, JSONTest, testNonEscapeUnicode);
CppUnit_addTest(pSuite, JSONTest, testEscapeUnicode);
CppUnit_addTest(pSuite, JSONTest, testCopy);
CppUnit_addTest(pSuite, JSONTest, testMove);
return pSuite;
}

View File

@ -72,12 +72,16 @@ public:
void testValidJanssonFiles();
void testInvalidJanssonFiles();
void testTemplate();
void testItunes();
void testUnicode();
void testInvalidUnicodeJanssonFiles();
void testSmallBuffer();
void testEscape0();
void testNonEscapeUnicode();
void testEscapeUnicode();
void testCopy();
void testMove();
void setUp();
void tearDown();