diff --git a/Data/Data_VS90.vcproj b/Data/Data_VS90.vcproj
index 4db2814e5..bdd6f6e7b 100644
--- a/Data/Data_VS90.vcproj
+++ b/Data/Data_VS90.vcproj
@@ -583,6 +583,10 @@
RelativePath=".\include\Poco\Data\Extraction.h"
>
+
+
@@ -723,6 +727,10 @@
RelativePath=".\src\DynamicLOB.cpp"
>
+
+
diff --git a/Data/Data_vs160.vcxproj b/Data/Data_vs160.vcxproj
index b1e83bf28..829557603 100644
--- a/Data/Data_vs160.vcxproj
+++ b/Data/Data_vs160.vcxproj
@@ -569,6 +569,7 @@
+
@@ -635,6 +636,9 @@
true
+
+ true
+
true
diff --git a/Data/Data_vs160.vcxproj.filters b/Data/Data_vs160.vcxproj.filters
index b9ddffbeb..eed50b05b 100644
--- a/Data/Data_vs160.vcxproj.filters
+++ b/Data/Data_vs160.vcxproj.filters
@@ -2,31 +2,31 @@
- {d0ab5265-2864-49b7-bd0f-8ac8ec3310ab}
+ {ca802690-e052-4003-ae74-b2cd7e2c95f9}
- {816b5750-54e8-4eea-9b7e-932128a4910b}
+ {c9a3889d-4e2d-43ac-887e-3b22b1cf4ba9}
- {f2c0b14b-1f5c-44a6-9c96-d8a1dddba2f9}
+ {b6b0997e-c4d7-4526-88c6-614fba4407cf}
- {b1da33d1-7edf-4c90-a82b-65bc3c051030}
+ {ca986e9c-3287-419e-8f6a-7472a96c71b9}
- {b8f868fc-fc3a-451f-b8f2-b533c7b119b8}
+ {33a16a37-8706-47fb-ac35-f3e913ff6a03}
- {8fa22f8e-34b1-415c-81c1-e8dad60db327}
+ {934c7f71-50fb-452a-94a6-c123cdaa043e}
- {7bcc1fd5-c9f1-4063-9948-efa1cdb00e46}
+ {e6946b3f-5400-4275-a315-25a8db846def}
- {d01e6c3f-45e1-4b0e-9fa5-690bfd21fe6d}
+ {6fd48faa-50da-4192-bf66-27068d6b2156}
- {f242ef0d-23d3-4b7b-a8ac-dbd1826bf531}
+ {71eaafa2-e2fe-4cb8-80e2-82ea694abeec}
@@ -93,6 +93,9 @@
DataCore\Header Files
+
+ DataCore\Header Files
+
DataCore\Header Files
@@ -215,6 +218,9 @@
DataCore\Source Files
+
+ DataCore\Source Files
+
DataCore\Source Files
diff --git a/Data/Makefile b/Data/Makefile
index b863b9671..ea354f6c2 100644
--- a/Data/Makefile
+++ b/Data/Makefile
@@ -8,8 +8,8 @@ include $(POCO_BASE)/build/rules/global
objects = AbstractBinder AbstractBinding AbstractExtraction AbstractExtractor \
AbstractPreparation AbstractPreparator ArchiveStrategy Transaction \
- Bulk Connector DataException Date DynamicLOB Limit MetaColumn \
- PooledSessionHolder PooledSessionImpl Position \
+ Bulk Connector DataException Date DynamicLOB JSONRowFormatter \
+ Limit MetaColumn PooledSessionHolder PooledSessionImpl Position \
Range RecordSet Row RowFilter RowFormatter RowIterator \
SimpleRowFormatter Session SessionFactory SessionImpl \
SessionPool SessionPoolContainer SQLChannel \
diff --git a/Data/include/Poco/Data/JSONRowFormatter.h b/Data/include/Poco/Data/JSONRowFormatter.h
new file mode 100644
index 000000000..8308e9ab4
--- /dev/null
+++ b/Data/include/Poco/Data/JSONRowFormatter.h
@@ -0,0 +1,159 @@
+//
+// JSONRowFormatter.h
+//
+// Library: Data
+// Package: DataCore
+// Module: JSONRowFormatter
+//
+// Definition of the JSONRowFormatter class.
+//
+// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
+// and Contributors.
+//
+// SPDX-License-Identifier: BSL-1.0
+//
+
+
+#ifndef Data_JSONRowFormatter_INCLUDED
+#define Data_JSONRowFormatter_INCLUDED
+
+
+#include "Poco/Data/RowFormatter.h"
+
+
+namespace Poco {
+namespace Data {
+
+
+class Data_API JSONRowFormatter: public Poco::Data::RowFormatter
+ /// Class for JSON formatting of data rows.
+ ///
+ /// Formatter can be configured to operate in four modes (and
+ /// certain combinations thereof) :
+ ///
+ /// - small (condensed mode, only array of values)
+ ///
+ /// Example:
+ /// {
+ /// [["Simpson", "Bart", "Springfield", 12],
+ /// ["Simpson", "Lisa", "Springfield", 10]]
+ /// }
+ ///
+ /// - row count (total row count provided)
+ ///
+ /// Example:
+ /// {
+ /// "count":2,
+ /// [["Simpson", "Bart", "Springfield", 12],
+ /// ["Simpson", "Lisa", "Springfield", 10]]
+ /// }
+ ///
+ /// - column names (column names provided as a string array)
+ ///
+ /// Example:
+ /// {
+ /// "names":["LastName", "FirstName", "Address", "Age"],
+ /// [["Simpson", "Bart", "Springfield", 12],
+ /// ["Simpson", "Lisa", "Springfield", 10]]
+ /// }
+ ///
+ /// - full (total row count, column names provided in every row of data)
+ ///
+ /// Example:
+ /// {
+ /// "count":2,
+ /// [
+ /// {"LastName": "Simpson", "FirstName": "Bart", "Address": "Springfield", "Age": 12},
+ /// {"LastName": "Simpson", "FirstName": "Lisa", "Address": "Springfield", "Age": 10}
+ /// ]
+ /// }
+ ///
+ /// Total row count will be specified by the Poco::SQLRecordSet. Note, however, that this is
+ /// not possible to do accurately in case of result set paging. For those cases, there is
+ /// setTotalRowCount() member function, which allows to explicitly set the total row count.
+ /// If the total row count is preset on the formatter, the Data framework shall not interfere.
+{
+public:
+ static const int JSON_FMT_MODE_SMALL = 1;
+ static const int JSON_FMT_MODE_ROW_COUNT = 2;
+ static const int JSON_FMT_MODE_COLUMN_NAMES = 4;
+ static const int JSON_FMT_MODE_FULL = 8;
+
+ JSONRowFormatter(int mode = (JSON_FMT_MODE_COLUMN_NAMES | JSON_FMT_MODE_SMALL));
+ /// Creates a new JSONRowFormatter.
+
+ ~JSONRowFormatter();
+ /// Destroys the JSONRowFormatter.
+
+ std::string& formatNames(const NameVecPtr pNames, std::string& formattedNames);
+ /// Formats names.
+
+ std::string& formatValues(const ValueVec& vals, std::string& formattedValues);
+ // Formats values.
+
+ void setJSONMode(int mode);
+ /// Sets the mode. Valid mode values are:
+ /// JSON_FMT_MODE_SMALL
+ /// JSON_FMT_MODE_ROW_COUNT
+ /// JSON_FMT_MODE_COLUMN_NAMES
+ /// JSON_FMT_MODE_FULL
+
+ bool printRowCount() const;
+ /// Returns true if row count printing is enabled,
+ /// false otherwise.
+
+ bool printColumnNames() const;
+ /// Returns true if column names printing is enabled,
+ /// false otherwise.
+
+ bool isSmall() const;
+ /// Returns true if compact mode formatting is enabled,
+ /// false otherwise.
+
+ bool isFull() const;
+ /// Returns true if full mode formatting is enabled,
+ /// false otherwise.
+
+
+private:
+ void adjustPrefix() const;
+
+ NameVecPtr _pNames;
+ int _mode;
+ bool _firstTime;
+};
+
+
+//
+// inlines
+//
+
+
+inline bool JSONRowFormatter::printRowCount() const
+{
+ return (_mode & JSON_FMT_MODE_ROW_COUNT) != 0;
+}
+
+
+inline bool JSONRowFormatter::printColumnNames() const
+{
+ return (_mode & JSON_FMT_MODE_COLUMN_NAMES) != 0;
+}
+
+
+inline bool JSONRowFormatter::isSmall() const
+{
+ return (_mode & JSON_FMT_MODE_SMALL) != 0;
+}
+
+
+inline bool JSONRowFormatter::isFull() const
+{
+ return (_mode & JSON_FMT_MODE_FULL) != 0;
+}
+
+
+} } // namespace Poco::Data
+
+
+#endif // Data_JSONRowFormatter_INCLUDED
diff --git a/Data/include/Poco/Data/RowFormatter.h b/Data/include/Poco/Data/RowFormatter.h
index a56fe6646..7c735a18d 100644
--- a/Data/include/Poco/Data/RowFormatter.h
+++ b/Data/include/Poco/Data/RowFormatter.h
@@ -139,7 +139,12 @@ public:
protected:
- void setPrefix(const std::string& prefix);
+ virtual void adjustPrefix() const;
+ /// Adjusts the prefix, if needed
+ /// (eg. to contain the total row count);
+ /// default no-op.
+
+ void setPrefix(const std::string& prefix) const;
/// Sets the prefix for the formatter.
void setPostfix(const std::string& postfix);
@@ -175,7 +180,7 @@ inline void RowFormatter::setTotalRowCount(int count)
}
-inline void RowFormatter::setPrefix(const std::string& prefix)
+inline void RowFormatter::setPrefix(const std::string& prefix) const
{
_prefix = prefix;
}
@@ -189,6 +194,7 @@ inline void RowFormatter::setPostfix(const std::string& postfix)
inline const std::string& RowFormatter::prefix() const
{
+ adjustPrefix();
return _prefix;
}
diff --git a/Data/samples/RowFormatter/src/RowFormatter.cpp b/Data/samples/RowFormatter/src/RowFormatter.cpp
index 122dbd361..4c928f940 100644
--- a/Data/samples/RowFormatter/src/RowFormatter.cpp
+++ b/Data/samples/RowFormatter/src/RowFormatter.cpp
@@ -17,6 +17,7 @@
#include "Poco/Data/Statement.h"
#include "Poco/Data/RecordSet.h"
#include "Poco/Data/RowFormatter.h"
+#include "Poco/Data/JSONRowFormatter.h"
#include "Poco/Data/SQLite/Connector.h"
#include
@@ -27,6 +28,7 @@ using Poco::Data::Session;
using Poco::Data::Statement;
using Poco::Data::RecordSet;
using Poco::Data::RowFormatter;
+using Poco::Data::JSONRowFormatter;
class HTMLTableFormatter : public RowFormatter
@@ -117,5 +119,11 @@ int main(int argc, char** argv)
std::cout << std::endl << "Simple formatting:" << std::endl << std::endl;
std::cout << RecordSet(session, "SELECT * FROM Simpsons");
+ // JSON formatting example (uses the JSONRowFormatter provided by framework)
+ std::cout << std::endl << "JSON formatting:" << std::endl << std::endl;
+ JSONRowFormatter jsonRowFormatter;
+ jsonRowFormatter.setJSONMode((RowFormatter::Mode)(JSONRowFormatter::JSON_FMT_MODE_ROW_COUNT | JSONRowFormatter::JSON_FMT_MODE_COLUMN_NAMES));
+ std::cout << RecordSet(session, "SELECT * FROM Simpsons", jsonRowFormatter);
+
return 0;
}
diff --git a/Data/src/JSONRowFormatter.cpp b/Data/src/JSONRowFormatter.cpp
new file mode 100644
index 000000000..3d5dc4fe2
--- /dev/null
+++ b/Data/src/JSONRowFormatter.cpp
@@ -0,0 +1,189 @@
+//
+// JSONRowFormatter.cpp
+//
+// Library: Data
+// Package: DataCore
+// Module: JSONRowFormatter
+//
+// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
+// and Contributors.
+//
+// SPDX-License-Identifier: BSL-1.0
+//
+
+
+#include "Poco/Data/JSONRowFormatter.h"
+#include "Poco/String.h"
+#include "Poco/JSONString.h"
+#include "Poco/Format.h"
+
+
+using Poco::trimInPlace;
+using Poco::format;
+using Poco::toJSON;
+
+
+namespace Poco {
+namespace Data {
+
+
+const int JSONRowFormatter::JSON_FMT_MODE_SMALL;
+const int JSONRowFormatter::JSON_FMT_MODE_ROW_COUNT;
+const int JSONRowFormatter::JSON_FMT_MODE_COLUMN_NAMES;
+const int JSONRowFormatter::JSON_FMT_MODE_FULL;
+
+
+JSONRowFormatter::JSONRowFormatter(int mode) : RowFormatter("{", "]}"),
+ _firstTime(true)
+{
+ if (mode == JSON_FMT_MODE_FULL)
+ {
+ mode |= JSON_FMT_MODE_ROW_COUNT;
+ mode |= JSON_FMT_MODE_COLUMN_NAMES;
+ }
+
+ setJSONMode(mode);
+}
+
+
+JSONRowFormatter::~JSONRowFormatter()
+{
+}
+
+
+void JSONRowFormatter::adjustPrefix() const
+{
+ if (printRowCount())
+ {
+ std::ostringstream ostr;
+ ostr << "{\"count\":" << getTotalRowCount() << ",";
+ if (_mode & JSON_FMT_MODE_FULL)
+ ostr << '[';
+ setPrefix(ostr.str());
+ }
+}
+
+
+void JSONRowFormatter::setJSONMode(int mode)
+{
+ if (mode < JSON_FMT_MODE_SMALL ||
+ mode > (JSON_FMT_MODE_SMALL | JSON_FMT_MODE_ROW_COUNT | JSON_FMT_MODE_COLUMN_NAMES | JSON_FMT_MODE_FULL))
+ {
+ throw Poco::InvalidArgumentException(
+ Poco::format("JSONRowFormatter mode must be between "
+ "%d (JSON_FMT_MODE_SMALL) and %d (JSON_FMT_MODE_FULL)",
+ JSON_FMT_MODE_SMALL,
+ JSON_FMT_MODE_FULL));
+ }
+
+ _mode = mode;
+ if (!(_mode & JSON_FMT_MODE_SMALL) && !(_mode & JSON_FMT_MODE_FULL))
+ _mode |= JSON_FMT_MODE_SMALL;
+ else if (_mode & JSON_FMT_MODE_FULL)
+ {
+ _mode |= JSON_FMT_MODE_ROW_COUNT;
+ }
+}
+
+
+std::string& JSONRowFormatter::formatValues(const ValueVec& vals, std::string& formattedValues)
+{
+ std::ostringstream str;
+ if (!_firstTime) str << ',';
+ if (isSmall())
+ {
+ if (_firstTime)
+ {
+ if (printColumnNames())
+ str << ",\"values\":";
+
+ str << '[';
+ }
+
+ str << '[';
+ ValueVec::const_iterator it = vals.begin();
+ ValueVec::const_iterator end = vals.end();
+ for (; it != end;)
+ {
+ if (!it->isEmpty())
+ {
+ if (it->isString() || it->isDate() || it->isTime())
+ {
+ std::string val = it->convert();
+ trimInPlace(val);
+ str << toJSON(val);
+ }
+ else
+ str << it->convert();
+ }
+ else
+ str << "null";
+
+ if (++it == end) break;
+
+ str << ',';
+ }
+ str << ']';
+ }
+ else if (isFull())
+ {
+ str << '{';
+ ValueVec::const_iterator it = vals.begin();
+ ValueVec::const_iterator end = vals.end();
+ NameVec::iterator nIt = _pNames->begin();
+ NameVec::iterator nEnd = _pNames->end();
+ for (; it != end && nIt != nEnd; ++nIt)
+ {
+ if (!it->isEmpty())
+ {
+ if (it->isString() || it->isDate() || it->isTime())
+ {
+ std::string val = it->convert();
+ trimInPlace(val);
+ str << '"' << *nIt << "\":" << toJSON(val);
+ }
+ else
+ str << '"' << *nIt << "\":" << it->convert();
+ }
+ else
+ str << '"' << *nIt << "\":null";
+
+ if (++it != end) str << ',';
+ }
+ str << '}';
+ }
+
+ _firstTime = false;
+ return formattedValues = str.str();
+}
+
+
+std::string& JSONRowFormatter::formatNames(const NameVecPtr pNames, std::string& formattedNames)
+{
+ //adjustPrefix();
+ if (isFull())
+ {
+ // names are used in formatValues
+ if (pNames && !_pNames) _pNames = pNames;
+ return formattedNames = "";
+ }
+ else if (printColumnNames())
+ {
+ std::ostringstream ostr;
+ ostr << "\"names\":[";
+ for (NameVec::const_iterator it = pNames->begin(),
+ end = pNames->end();;)
+ {
+ ostr << '"' << *it << '"';
+ if (++it == end) break;
+ ostr << ',';
+ }
+ ostr << "]";
+ return formattedNames = ostr.str();
+ }
+
+ return formattedNames = "";
+}
+
+
+} }// namespace Poco::Data
diff --git a/Data/src/RowFormatter.cpp b/Data/src/RowFormatter.cpp
index 071c80f6f..3173fd84e 100644
--- a/Data/src/RowFormatter.cpp
+++ b/Data/src/RowFormatter.cpp
@@ -77,4 +77,10 @@ void RowFormatter::reset()
}
+void RowFormatter::adjustPrefix() const
+{
+ return;
+}
+
+
} } // namespace Poco::Data
diff --git a/Data/testsuite/src/DataTest.cpp b/Data/testsuite/src/DataTest.cpp
index afda8fb78..7c9159655 100644
--- a/Data/testsuite/src/DataTest.cpp
+++ b/Data/testsuite/src/DataTest.cpp
@@ -21,6 +21,7 @@
#include "Poco/Data/Date.h"
#include "Poco/Data/Time.h"
#include "Poco/Data/SimpleRowFormatter.h"
+#include "Poco/Data/JSONRowFormatter.h"
#include "Poco/Data/DataException.h"
#include "Connector.h"
#include "Poco/BinaryReader.h"
@@ -65,7 +66,9 @@ using Poco::Data::CLOBOutputStream;
using Poco::Data::MetaColumn;
using Poco::Data::Column;
using Poco::Data::Row;
+using Poco::Data::RowFormatter;
using Poco::Data::SimpleRowFormatter;
+using Poco::Data::JSONRowFormatter;
using Poco::Data::Date;
using Poco::Data::Time;
using Poco::Data::AbstractExtractor;
@@ -1156,7 +1159,7 @@ void DataTest::testRowStrictWeak(const Row& row1, const Row& row2, const Row& ro
}
-void DataTest::testRowFormat()
+void DataTest::testSimpleRowFormatter()
{
Row row1;
row1.append("field0", 0);
@@ -1200,6 +1203,38 @@ void DataTest::testRowFormat()
}
+void DataTest::testJSONRowFormatter()
+{
+ Row row1;
+ row1.append("field0", 0);
+ row1.append("field1", "1");
+ row1.append("field2", DateTime(2007, 3, 13, 8, 12, 15));
+ row1.append("field3", Var());
+ row1.append("field4", 4);
+ row1.setFormatter(new JSONRowFormatter);
+
+ assertTrue(row1.getFormatter().prefix() == "{");
+ assertTrue(row1.getFormatter().postfix() == "]}");
+ assertTrue(row1.getFormatter().getMode() == RowFormatter::FORMAT_PROGRESSIVE);
+ assertTrue(row1.namesToString() == "\"names\":[\"field0\",\"field1\",\"field2\",\"field3\",\"field4\"]");
+ assertTrue(row1.valuesToString() == ",\"values\":[[0,\"1\",\"2007-03-13T08:12:15Z\",null,4]");
+
+ row1.setFormatter(new JSONRowFormatter(JSONRowFormatter::JSON_FMT_MODE_SMALL));
+ assertTrue(row1.getFormatter().getMode() == RowFormatter::FORMAT_PROGRESSIVE);
+ assertTrue(row1.namesToString() == "");
+ assertTrue(row1.valuesToString() == "[[0,\"1\",\"2007-03-13T08:12:15Z\",null,4]");
+ assertTrue(row1.valuesToString() == ",[0,\"1\",\"2007-03-13T08:12:15Z\",null,4]");
+
+ row1.setFormatter(new JSONRowFormatter(JSONRowFormatter::JSON_FMT_MODE_FULL));
+ assertTrue(row1.getFormatter().prefix() == "{\"count\":0,[");
+ assertTrue(row1.getFormatter().postfix() == "]}");
+ assertTrue(row1.getFormatter().getMode() == RowFormatter::FORMAT_PROGRESSIVE);
+ assertTrue(row1.namesToString() == "");
+ assertTrue(row1.valuesToString() == "{\"field0\":0,\"field1\":\"1\",\"field2\":\"2007-03-13T08:12:15Z\",\"field3\":null,\"field4\":4}");
+ assertTrue(row1.valuesToString() == ",{\"field0\":0,\"field1\":\"1\",\"field2\":\"2007-03-13T08:12:15Z\",\"field3\":null,\"field4\":4}");
+}
+
+
void DataTest::testDateAndTime()
{
DateTime dt;
@@ -1415,7 +1450,8 @@ CppUnit::Test* DataTest::suite()
CppUnit_addTest(pSuite, DataTest, testColumnList);
CppUnit_addTest(pSuite, DataTest, testRow);
CppUnit_addTest(pSuite, DataTest, testRowSort);
- CppUnit_addTest(pSuite, DataTest, testRowFormat);
+ CppUnit_addTest(pSuite, DataTest, testSimpleRowFormatter);
+ CppUnit_addTest(pSuite, DataTest, testJSONRowFormatter);
CppUnit_addTest(pSuite, DataTest, testDateAndTime);
CppUnit_addTest(pSuite, DataTest, testExternalBindingAndExtraction);
CppUnit_addTest(pSuite, DataTest, testTranscode);
diff --git a/Data/testsuite/src/DataTest.h b/Data/testsuite/src/DataTest.h
index 6b5015616..ae3241166 100644
--- a/Data/testsuite/src/DataTest.h
+++ b/Data/testsuite/src/DataTest.h
@@ -40,7 +40,8 @@ public:
void testColumnList();
void testRow();
void testRowSort();
- void testRowFormat();
+ void testSimpleRowFormatter();;
+ void testJSONRowFormatter();
void testDateAndTime();
void testExternalBindingAndExtraction();
void testTranscode();