diff --git a/CHANGELOG b/CHANGELOG index c21c387c4..7d595257f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,10 @@ Release 1.5.2 (2013-03-??) - added ListMap (map-like container with preserving insertion order) - MailMessage: attachments saving support and consistent read/write - fixed GH #124: Possibile buffer overrun in Foundation/EventLogChannel +- fixed GH #119: JSON::Object holds values in ordered map +- added PrintHandler +- renamed DefaultHandler to ParseHandler +- redefined DefaultHandler as typedef to ParseHandler Release 1.5.1 (2013-01-11) ========================== diff --git a/Foundation/include/Poco/Platform.h b/Foundation/include/Poco/Platform.h index ff5bc118c..f34f2777f 100644 --- a/Foundation/include/Poco/Platform.h +++ b/Foundation/include/Poco/Platform.h @@ -247,4 +247,11 @@ #endif +#if defined(POCO_OS_FAMILY_WINDOWS) + #define POCO_DEFAULT_NEWLINE_CHARS "\r\n" +#else + #define POCO_DEFAULT_NEWLINE_CHARS "\n" +#endif + + #endif // Foundation_Platform_INCLUDED diff --git a/Foundation/src/LineEndingConverter.cpp b/Foundation/src/LineEndingConverter.cpp index 396e8f45d..9ca1c1e55 100644 --- a/Foundation/src/LineEndingConverter.cpp +++ b/Foundation/src/LineEndingConverter.cpp @@ -40,13 +40,6 @@ namespace Poco { -#if defined(_WIN32) -#define POCO_DEFAULT_NEWLINE_CHARS "\r\n" -#else -#define POCO_DEFAULT_NEWLINE_CHARS "\n" -#endif - - const std::string LineEnding::NEWLINE_DEFAULT(POCO_DEFAULT_NEWLINE_CHARS); const std::string LineEnding::NEWLINE_CR("\r"); const std::string LineEnding::NEWLINE_CRLF("\r\n"); diff --git a/JSON/JSON_CE_vs90.vcproj b/JSON/JSON_CE_vs90.vcproj index 9600be84b..19e64d3f6 100644 --- a/JSON/JSON_CE_vs90.vcproj +++ b/JSON/JSON_CE_vs90.vcproj @@ -433,7 +433,7 @@ + RelativePath=".\src\ParseHandler.cpp"/> + + + + + @@ -281,11 +283,12 @@ - + + diff --git a/JSON/JSON_vs100.vcxproj.filters b/JSON/JSON_vs100.vcxproj.filters index 90e41bf38..7b392ea7c 100644 --- a/JSON/JSON_vs100.vcxproj.filters +++ b/JSON/JSON_vs100.vcxproj.filters @@ -42,14 +42,17 @@ Header Files + + Header Files + + + Header Files + Source Files - - Source Files - Source Files @@ -74,5 +77,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/JSON/JSON_vs110.vcxproj b/JSON/JSON_vs110.vcxproj index 52d1c01a2..e5abd65e2 100644 --- a/JSON/JSON_vs110.vcxproj +++ b/JSON/JSON_vs110.vcxproj @@ -274,11 +274,12 @@ - + + @@ -292,6 +293,8 @@ + + diff --git a/JSON/JSON_vs110.vcxproj.filters b/JSON/JSON_vs110.vcxproj.filters index 6e53552f9..c722da3db 100644 --- a/JSON/JSON_vs110.vcxproj.filters +++ b/JSON/JSON_vs110.vcxproj.filters @@ -12,7 +12,7 @@ Source Files - + Source Files @@ -27,6 +27,9 @@ Source Files + + Source Files + Source Files @@ -62,6 +65,12 @@ Header Files + + Header Files + + + Header Files + Header Files diff --git a/JSON/JSON_vs71.vcproj b/JSON/JSON_vs71.vcproj index b9eaff778..c268e0007 100644 --- a/JSON/JSON_vs71.vcproj +++ b/JSON/JSON_vs71.vcproj @@ -365,7 +365,7 @@ + RelativePath=".\src\ParseHandler.cpp"/> + + + + RelativePath=".\src\ParseHandler.cpp"/> + + + + RelativePath=".\src\ParseHandler.cpp"/> + + + - + + @@ -290,6 +291,8 @@ + + diff --git a/JSON/JSON_x64_vs100.vcxproj.filters b/JSON/JSON_x64_vs100.vcxproj.filters index 4578d5ef4..fc1a777c5 100644 --- a/JSON/JSON_x64_vs100.vcxproj.filters +++ b/JSON/JSON_x64_vs100.vcxproj.filters @@ -12,7 +12,7 @@ Source Files - + Source Files @@ -27,6 +27,9 @@ Source Files + + Source Files + Source Files @@ -62,6 +65,12 @@ Header Files + + Header Files + + + Header Files + Header Files diff --git a/JSON/JSON_x64_vs110.vcxproj b/JSON/JSON_x64_vs110.vcxproj index 026253844..12d55e3ce 100644 --- a/JSON/JSON_x64_vs110.vcxproj +++ b/JSON/JSON_x64_vs110.vcxproj @@ -272,11 +272,12 @@ - + + @@ -290,6 +291,8 @@ + + diff --git a/JSON/JSON_x64_vs110.vcxproj.filters b/JSON/JSON_x64_vs110.vcxproj.filters index 4578d5ef4..fc1a777c5 100644 --- a/JSON/JSON_x64_vs110.vcxproj.filters +++ b/JSON/JSON_x64_vs110.vcxproj.filters @@ -12,7 +12,7 @@ Source Files - + Source Files @@ -27,6 +27,9 @@ Source Files + + Source Files + Source Files @@ -62,6 +65,12 @@ Header Files + + Header Files + + + Header Files + Header Files diff --git a/JSON/JSON_x64_vs90.vcproj b/JSON/JSON_x64_vs90.vcproj index a26d8d8ca..e37f5197b 100644 --- a/JSON/JSON_x64_vs90.vcproj +++ b/JSON/JSON_x64_vs90.vcproj @@ -390,7 +390,7 @@ + RelativePath=".\src\ParseHandler.cpp"/> + + + +#include "Poco/JSON/ParseHandler.h" namespace Poco { namespace JSON { -class JSON_API DefaultHandler : public Handler - /// Provides a default handler for the JSON parser. - /// This handler will build up an object or array based - /// on the handlers called by the parser. -{ -public: - - DefaultHandler(); - /// Default Constructor - - virtual ~DefaultHandler(); - /// Destructor - - void startObject(); - /// Handles a {, meaning a new object will be read - - void endObject(); - /// Handles a }, meaning the object is read - - void startArray(); - /// Handles a [, meaning a new array will be read - - void endArray(); - /// Handles a ], meaning the array is read - - void key(const std::string& k); - /// A key is read - - Dynamic::Var result() const; - /// Returns the result of the parser. Which is an object or an array. - - virtual void value(int v); - /// An integer value is read - - virtual void value(unsigned v); - /// An unsigned value is read. This will only be triggered if the - /// value cannot fit into a signed int. - -#if defined(POCO_HAVE_INT64) - virtual void value(Int64 v); - /// A 64-bit integer value is read - - virtual void value(UInt64 v); - /// An unsigned 64-bit integer value is read. This will only be - /// triggered if the value cannot fit into a signed 64-bit integer. -#endif - - virtual void value(const std::string& s); - /// A string value is read. - - virtual void value(double d); - /// A double value is read - - virtual void value(bool b); - /// A boolean value is read - - virtual void null(); - /// A null value is read - -private: - void setValue(const Poco::Dynamic::Var& value); - - std::stack _stack; - std::string _key; - Dynamic::Var _result; -}; - - -inline Dynamic::Var DefaultHandler::result() const -{ - return _result; -} - - -inline void DefaultHandler::value(int v) -{ - setValue(v); -} - - -inline void DefaultHandler::value(unsigned v) -{ - setValue(v); -} - - -#if defined(POCO_HAVE_INT64) -inline void DefaultHandler::value(Int64 v) -{ - setValue(v); -} - - -inline void DefaultHandler::value(UInt64 v) -{ - setValue(v); -} -#endif - - -inline void DefaultHandler::value(const std::string& s) -{ - setValue(s); -} - - -inline void DefaultHandler::value(double d) -{ - setValue(d); -} - - -inline void DefaultHandler::value(bool b) -{ - setValue(b); -} - - -inline void DefaultHandler::null() -{ - Poco::Dynamic::Var empty; - setValue(empty); -} +//@deprecated +typedef ParseHandler DefaultHandler; }} // namespace Poco::JSON diff --git a/JSON/include/Poco/JSON/Handler.h b/JSON/include/Poco/JSON/Handler.h index b8d3ed65d..bb3a0c797 100644 --- a/JSON/include/Poco/JSON/Handler.h +++ b/JSON/include/Poco/JSON/Handler.h @@ -51,6 +51,9 @@ namespace JSON { class JSON_API Handler { public: + Handler(); + /// Constructor; + virtual void startObject() = 0; /// The parser has read a {, meaning a new object will be read @@ -74,7 +77,7 @@ public: virtual void value(unsigned v) = 0; /// An unsigned value is read. This will only be triggered if the - /// value cannot fit into a signed int. + /// value cannot fit into a signed int. #if defined(POCO_HAVE_INT64) virtual void value(Int64 v) = 0; @@ -82,7 +85,7 @@ public: virtual void value(UInt64 v) = 0; /// An unsigned 64-bit integer value is read. This will only be - /// triggered if the value cannot fit into a signed 64-bit integer. + /// triggered if the value cannot fit into a signed 64-bit integer. #endif virtual void value(const std::string& value) = 0; @@ -94,6 +97,9 @@ public: virtual void value(bool b) = 0; /// A boolean value is read + virtual void comma(); + /// A comma is read + protected: virtual ~Handler(); diff --git a/JSON/include/Poco/JSON/Object.h b/JSON/include/Poco/JSON/Object.h index 34dff1e38..b2c17e21b 100644 --- a/JSON/include/Poco/JSON/Object.h +++ b/JSON/include/Poco/JSON/Object.h @@ -46,6 +46,7 @@ #include "Poco/Dynamic/Var.h" #include #include +#include #include #include @@ -60,8 +61,10 @@ class JSON_API Object public: typedef SharedPtr Ptr; - Object(); - /// Default constructor + Object(bool preserveInsertionOrder = false); + /// Default constructor. If preserveInsertionOrder, object + /// will preserve the items insertion order. Otherwise, items + /// will be sorted by keys. Object(const Object& copy); /// Copy constructor @@ -138,17 +141,55 @@ public: void set(const std::string& key, const Dynamic::Var& value); /// Sets a new value - void stringify(std::ostream& out, unsigned int indent = 0) const; + void stringify(std::ostream& out, unsigned int indent = 0, int step = -1) const; /// Prints the object to out. When indent is 0, the object - /// will be printed on one line without indentation. + /// will be printed on a single line without indentation. void remove(const std::string& key); /// Removes the property with the given key private: - //TODO: unordered map + + template + void doStringify(const C& container, std::ostream& out, unsigned int indent, int step) const + { + out << '{'; + + if (indent > 0) out << std::endl; + + for (C::const_iterator it = container.begin(); it != container.end();) + { + for(int i = 0; i < indent; i++) out << ' '; + + out << '"' << getKey(it) << '"'; + out << ((indent > 0) ? " : " : ":"); + + Stringifier::stringify(getValue(it), out, indent + step, step); + + if ( ++it != container.end() ) out << ','; + + if (step > 0) out << '\n'; + } + + if (indent >= step) indent -= step; + + for (int i = 0; i < indent; i++) + out << ' '; + + out << '}'; + } + typedef std::map ValueMap; - ValueMap _values; + typedef std::deque KeyPtrList; + + 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; + + ValueMap _values; + KeyPtrList _keys; + bool _preserveInsOrder; }; @@ -180,12 +221,6 @@ inline bool Object::isObject(const std::string& key) const } -inline void Object::set(const std::string& key, const Dynamic::Var& value) -{ - _values[key] = value; -} - - inline std::size_t Object::size() const { return static_cast(_values.size()); @@ -198,6 +233,24 @@ inline void Object::remove(const std::string& key) } +inline const std::string& Object::getKey(ValueMap::const_iterator& it) const +{ + return it->first; +} + + +inline const Dynamic::Var& Object::getValue(ValueMap::const_iterator& it) const +{ + return it->second; +} + + +inline const Dynamic::Var& Object::getValue(KeyPtrList::const_iterator& it) const +{ + return **it; +} + + }} // Namespace Poco::JSON diff --git a/JSON/include/Poco/JSON/ParseHandler.h b/JSON/include/Poco/JSON/ParseHandler.h new file mode 100644 index 000000000..bd7835e31 --- /dev/null +++ b/JSON/include/Poco/JSON/ParseHandler.h @@ -0,0 +1,181 @@ +// +// ParseHandler.h +// +// $Id$ +// +// Library: JSON +// Package: JSON +// Module: ParseHandler +// +// Definition of the ParseHandler class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#ifndef JSON_ParseHandler_INCLUDED +#define JSON_ParseHandler_INCLUDED + + +#include "Poco/JSON/Handler.h" +#include + + +namespace Poco { +namespace JSON { + + +class JSON_API ParseHandler : public Handler + /// Provides a default handler for the JSON parser. + /// This handler will build up an object or array based + /// on the handlers called by the parser. +{ +public: + + ParseHandler(bool preserveObjectOrder = false); + /// Creates the ParseHandler. + + virtual ~ParseHandler(); + /// Destroys the ParseHandler. + + void startObject(); + /// Handles a '{'; a new object is started. + + void endObject(); + /// Handles a '}'; the object is closed. + + void startArray(); + /// Handles a '['; a new array is started. + + void endArray(); + /// Handles a ']'; the array is closed. + + void key(const std::string& k); + /// A key is read + + Dynamic::Var result() const; + /// Returns the result of the parser (an object or an array). + + virtual void value(int v); + /// An integer value is read + + virtual void value(unsigned v); + /// An unsigned value is read. This will only be triggered if the + /// value cannot fit into a signed int. + +#if defined(POCO_HAVE_INT64) + virtual void value(Int64 v); + /// A 64-bit integer value is read + + virtual void value(UInt64 v); + /// An unsigned 64-bit integer value is read. This will only be + /// triggered if the value cannot fit into a signed 64-bit integer. +#endif + + virtual void value(const std::string& s); + /// A string value is read. + + virtual void value(double d); + /// A double value is read. + + virtual void value(bool b); + /// A boolean value is read. + + virtual void null(); + /// A null value is read. + +private: + void setValue(const Poco::Dynamic::Var& value); + typedef std::stack Stack; + + Stack _stack; + std::string _key; + Dynamic::Var _result; + bool _preserveObjectOrder; +}; + + +inline Dynamic::Var ParseHandler::result() const +{ + return _result; +} + + +inline void ParseHandler::value(int v) +{ + setValue(v); +} + + +inline void ParseHandler::value(unsigned v) +{ + setValue(v); +} + + +#if defined(POCO_HAVE_INT64) +inline void ParseHandler::value(Int64 v) +{ + setValue(v); +} + + +inline void ParseHandler::value(UInt64 v) +{ + setValue(v); +} +#endif + + +inline void ParseHandler::value(const std::string& s) +{ + setValue(s); +} + + +inline void ParseHandler::value(double d) +{ + setValue(d); +} + + +inline void ParseHandler::value(bool b) +{ + setValue(b); +} + + +inline void ParseHandler::null() +{ + Poco::Dynamic::Var empty; + setValue(empty); +} + + +}} // namespace Poco::JSON + + +#endif // JSON_ParseHandler_INCLUDED diff --git a/JSON/include/Poco/JSON/PrintHandler.h b/JSON/include/Poco/JSON/PrintHandler.h new file mode 100644 index 000000000..3d85cce67 --- /dev/null +++ b/JSON/include/Poco/JSON/PrintHandler.h @@ -0,0 +1,139 @@ +// +// PrintHandler.h +// +// $Id$ +// +// Library: JSON +// Package: JSON +// Module: PrintHandler +// +// Definition of the PrintHandler class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#ifndef JSON_PrintHandler_INCLUDED +#define JSON_PrintHandler_INCLUDED + + +#include "Poco/JSON/JSON.h" +#include "Poco/JSON/Handler.h" + + +namespace Poco { +namespace JSON { + + +class JSON_API PrintHandler : public Handler + /// PrintHandler formats and prints the JSON object + /// to either user-provided std::ostream or standard out. + /// If indent i zero, the output is condensed JSON string, + /// otherwise, the proper indentation is applied to elements. +{ +public: + static const unsigned JSON_PRINT_FLAT = 0; + + PrintHandler(unsigned indent = 0); + /// Creates the PrintHandler. + + PrintHandler(std::ostream& out, unsigned indent = 0); + /// Creates the PrintHandler. + + ~PrintHandler(); + /// Destroys the PrintHandler. + + void startObject(); + /// The parser has read a '{'; a new object is started. + /// If indent is greater than zero, a newline will be apended. + + void endObject(); + /// The parser has read a '}'; the object is closed. + + void startArray(); + /// The parser has read a [; a new array will be started. + /// If indent is greater than zero, a newline will be apended. + + void endArray(); + /// The parser has read a ]; the array is closed. + + void key(const std::string& k); + /// A key of an object is read; it will be written to the output, + /// followed by a ':'. If indent is greater than zero, the colon + /// is padded by a space before and after. + + void null(); + /// A null value is read; "null" will be written to the output. + + void value(int v); + /// An integer value is read. + + void value(unsigned v); + /// An unsigned value is read. This will only be triggered if the + /// value cannot fit into a signed int. + +#if defined(POCO_HAVE_INT64) + void value(Int64 v); + /// A 64-bit integer value is read; it will be written to the output. + + void value(UInt64 v); + /// An unsigned 64-bit integer value is read; it will be written to the output. +#endif + + void value(const std::string& value); + /// A string value is read; it will be fromatted and written to the output. + + void value(double d); + /// A double value is read; it will be written to the output. + + void value(bool b); + /// A boolean value is read; it will be written to the output. + + void comma(); + /// A comma is read; it will be written to the output as "true" or "false". + + void setIndent(unsigned indent) + { + _indent = indent; + } + +private: + + const char* endLine() const; + unsigned indent(); + bool printFlat() const; + + std::ostream& _out; + unsigned _indent; + std::string _tab; + bool _array; +}; + + +}} // namespace Poco::JSON + + +#endif // JSON_PrintHandler_INCLUDED diff --git a/JSON/include/Poco/JSON/Stringifier.h b/JSON/include/Poco/JSON/Stringifier.h index f7ef4ca89..74e4b607b 100644 --- a/JSON/include/Poco/JSON/Stringifier.h +++ b/JSON/include/Poco/JSON/Stringifier.h @@ -53,12 +53,39 @@ class JSON_API Stringifier /// Helper class for creating a String from a JSON object or array { public: - static void stringify(const Dynamic::Var& any, std::ostream& out, unsigned int indent = 0); + static void condense(const Dynamic::Var& any, std::ostream& out); + /// 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, bool preserveInsertionOrder, std::ostream& out, unsigned int indent = 0); + /// Writes a String representation of the value to the output stream while preserving the insertion order. + /// When indent is 0, the generated string will be created as small as possible (condensed). + /// When preserveInsertionOrder is true, the original string object members order will be preserved. + /// This is a "shortcut" to stringify(any, out, indent, -1, preserveInsertionOrder). + + static void stringify(const Dynamic::Var& any, std::ostream& out, unsigned int indent = 0, int step = -1, bool preserveInsertionOrder = 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. + + static void formatString(const std::string& value, std::ostream& out); + /// Formats the JSON string and streams it into ostream. }; +inline void Stringifier::condense(const Dynamic::Var& any, std::ostream& out) +{ + stringify(any, out, 0, -1, true); +} + + +inline void Stringifier::stringify(const Dynamic::Var& any, bool preserveInsertionOrder, std::ostream& out, unsigned int indent) +{ + stringify(any, out, indent, -1, preserveInsertionOrder); +} + + }} // namespace Poco::JSON diff --git a/JSON/samples/Benchmark/src/Benchmark.cpp b/JSON/samples/Benchmark/src/Benchmark.cpp index 64ddd65d5..4d640cb37 100644 --- a/JSON/samples/Benchmark/src/Benchmark.cpp +++ b/JSON/samples/Benchmark/src/Benchmark.cpp @@ -34,7 +34,7 @@ #include "Poco/Timestamp.h" #include "Poco/JSON/Parser.h" -#include "Poco/JSON/DefaultHandler.h" +#include "Poco/JSON/ParseHandler.h" #include "Poco/JSON/JSONException.h" #include "Poco/Environment.h" #include "Poco/Path.h" @@ -82,7 +82,7 @@ int main(int argc, char** argv) try { - Poco::JSON::DefaultHandler handler; + Poco::JSON::ParseHandler handler; Poco::JSON::Parser parser; parser.setHandler(&handler); Poco::Timestamp time1; diff --git a/JSON/src/Array.cpp b/JSON/src/Array.cpp index 0936825ad..4c79548ed 100644 --- a/JSON/src/Array.cpp +++ b/JSON/src/Array.cpp @@ -120,8 +120,10 @@ bool Array::isObject(unsigned int index) const } -void Array::stringify(std::ostream& out, unsigned int indent) const +void Array::stringify(std::ostream& out, unsigned int indent, int step) const { + if (step == -1) step = indent; + out << "["; if (indent > 0) out << std::endl; @@ -130,15 +132,22 @@ void Array::stringify(std::ostream& out, unsigned int indent) const { for(int i = 0; i < indent; i++) out << ' '; - Stringifier::stringify(*it, out, indent); + Stringifier::stringify(*it, out, indent + step, step); if ( ++it != _values.end() ) { out << ","; - if ( indent > 0 ) out << std::endl; + if (step > 0) out << '\n'; } } + if (step > 0) out << '\n'; + + if (indent >= step) indent -= step; + + for (int i = 0; i < indent; i++) + out << ' '; + out << "]"; } diff --git a/JSON/src/Handler.cpp b/JSON/src/Handler.cpp index 47e17fe93..502a841ce 100644 --- a/JSON/src/Handler.cpp +++ b/JSON/src/Handler.cpp @@ -42,9 +42,19 @@ namespace Poco { namespace JSON { +Handler::Handler() +{ +} + + Handler::~Handler() { } +void Handler::comma() +{ +} + + } } // namespace Poco::JSON diff --git a/JSON/src/Object.cpp b/JSON/src/Object.cpp index a54c5f527..97026c3fd 100644 --- a/JSON/src/Object.cpp +++ b/JSON/src/Object.cpp @@ -48,20 +48,20 @@ namespace Poco { namespace JSON { -Object::Object() +Object::Object(bool preserveInsertionOrder): _preserveInsOrder(preserveInsertionOrder) { - } -Object::Object(const Object& copy) : _values(copy._values) +Object::Object(const Object& copy) : _values(copy._values), + _keys(copy._keys), + _preserveInsOrder(copy._preserveInsOrder) { } Object::~Object() { - } @@ -114,27 +114,43 @@ void Object::getNames(std::vector& names) const } -void Object::stringify(std::ostream& out, unsigned int indent) const +void Object::stringify(std::ostream& out, unsigned int indent, int step) const { - out << '{'; + if (step == -1) step = indent; - if (indent > 0) out << std::endl; + if(!_preserveInsOrder) + doStringify(_values, out, indent, step); + else + doStringify(_keys, out, indent, step); +} - for (ValueMap::const_iterator it = _values.begin(); it != _values.end();) + +const std::string& Object::getKey(KeyPtrList::const_iterator& iter) const +{ + ValueMap::const_iterator it = _values.begin(); + ValueMap::const_iterator end = _values.end(); + for (; it != end; ++it) { - for(int i = 0; i < indent; i++) out << ' '; - - out << '"' << it->first << '"'; - out << (( indent > 0 ) ? " : " : ":"); - - Stringifier::stringify(it->second, out, indent); - - if ( ++it != _values.end() ) out << ','; - - if ( indent > 0 ) out << std::endl; + if (it->second == **iter) return it->first; + } + + throw NotFoundException((*iter)->convert()); +} + + +void Object::set(const std::string& key, const Dynamic::Var& value) +{ + _values[key] = value; + if (_preserveInsOrder) + { + KeyPtrList::iterator it = _keys.begin(); + KeyPtrList::iterator end = _keys.end(); + for (; it != end; ++it) + { + if (key == **it) return; + } + _keys.push_back(&_values[key]); } - - out << '}'; } diff --git a/JSON/src/DefaultHandler.cpp b/JSON/src/ParseHandler.cpp similarity index 85% rename from JSON/src/DefaultHandler.cpp rename to JSON/src/ParseHandler.cpp index de5b216b2..db154165e 100644 --- a/JSON/src/DefaultHandler.cpp +++ b/JSON/src/ParseHandler.cpp @@ -1,11 +1,11 @@ // -// DefaultHandler.cpp +// ParseHandler.cpp // // $Id$ // // Library: JSON // Package: JSON -// Module: DefaultHandler +// Module: ParseHandler // // Copyright (c) 2012, Applied Informatics Software Engineering GmbH. // and Contributors. @@ -34,7 +34,7 @@ // -#include "Poco/JSON/DefaultHandler.h" +#include "Poco/JSON/ParseHandler.h" #include "Poco/JSON/Object.h" @@ -45,19 +45,20 @@ namespace Poco { namespace JSON { -DefaultHandler::DefaultHandler() : Handler() +ParseHandler::ParseHandler(bool preserveObjectOrder) : Handler(), + _preserveObjectOrder(preserveObjectOrder) { } -DefaultHandler::~DefaultHandler() +ParseHandler::~ParseHandler() { } -void DefaultHandler::startObject() +void ParseHandler::startObject() { - Object::Ptr newObj = new Object(); + Object::Ptr newObj = new Object(_preserveObjectOrder); if ( _stack.empty() ) // The first object { @@ -85,13 +86,13 @@ void DefaultHandler::startObject() } -void DefaultHandler::endObject() +void ParseHandler::endObject() { _stack.pop(); } -void DefaultHandler::startArray() +void ParseHandler::startArray() { Array::Ptr newArr = new Array(); @@ -121,19 +122,19 @@ void DefaultHandler::startArray() } -void DefaultHandler::endArray() +void ParseHandler::endArray() { _stack.pop(); } -void DefaultHandler::key(const std::string& k) +void ParseHandler::key(const std::string& k) { _key = k; } -void DefaultHandler::setValue(const Var& value) +void ParseHandler::setValue(const Var& value) { Var parent = _stack.top(); diff --git a/JSON/src/Parser.cpp b/JSON/src/Parser.cpp index 910aed398..540b34d76 100644 --- a/JSON/src/Parser.cpp +++ b/JSON/src/Parser.cpp @@ -67,11 +67,11 @@ public: bool start(char c, std::istream& istr) { if (c == '{' - || c == '}' - || c == ']' - || c == '[' - || c == ',' - || c == ':') + || c == '}' + || c == ']' + || c == '[' + || c == ',' + || c == ':') { _value = c; return true; @@ -594,6 +594,7 @@ bool Parser::readRow(bool firstCall) { if (token->asChar() == ',') { + _handler->comma(); return true; // Read next row } else if (token->asChar() == '}') @@ -636,46 +637,46 @@ void Parser::readValue(const Token* token) if (_handler != NULL) { #if defined(POCO_HAVE_INT64) - try - { - Int64 value = token->asInteger64(); - // if number is 32-bit, then handle as such - if ( value > std::numeric_limits::max() - || value < std::numeric_limits::min() ) - { - _handler->value(value); - } - else - { - _handler->value(static_cast(value)); - } - } - // try to handle error as unsigned in case of overflow - catch ( const SyntaxException& ) - { - UInt64 value = token->asUnsignedInteger64(); - // if number is 32-bit, then handle as such - if ( value > std::numeric_limits::max() ) - { - _handler->value(value); - } - else - { - _handler->value(static_cast(value)); - } - } + try + { + Int64 value = token->asInteger64(); + // if number is 32-bit, then handle as such + if ( value > std::numeric_limits::max() + || value < std::numeric_limits::min() ) + { + _handler->value(value); + } + else + { + _handler->value(static_cast(value)); + } + } + // try to handle error as unsigned in case of overflow + catch ( const SyntaxException& ) + { + UInt64 value = token->asUnsignedInteger64(); + // if number is 32-bit, then handle as such + if ( value > std::numeric_limits::max() ) + { + _handler->value(value); + } + else + { + _handler->value(static_cast(value)); + } + } #else - try - { - int value = token->asInteger(); - _handle->value(value); - } - // try to handle error as unsigned in case of overflow - catch ( const SyntaxException& ) - { - unsigned value = token->asUnsignedInteger(); - _handle->value(value); - } + try + { + int value = token->asInteger(); + _handle->value(value); + } + // try to handle error as unsigned in case of overflow + catch ( const SyntaxException& ) + { + unsigned value = token->asUnsignedInteger(); + _handle->value(value); + } #endif } break; @@ -773,13 +774,16 @@ bool Parser::readElements(bool firstCall) token = nextToken(); - if ( token->is(Token::SEPARATOR_TOKEN) ) + if (token->is(Token::SEPARATOR_TOKEN)) { if (token->asChar() == ']') return false; // End of array if (token->asChar() == ',') + { + _handler->comma(); return true; + } throw JSONException(format("Invalid separator '%c' found. Expecting , or ]", token->asChar())); } diff --git a/JSON/src/PrintHandler.cpp b/JSON/src/PrintHandler.cpp new file mode 100644 index 000000000..f87dd5429 --- /dev/null +++ b/JSON/src/PrintHandler.cpp @@ -0,0 +1,194 @@ +// +// PrintHandler.cpp +// +// $Id$ +// +// Library: JSON +// Package: JSON +// Module: PrintHandler +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#include "Poco/JSON/PrintHandler.h" +#include "Poco/JSON/Stringifier.h" + + +namespace Poco { +namespace JSON { + + +PrintHandler::PrintHandler(unsigned indent): + _out(std::cout), + _indent(indent), + _array(false) +{ +} + + +PrintHandler::PrintHandler(std::ostream& out, unsigned indent): + _out(out), + _indent(indent), + _array(false) +{ +} + + +PrintHandler::~PrintHandler() +{ +} + + +const char* PrintHandler::endLine() const +{ + if (!printFlat()) return "\n"; + else return ""; +} + + +bool PrintHandler::printFlat() const +{ + return _indent == JSON_PRINT_FLAT; +} + + +unsigned PrintHandler::indent() +{ + if (!printFlat()) return _indent; + + return 0; +} + + +void PrintHandler::startObject() +{ + _out << '{'; + _out << endLine(); + _tab.append(indent(), ' '); +} + + +void PrintHandler::endObject() +{ + if( _tab.length() >= indent()) + _tab.erase(_tab.length() - indent()); + + _out << endLine() << _tab << '}'; +} + + +void PrintHandler::startArray() +{ + _out << '[' << endLine(); + _tab.append(indent(), ' '); + _array = true; +} + + +void PrintHandler::endArray() +{ + _tab.erase(_tab.length() - indent()); + _out << endLine() << _tab << ']'; + _array = false; +} + + +void PrintHandler::key(const std::string& k) +{ + _out << _tab << '"' << k << '"'; + if (!printFlat()) _out << ' '; + _out << ':'; + if (!printFlat()) _out << ' '; +} + + +void PrintHandler::null() +{ + if (_array) _out << _tab; + _out << "null"; +} + + +void PrintHandler::value(int v) +{ + if (_array) _out << _tab; + _out << v; +} + + +void PrintHandler::value(unsigned v) +{ + if (_array) _out << _tab; + _out << v; +} + + +#if defined(POCO_HAVE_INT64) +void PrintHandler::value(Int64 v) +{ + if (_array) _out << _tab; + _out << v; +} + + +void PrintHandler::value(UInt64 v) +{ + if (_array) _out << _tab; + _out << v; +} +#endif + + +void PrintHandler::value(const std::string& value) +{ + if (_array) _out << _tab; + Stringifier::formatString(value, _out); +} + + + +void PrintHandler::value(double d) +{ + if (_array) _out << _tab; + _out << d; +} + + +void PrintHandler::value(bool b) +{ + if (_array) _out << _tab; + _out << b; +} + + +void PrintHandler::comma() +{ + _out << ',' << endLine(); +} + + +} } // namespace Poco::JSON diff --git a/JSON/src/Stringifier.cpp b/JSON/src/Stringifier.cpp index 46842e4c8..687d37b70 100644 --- a/JSON/src/Stringifier.cpp +++ b/JSON/src/Stringifier.cpp @@ -47,27 +47,29 @@ namespace Poco { namespace JSON { -void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int indent) +void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int indent, int step, bool preserveInsertionOrder) { + if (step == -1) step = indent; + if ( any.type() == typeid(Object) ) { const Object& o = any.extract(); - o.stringify(out, indent == 0 ? 0 : indent + 2); + o.stringify(out, indent == 0 ? 0 : indent, step); } else if ( any.type() == typeid(Array) ) { const Array& a = any.extract(); - a.stringify(out, indent == 0 ? 0 : indent + 2); + a.stringify(out, indent == 0 ? 0 : indent, step); } else if ( any.type() == typeid(Object::Ptr) ) { const Object::Ptr& o = any.extract(); - o->stringify(out, indent == 0 ? 0 : indent + 2); + o->stringify(out, indent == 0 ? 0 : indent, step); } else if ( any.type() == typeid(Array::Ptr) ) { const Array::Ptr& a = any.extract(); - a->stringify(out, indent == 0 ? 0 : indent + 2); + a->stringify(out, indent == 0 ? 0 : indent, step); } else if ( any.isEmpty() ) { @@ -75,48 +77,8 @@ void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int inde } else if ( any.isString() ) { - out << '"'; std::string value = any.convert(); - for(std::string::const_iterator it = value.begin(); it != value.end(); ++it) - { - switch (*it) - { - case '"': - out << "\\\""; - break; - case '\\': - out << "\\\\"; - break; - case '\b': - out << "\\b"; - break; - case '\f': - out << "\\f"; - break; - case '\n': - out << "\\n"; - break; - case '\r': - out << "\\r"; - break; - case '\t': - out << "\\t"; - break; - default: - { - if ( *it > 0 && *it <= 0x1F ) - { - out << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast(*it); - } - else - { - out << *it; - } - break; - } - } - } - out << '"'; + formatString(value, out); } else { @@ -125,4 +87,49 @@ void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int inde } +void Stringifier::formatString(const std::string& value, std::ostream& out) +{ + out << '"'; + for (std::string::const_iterator it = value.begin(); it != value.end(); ++it) + { + switch (*it) + { + case '"': + out << "\\\""; + break; + case '\\': + out << "\\\\"; + break; + case '\b': + out << "\\b"; + break; + case '\f': + out << "\\f"; + break; + case '\n': + out << "\\n"; + break; + case '\r': + out << "\\r"; + break; + case '\t': + out << "\\t"; + break; + default: + { + if ( *it > 0 && *it <= 0x1F ) + { + out << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast(*it); + } + else + { + out << *it; + } + break; + } + } + } + out << '"'; +} + } } // Namespace Poco::JSON diff --git a/JSON/testsuite/src/JSONTest.cpp b/JSON/testsuite/src/JSONTest.cpp index a0e1a1617..b580c02e0 100644 --- a/JSON/testsuite/src/JSONTest.cpp +++ b/JSON/testsuite/src/JSONTest.cpp @@ -39,7 +39,8 @@ #include "Poco/JSON/Query.h" #include "Poco/JSON/JSONException.h" #include "Poco/JSON/Stringifier.h" -#include "Poco/JSON/DefaultHandler.h" +#include "Poco/JSON/ParseHandler.h" +#include "Poco/JSON/PrintHandler.h" #include "Poco/JSON/Template.h" #include "Poco/Path.h" @@ -87,7 +88,7 @@ void JSONTest::testNullProperty() Var result; try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -114,7 +115,7 @@ void JSONTest::testTrueProperty() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -143,7 +144,7 @@ void JSONTest::testFalseProperty() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -172,7 +173,7 @@ void JSONTest::testNumberProperty() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -202,7 +203,7 @@ void JSONTest::testUnsignedNumberProperty() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -233,7 +234,7 @@ void JSONTest::testNumber64Property() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -263,7 +264,7 @@ void JSONTest::testUnsignedNumber64Property() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -294,7 +295,7 @@ void JSONTest::testStringProperty() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -323,7 +324,7 @@ void JSONTest::testEmptyObject() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -349,7 +350,7 @@ void JSONTest::testDoubleProperty() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -378,7 +379,7 @@ void JSONTest::testDouble2Property() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -407,7 +408,7 @@ void JSONTest::testDouble3Property() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -436,7 +437,7 @@ void JSONTest::testObjectProperty() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -472,7 +473,7 @@ void JSONTest::testObjectArray() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -494,6 +495,37 @@ void JSONTest::testObjectArray() } +void JSONTest::testArrayOfObjects() +{ + std::string json = "[ {\"test\" : 0}, { \"test1\" : [1, 2, 3], \"test2\" : 4 } ]"; + Parser parser; + Var result; + + try + { + ParseHandler handler; + parser.setHandler(&handler); + parser.parse(json); + result = handler.result(); + } + catch(JSONException& jsone) + { + std::cout << jsone.message() << std::endl; + assert(false); + } + + assert(result.type() == typeid(Array::Ptr)); + Array::Ptr arr = result.extract(); + Object::Ptr object = arr->getObject(0); + assert (object->getValue("test") == 0); + object = arr->getObject(1); + arr = object->getArray("test1"); + result = arr->get(0); + assert (result == 1); + +} + + void JSONTest::testEmptyArray() { std::string json = "[]"; @@ -502,7 +534,7 @@ void JSONTest::testEmptyArray() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -528,7 +560,7 @@ void JSONTest::testNestedArray() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -554,7 +586,7 @@ void JSONTest::testNullElement() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -582,7 +614,7 @@ void JSONTest::testTrueElement() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -611,7 +643,7 @@ void JSONTest::testFalseElement() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -640,7 +672,7 @@ void JSONTest::testNumberElement() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -669,7 +701,7 @@ void JSONTest::testStringElement() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -698,7 +730,7 @@ void JSONTest::testEmptyObjectElement() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -725,7 +757,7 @@ void JSONTest::testDoubleElement() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -754,7 +786,7 @@ void JSONTest::testOptValue() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -781,7 +813,7 @@ void JSONTest::testQuery() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -801,15 +833,77 @@ void JSONTest::testQuery() } +void JSONTest::testPrintHandler() +{ + std::string json = "{ \"name\" : \"Homer\", \"age\" : 38, \"wife\" : \"Marge\", \"age\" : 36, \"children\" : [ \"Bart\", \"Lisa\", \"Maggie\" ] }"; + Parser parser; + std::ostringstream ostr; + PrintHandler handler(ostr); + parser.setHandler(&handler); + parser.parse(json); + assert (ostr.str() == "{\"name\":\"Homer\",\"age\":38,\"wife\":\"Marge\",\"age\":36,\"children\":[\"Bart\",\"Lisa\",\"Maggie\"]}"); + + handler.setIndent(1); + ostr.str(""); + parser.parse(json); + assert (ostr.str() == "{\n" + " \"name\" : \"Homer\",\n" + " \"age\" : 38,\n" + " \"wife\" : \"Marge\",\n" + " \"age\" : 36,\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ]\n" + "}" + ); + + handler.setIndent(2); + ostr.str(""); + parser.parse(json); + assert (ostr.str() == "{\n" + " \"name\" : \"Homer\",\n" + " \"age\" : 38,\n" + " \"wife\" : \"Marge\",\n" + " \"age\" : 36,\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ]\n" + "}" + ); + + handler.setIndent(4); + ostr.str(""); + parser.parse(json); + assert (ostr.str() == "{\n" + " \"name\" : \"Homer\",\n" + " \"age\" : 38,\n" + " \"wife\" : \"Marge\",\n" + " \"age\" : 36,\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ]\n" + "}" + ); +} + + void JSONTest::testStringify() { - std::string json = "{ \"name\" : \"Franky\", \"children\" : [ \"Jonas\", \"Ellen\" ] }"; + std::string json = "{ \"Simpsons\" : { \"husband\" : { \"name\" : \"Homer\" , \"age\" : 38 }, \"wife\" : { \"name\" : \"Marge\", \"age\" : 36 }, " + "\"children\" : [ \"Bart\", \"Lisa\", \"Maggie\" ], " + "\"address\" : { \"number\" : 742, \"street\" : \"Evergreen Terrace\", \"town\" : \"Springfield\" } } }"; Parser parser; Var result; try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -822,9 +916,221 @@ void JSONTest::testStringify() assert(result.type() == typeid(Object::Ptr)); std::ostringstream ostr; + + Stringifier::condense(result, ostr); + std::string str = "{" + "\"Simpsons\":{" + "\"address\":{" + "\"number\":742," + "\"street\":\"Evergreen Terrace\"," + "\"town\":\"Springfield\"" + "}," + "\"children\":[" + "\"Bart\"," + "\"Lisa\"," + "\"Maggie\"]," + "\"husband\":{" + "\"age\":38," + "\"name\":\"Homer\"" + "}," + "\"wife\":{" + "\"age\":36,\"name\":\"Marge\"" + "}}}"; + assert (ostr.str() == str); + + ostr.str(""); Stringifier::stringify(result, ostr); - //TODO: need map that does not order for internal container - assert (ostr.str() == "{\"name\":\"Franky\",\"children\":[\"Jonas\",\"Ellen\"]}"); + assert (ostr.str() == str); + + ostr.str(""); + Stringifier::stringify(result, ostr, 1); + str = "{\n" + " \"Simpsons\" : {\n" + " \"address\" : {\n" + " \"number\" : 742,\n" + " \"street\" : \"Evergreen Terrace\",\n" + " \"town\" : \"Springfield\"\n" + " },\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ],\n" + " \"husband\" : {\n" + " \"age\" : 38,\n" + " \"name\" : \"Homer\"\n" + " },\n" + " \"wife\" : {\n" + " \"age\" : 36,\n" + " \"name\" : \"Marge\"\n" + " }\n" + " }\n" + "}"; + assert (ostr.str() == str); + + ostr.str(""); + Stringifier::stringify(result, ostr, 2); + str = "{\n" + " \"Simpsons\" : {\n" + " \"address\" : {\n" + " \"number\" : 742,\n" + " \"street\" : \"Evergreen Terrace\",\n" + " \"town\" : \"Springfield\"\n" + " },\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ],\n" + " \"husband\" : {\n" + " \"age\" : 38,\n" + " \"name\" : \"Homer\"\n" + " },\n" + " \"wife\" : {\n" + " \"age\" : 36,\n" + " \"name\" : \"Marge\"\n" + " }\n" + " }\n" + "}"; + assert (ostr.str() == str); + + ostr.str(""); + Stringifier::stringify(result, ostr, 4); + str = "{\n" + " \"Simpsons\" : {\n" + " \"address\" : {\n" + " \"number\" : 742,\n" + " \"street\" : \"Evergreen Terrace\",\n" + " \"town\" : \"Springfield\"\n" + " },\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ],\n" + " \"husband\" : {\n" + " \"age\" : 38,\n" + " \"name\" : \"Homer\"\n" + " },\n" + " \"wife\" : {\n" + " \"age\" : 36,\n" + " \"name\" : \"Marge\"\n" + " }\n" + " }\n" + "}"; + assert (ostr.str() == str); +} + + +void JSONTest::testStringifyPreserveOrder() +{ + std::string json = "{ \"Simpsons\" : { \"husband\" : { \"name\" : \"Homer\" , \"age\" : 38 }, \"wife\" : { \"name\" : \"Marge\", \"age\" : 36 }, " + "\"children\" : [ \"Bart\", \"Lisa\", \"Maggie\" ], " + "\"address\" : { \"number\" : 742, \"street\" : \"Evergreen Terrace\", \"town\" : \"Springfield\" } } }"; + Parser parser; + Var result; + + try + { + ParseHandler handler(true); + parser.setHandler(&handler); + parser.parse(json); + result = handler.result(); + } + catch(JSONException& jsone) + { + std::cout << jsone.message() << std::endl; + assert(false); + } + + assert(result.type() == typeid(Object::Ptr)); + std::ostringstream ostr; + + Stringifier::condense(result, ostr); + assert (ostr.str() == "{\"Simpsons\":{\"husband\":{\"name\":\"Homer\",\"age\":38},\"wife\":{\"name\":\"Marge\",\"age\":36}," + "\"children\":[\"Bart\",\"Lisa\",\"Maggie\"]," + "\"address\":{\"number\":742,\"street\":\"Evergreen Terrace\",\"town\":\"Springfield\"}}}"); + + ostr.str(""); + Stringifier::stringify(result, ostr); + assert (ostr.str() == "{\"Simpsons\":{\"husband\":{\"name\":\"Homer\",\"age\":38},\"wife\":{\"name\":\"Marge\",\"age\":36}," + "\"children\":[\"Bart\",\"Lisa\",\"Maggie\"]," + "\"address\":{\"number\":742,\"street\":\"Evergreen Terrace\",\"town\":\"Springfield\"}}}"); + + ostr.str(""); + Stringifier::stringify(result, ostr, 1); + assert (ostr.str() == "{\n" + " \"Simpsons\" : {\n" + " \"husband\" : {\n" + " \"name\" : \"Homer\",\n" + " \"age\" : 38\n" + " },\n" + " \"wife\" : {\n" + " \"name\" : \"Marge\",\n" + " \"age\" : 36\n" + " },\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ],\n" + " \"address\" : {\n" + " \"number\" : 742,\n" + " \"street\" : \"Evergreen Terrace\",\n" + " \"town\" : \"Springfield\"\n" + " }\n" + " }\n" + "}"); + + ostr.str(""); + Stringifier::stringify(result, ostr, 2); + assert (ostr.str() == "{\n" + " \"Simpsons\" : {\n" + " \"husband\" : {\n" + " \"name\" : \"Homer\",\n" + " \"age\" : 38\n" + " },\n" + " \"wife\" : {\n" + " \"name\" : \"Marge\",\n" + " \"age\" : 36\n" + " },\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ],\n" + " \"address\" : {\n" + " \"number\" : 742,\n" + " \"street\" : \"Evergreen Terrace\",\n" + " \"town\" : \"Springfield\"\n" + " }\n" + " }\n" + "}"); + + ostr.str(""); + Stringifier::stringify(result, ostr, 4); + assert (ostr.str() == "{\n" + " \"Simpsons\" : {\n" + " \"husband\" : {\n" + " \"name\" : \"Homer\",\n" + " \"age\" : 38\n" + " },\n" + " \"wife\" : {\n" + " \"name\" : \"Marge\",\n" + " \"age\" : 36\n" + " },\n" + " \"children\" : [\n" + " \"Bart\",\n" + " \"Lisa\",\n" + " \"Maggie\"\n" + " ],\n" + " \"address\" : {\n" + " \"number\" : 742,\n" + " \"street\" : \"Evergreen Terrace\",\n" + " \"town\" : \"Springfield\"\n" + " }\n" + " }\n" + "}"); } @@ -852,7 +1158,7 @@ void JSONTest::testValidJanssonFiles() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(fis); result = handler.result(); @@ -900,7 +1206,7 @@ void JSONTest::testInvalidJanssonFiles() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(fis); result = handler.result(); @@ -944,7 +1250,7 @@ void JSONTest::testInvalidUnicodeJanssonFiles() try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(fis); result = handler.result(); @@ -977,6 +1283,7 @@ void JSONTest::testTemplate() tpl.render(data, std::cout); } + void JSONTest::testUnicode() { const unsigned char supp[] = {0x61, 0xE1, 0xE9, 0x78, 0xED, 0xF3, 0xFA, 0x0}; @@ -988,7 +1295,7 @@ void JSONTest::testUnicode() Var result; try { - DefaultHandler handler; + ParseHandler handler; parser.setHandler(&handler); parser.parse(json); result = handler.result(); @@ -1012,6 +1319,7 @@ void JSONTest::testUnicode() assert(test.convert() == original); } + std::string JSONTest::getTestFilesPath(const std::string& type) { std::ostringstream ostr; @@ -1060,6 +1368,7 @@ CppUnit::Test* JSONTest::suite() CppUnit_addTest(pSuite, JSONTest, testDouble3Property); CppUnit_addTest(pSuite, JSONTest, testObjectProperty); CppUnit_addTest(pSuite, JSONTest, testObjectArray); + CppUnit_addTest(pSuite, JSONTest, testArrayOfObjects); CppUnit_addTest(pSuite, JSONTest, testEmptyArray); CppUnit_addTest(pSuite, JSONTest, testNestedArray); CppUnit_addTest(pSuite, JSONTest, testNullElement); @@ -1071,7 +1380,9 @@ CppUnit::Test* JSONTest::suite() CppUnit_addTest(pSuite, JSONTest, testDoubleElement); CppUnit_addTest(pSuite, JSONTest, testOptValue); CppUnit_addTest(pSuite, JSONTest, testQuery); + CppUnit_addTest(pSuite, JSONTest, testPrintHandler); CppUnit_addTest(pSuite, JSONTest, testStringify); + CppUnit_addTest(pSuite, JSONTest, testStringifyPreserveOrder); CppUnit_addTest(pSuite, JSONTest, testValidJanssonFiles); CppUnit_addTest(pSuite, JSONTest, testInvalidJanssonFiles); CppUnit_addTest(pSuite, JSONTest, testInvalidUnicodeJanssonFiles); diff --git a/JSON/testsuite/src/JSONTest.h b/JSON/testsuite/src/JSONTest.h index 7f0b92049..59d0d6003 100644 --- a/JSON/testsuite/src/JSONTest.h +++ b/JSON/testsuite/src/JSONTest.h @@ -62,6 +62,7 @@ public: void testDouble3Property(); void testObjectProperty(); void testObjectArray(); + void testArrayOfObjects(); void testEmptyArray(); void testNestedArray(); void testNullElement(); @@ -73,7 +74,9 @@ public: void testDoubleElement(); void testOptValue(); void testQuery(); + void testPrintHandler(); void testStringify(); + void testStringifyPreserveOrder(); void testValidJanssonFiles(); void testInvalidJanssonFiles();