ソースを参照

GH #119: JSON::Object holds values in ordered map

- fixed GH #119: JSON::Object holds values in ordered map
- added PrintHandler
- renamed DefaultHandler to ParseHandler
- redefined DefaultHandler as typedef to ParseHandler
aleks-f 13 年 前
コミット
42ff341cb9

+ 4 - 0
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)
 ==========================

+ 7 - 0
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

+ 0 - 7
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");

+ 7 - 1
JSON/JSON_CE_vs90.vcproj

@@ -433,7 +433,7 @@
 			<File
 				RelativePath=".\src\Array.cpp"/>
 			<File
-				RelativePath=".\src\DefaultHandler.cpp"/>
+				RelativePath=".\src\ParseHandler.cpp"/>
 			<File
 				RelativePath=".\src\Handler.cpp"/>
 			<File
@@ -442,6 +442,8 @@
 				RelativePath=".\src\Object.cpp"/>
 			<File
 				RelativePath=".\src\Parser.cpp"/>
+			<File
+				RelativePath=".\src\PrintHandler.cpp"/>
 			<File
 				RelativePath=".\src\Query.cpp"/>
 			<File
@@ -467,6 +469,10 @@
 				RelativePath=".\include\Poco\JSON\Object.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Parser.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\ParseHandler.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\PrintHandler.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Query.h"/>
 			<File

+ 4 - 1
JSON/JSON_vs100.vcxproj

@@ -273,7 +273,9 @@
     <ClInclude Include="include\Poco\JSON\JSON.h" />
     <ClInclude Include="include\Poco\JSON\JSONException.h" />
     <ClInclude Include="include\Poco\JSON\Object.h" />
+    <ClInclude Include="include\Poco\JSON\ParseHandler.h" />
     <ClInclude Include="include\Poco\JSON\Parser.h" />
+    <ClInclude Include="include\Poco\JSON\PrintHandler.h" />
     <ClInclude Include="include\Poco\JSON\Query.h" />
     <ClInclude Include="include\Poco\JSON\Stringifier.h" />
     <ClInclude Include="include\Poco\JSON\Template.h" />
@@ -281,11 +283,12 @@
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="src\Array.cpp" />
-    <ClCompile Include="src\DefaultHandler.cpp" />
+    <ClCompile Include="src\ParseHandler.cpp" />
     <ClCompile Include="src\Handler.cpp" />
     <ClCompile Include="src\JSONException.cpp" />
     <ClCompile Include="src\Object.cpp" />
     <ClCompile Include="src\Parser.cpp" />
+    <ClCompile Include="src\PrintHandler.cpp" />
     <ClCompile Include="src\Query.cpp" />
     <ClCompile Include="src\Stringifier.cpp" />
     <ClCompile Include="src\Template.cpp" />

+ 12 - 3
JSON/JSON_vs100.vcxproj.filters

@@ -42,14 +42,17 @@
     <ClInclude Include="include\Poco\JSON\TemplateCache.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\Poco\JSON\PrintHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="include\Poco\JSON\ParseHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="src\Array.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="src\DefaultHandler.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="src\Handler.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -74,5 +77,11 @@
     <ClCompile Include="src\TemplateCache.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\PrintHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="src\ParseHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 4 - 1
JSON/JSON_vs110.vcxproj

@@ -274,11 +274,12 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="src\Array.cpp"/>
-    <ClCompile Include="src\DefaultHandler.cpp"/>
+    <ClCompile Include="src\ParseHandler.cpp"/>
     <ClCompile Include="src\Handler.cpp"/>
     <ClCompile Include="src\JSONException.cpp"/>
     <ClCompile Include="src\Object.cpp"/>
     <ClCompile Include="src\Parser.cpp"/>
+    <ClCompile Include="src\PrintHandler.cpp"/>
     <ClCompile Include="src\Query.cpp"/>
     <ClCompile Include="src\Stringifier.cpp"/>
     <ClCompile Include="src\Template.cpp"/>
@@ -292,6 +293,8 @@
     <ClInclude Include="include\Poco\JSON\JSONException.h"/>
     <ClInclude Include="include\Poco\JSON\Object.h"/>
     <ClInclude Include="include\Poco\JSON\Parser.h"/>
+    <ClInclude Include="include\Poco\JSON\ParseHandler.h"/>
+    <ClInclude Include="include\Poco\JSON\PrintHandler.h"/>
     <ClInclude Include="include\Poco\JSON\Query.h"/>
     <ClInclude Include="include\Poco\JSON\Stringifier.h"/>
     <ClInclude Include="include\Poco\JSON\Template.h"/>

+ 10 - 1
JSON/JSON_vs110.vcxproj.filters

@@ -12,7 +12,7 @@
     <ClCompile Include="src\Array.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="src\DefaultHandler.cpp">
+    <ClCompile Include="src\ParseHandler.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="src\Handler.cpp">
@@ -27,6 +27,9 @@
     <ClCompile Include="src\Parser.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\PrintHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="src\Query.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -62,6 +65,12 @@
     <ClInclude Include="include\Poco\JSON\Parser.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\Poco\JSON\ParseHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="include\Poco\JSON\PrintHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="include\Poco\JSON\Query.h">
       <Filter>Header Files</Filter>
     </ClInclude>

+ 7 - 1
JSON/JSON_vs71.vcproj

@@ -365,7 +365,7 @@
 			<File
 				RelativePath=".\src\Array.cpp"/>
 			<File
-				RelativePath=".\src\DefaultHandler.cpp"/>
+				RelativePath=".\src\ParseHandler.cpp"/>
 			<File
 				RelativePath=".\src\Handler.cpp"/>
 			<File
@@ -374,6 +374,8 @@
 				RelativePath=".\src\Object.cpp"/>
 			<File
 				RelativePath=".\src\Parser.cpp"/>
+			<File
+				RelativePath=".\src\PrintHandler.cpp"/>
 			<File
 				RelativePath=".\src\Query.cpp"/>
 			<File
@@ -399,6 +401,10 @@
 				RelativePath=".\include\Poco\JSON\Object.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Parser.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\ParseHandler.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\PrintHandler.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Query.h"/>
 			<File

+ 7 - 1
JSON/JSON_vs80.vcproj

@@ -386,7 +386,7 @@
 			<File
 				RelativePath=".\src\Array.cpp"/>
 			<File
-				RelativePath=".\src\DefaultHandler.cpp"/>
+				RelativePath=".\src\ParseHandler.cpp"/>
 			<File
 				RelativePath=".\src\Handler.cpp"/>
 			<File
@@ -395,6 +395,8 @@
 				RelativePath=".\src\Object.cpp"/>
 			<File
 				RelativePath=".\src\Parser.cpp"/>
+			<File
+				RelativePath=".\src\PrintHandler.cpp"/>
 			<File
 				RelativePath=".\src\Query.cpp"/>
 			<File
@@ -420,6 +422,10 @@
 				RelativePath=".\include\Poco\JSON\Object.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Parser.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\ParseHandler.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\PrintHandler.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Query.h"/>
 			<File

+ 7 - 1
JSON/JSON_vs90.vcproj

@@ -385,7 +385,7 @@
 			<File
 				RelativePath=".\src\Array.cpp"/>
 			<File
-				RelativePath=".\src\DefaultHandler.cpp"/>
+				RelativePath=".\src\ParseHandler.cpp"/>
 			<File
 				RelativePath=".\src\Handler.cpp"/>
 			<File
@@ -394,6 +394,8 @@
 				RelativePath=".\src\Object.cpp"/>
 			<File
 				RelativePath=".\src\Parser.cpp"/>
+			<File
+				RelativePath=".\src\PrintHandler.cpp"/>
 			<File
 				RelativePath=".\src\Query.cpp"/>
 			<File
@@ -419,6 +421,10 @@
 				RelativePath=".\include\Poco\JSON\Object.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Parser.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\ParseHandler.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\PrintHandler.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Query.h"/>
 			<File

+ 4 - 1
JSON/JSON_x64_vs100.vcxproj

@@ -272,11 +272,12 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="src\Array.cpp"/>
-    <ClCompile Include="src\DefaultHandler.cpp"/>
+    <ClCompile Include="src\ParseHandler.cpp"/>
     <ClCompile Include="src\Handler.cpp"/>
     <ClCompile Include="src\JSONException.cpp"/>
     <ClCompile Include="src\Object.cpp"/>
     <ClCompile Include="src\Parser.cpp"/>
+    <ClCompile Include="src\PrintHandler.cpp"/>
     <ClCompile Include="src\Query.cpp"/>
     <ClCompile Include="src\Stringifier.cpp"/>
     <ClCompile Include="src\Template.cpp"/>
@@ -290,6 +291,8 @@
     <ClInclude Include="include\Poco\JSON\JSONException.h"/>
     <ClInclude Include="include\Poco\JSON\Object.h"/>
     <ClInclude Include="include\Poco\JSON\Parser.h"/>
+    <ClInclude Include="include\Poco\JSON\ParseHandler.h"/>
+    <ClInclude Include="include\Poco\JSON\PrintHandler.h"/>
     <ClInclude Include="include\Poco\JSON\Query.h"/>
     <ClInclude Include="include\Poco\JSON\Stringifier.h"/>
     <ClInclude Include="include\Poco\JSON\Template.h"/>

+ 10 - 1
JSON/JSON_x64_vs100.vcxproj.filters

@@ -12,7 +12,7 @@
     <ClCompile Include="src\Array.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="src\DefaultHandler.cpp">
+    <ClCompile Include="src\ParseHandler.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="src\Handler.cpp">
@@ -27,6 +27,9 @@
     <ClCompile Include="src\Parser.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\PrintHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="src\Query.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -62,6 +65,12 @@
     <ClInclude Include="include\Poco\JSON\Parser.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\Poco\JSON\ParseHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="include\Poco\JSON\PrintHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="include\Poco\JSON\Query.h">
       <Filter>Header Files</Filter>
     </ClInclude>

+ 4 - 1
JSON/JSON_x64_vs110.vcxproj

@@ -272,11 +272,12 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="src\Array.cpp"/>
-    <ClCompile Include="src\DefaultHandler.cpp"/>
+    <ClCompile Include="src\ParseHandler.cpp"/>
     <ClCompile Include="src\Handler.cpp"/>
     <ClCompile Include="src\JSONException.cpp"/>
     <ClCompile Include="src\Object.cpp"/>
     <ClCompile Include="src\Parser.cpp"/>
+    <ClCompile Include="src\PrintHandler.cpp"/>
     <ClCompile Include="src\Query.cpp"/>
     <ClCompile Include="src\Stringifier.cpp"/>
     <ClCompile Include="src\Template.cpp"/>
@@ -290,6 +291,8 @@
     <ClInclude Include="include\Poco\JSON\JSONException.h"/>
     <ClInclude Include="include\Poco\JSON\Object.h"/>
     <ClInclude Include="include\Poco\JSON\Parser.h"/>
+    <ClInclude Include="include\Poco\JSON\ParseHandler.h"/>
+    <ClInclude Include="include\Poco\JSON\PrintHandler.h"/>
     <ClInclude Include="include\Poco\JSON\Query.h"/>
     <ClInclude Include="include\Poco\JSON\Stringifier.h"/>
     <ClInclude Include="include\Poco\JSON\Template.h"/>

+ 10 - 1
JSON/JSON_x64_vs110.vcxproj.filters

@@ -12,7 +12,7 @@
     <ClCompile Include="src\Array.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="src\DefaultHandler.cpp">
+    <ClCompile Include="src\ParseHandler.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="src\Handler.cpp">
@@ -27,6 +27,9 @@
     <ClCompile Include="src\Parser.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\PrintHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="src\Query.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -62,6 +65,12 @@
     <ClInclude Include="include\Poco\JSON\Parser.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\Poco\JSON\ParseHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="include\Poco\JSON\PrintHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="include\Poco\JSON\Query.h">
       <Filter>Header Files</Filter>
     </ClInclude>

+ 7 - 1
JSON/JSON_x64_vs90.vcproj

@@ -390,7 +390,7 @@
 			<File
 				RelativePath=".\src\Array.cpp"/>
 			<File
-				RelativePath=".\src\DefaultHandler.cpp"/>
+				RelativePath=".\src\ParseHandler.cpp"/>
 			<File
 				RelativePath=".\src\Handler.cpp"/>
 			<File
@@ -399,6 +399,8 @@
 				RelativePath=".\src\Object.cpp"/>
 			<File
 				RelativePath=".\src\Parser.cpp"/>
+			<File
+				RelativePath=".\src\PrintHandler.cpp"/>
 			<File
 				RelativePath=".\src\Query.cpp"/>
 			<File
@@ -424,6 +426,10 @@
 				RelativePath=".\include\Poco\JSON\Object.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Parser.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\ParseHandler.h"/>
+			<File
+				RelativePath=".\include\Poco\JSON\PrintHandler.h"/>
 			<File
 				RelativePath=".\include\Poco\JSON\Query.h"/>
 			<File

+ 1 - 1
JSON/Makefile

@@ -11,7 +11,7 @@ include $(POCO_BASE)/build/rules/global
 INCLUDE += -I $(POCO_BASE)/JSON/include/Poco/JSON
 
 objects = Array Object Parser Handler  \
-	Stringifier DefaultHandler Query JSONException \
+	Stringifier ParseHandler Query JSONException \
 	Template TemplateCache
 
 target         = PocoJSON

+ 3 - 3
JSON/include/Poco/JSON/Array.h

@@ -137,9 +137,9 @@ public:
 	void add(const Dynamic::Var& value);
 		/// Add the given value to the array
 
-	void stringify(std::ostream& out, unsigned int indent) const;
-		/// Prints the array to out. When indent is 0, the array
-		/// will be printed on one line without indentation.
+	void stringify(std::ostream& out, unsigned int indent = 0, int step = -1) const;
+		/// Prints the array to out. When indent has zero value,
+		/// the array will be printed without newline breaks and spaces between elements.
 
 	void remove(unsigned int index);
 		/// Removes the element on the given index.

+ 3 - 125
JSON/include/Poco/JSON/DefaultHandler.h

@@ -40,137 +40,15 @@
 #define JSON_DefaultHandler_INCLUDED
 
 
-#include "Poco/JSON/Handler.h"
-#include <stack>
+#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<Dynamic::Var> _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

+ 8 - 2
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();

+ 65 - 12
JSON/include/Poco/JSON/Object.h

@@ -46,6 +46,7 @@
 #include "Poco/Dynamic/Var.h"
 #include <map>
 #include <vector>
+#include <deque>
 #include <iostream>
 #include <sstream>
 
@@ -60,8 +61,10 @@ class JSON_API Object
 public:
 	typedef SharedPtr<Object> 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 <typename C>
+	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<std::string, Dynamic::Var> ValueMap;
-	ValueMap _values;
+	typedef std::deque<Dynamic::Var*> 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<std::size_t>(_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
 
 

+ 181 - 0
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 <stack>
+
+
+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<Dynamic::Var> 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

+ 139 - 0
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

+ 28 - 1
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
 
 

+ 2 - 2
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;

+ 12 - 3
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 << "]";
 }
 

+ 10 - 0
JSON/src/Handler.cpp

@@ -42,9 +42,19 @@ namespace Poco {
 namespace JSON {
 
 
+Handler::Handler()
+{
+}
+
+
 Handler::~Handler()
 {
 }
 
 
+void Handler::comma()
+{
+}
+
+
 } } // namespace Poco::JSON

+ 33 - 17
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<std::string>& 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();)
-	{
-		for(int i = 0; i < indent; i++) out << ' ';
 
-		out << '"' << it->first << '"';
-		out << (( indent > 0 ) ? " : " : ":");
+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)
+	{
+		if (it->second == **iter) return it->first;
+	}
 
-		Stringifier::stringify(it->second, out, indent);
+	throw NotFoundException((*iter)->convert<std::string>());
+}
 
-		if ( ++it != _values.end() ) out << ',';
 
-		if ( indent > 0 ) out << std::endl;
+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 << '}';
 }
 
 

+ 13 - 12
JSON/src/DefaultHandler.cpp → 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();
 

+ 49 - 45
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<int>::max()
-                    || value < std::numeric_limits<int>::min() )
-                {
-                    _handler->value(value);
-                }
-                else
-                {
-                    _handler->value(static_cast<int>(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<unsigned>::max() )
-                {
-                    _handler->value(value);
-                }
-                else
-                {
-                    _handler->value(static_cast<unsigned>(value));
-                }
-            }
+			try
+			{
+				Int64 value = token->asInteger64();
+				// if number is 32-bit, then handle as such
+				if (	value > std::numeric_limits<int>::max()
+					|| value < std::numeric_limits<int>::min() )
+				{
+					_handler->value(value);
+				}
+				else
+				{
+					_handler->value(static_cast<int>(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<unsigned>::max() )
+				{
+					_handler->value(value);
+				}
+				else
+				{
+					_handler->value(static_cast<unsigned>(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()));
 	}

+ 194 - 0
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

+ 53 - 46
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<Object>();
-		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<Array>();
-		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<Object::Ptr>();
-		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<Array::Ptr>();
-		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<std::string>();
-		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<int>(*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<int>(*it);
+			}
+			else
+			{
+				out << *it;
+			}
+			break;
+		}
+		}
+	}
+	out << '"';
+}
+
 } }  // Namespace Poco::JSON

+ 345 - 34
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<Array::Ptr>();
+	Object::Ptr object = arr->getObject(0);
+	assert (object->getValue<int>("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,206 @@ 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
+	{
+		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(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);
+	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
 	{
-		DefaultHandler handler;
+		ParseHandler handler(true);
 		parser.setHandler(&handler);
 		parser.parse(json);
 		result = handler.result();
@@ -822,9 +1045,92 @@ void JSONTest::testStringify()
 
 	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);
-	//TODO: need map that does not order for internal container
-	assert (ostr.str() == "{\"name\":\"Franky\",\"children\":[\"Jonas\",\"Ellen\"]}");
+	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<std::string>() == 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);

+ 3 - 0
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();