Preserve entries order in DynamicStruct #2410 (#2413)

* Preserve entries order in DynamicStruct #2410

* disable C++11 default

* ifdef C++11 code
This commit is contained in:
Aleksandar Fabijanic 2018-08-01 08:06:59 -07:00 committed by GitHub
parent 6107b43a7b
commit 231ef2762d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 825 additions and 73 deletions

View File

@ -22,6 +22,8 @@
#include "Poco/Dynamic/Var.h" #include "Poco/Dynamic/Var.h"
#include "Poco/Dynamic/VarHolder.h" #include "Poco/Dynamic/VarHolder.h"
#include "Poco/SharedPtr.h" #include "Poco/SharedPtr.h"
#include "Poco/OrderedMap.h"
#include "Poco/OrderedSet.h"
#include <map> #include <map>
#include <set> #include <set>
@ -30,19 +32,19 @@ namespace Poco {
namespace Dynamic { namespace Dynamic {
template <typename K> template <typename K, typename M = std::map<K, Var>, typename S = std::set<K> >
class Struct class Struct
/// Struct allows to define a named collection of Var objects. /// Struct allows to define a named collection of Var objects.
{ {
public: public:
typedef typename std::map<K, Var> Data; typedef M Data;
typedef typename std::set<K> NameSet; typedef S NameSet;
typedef typename Data::iterator Iterator; typedef typename Data::iterator Iterator;
typedef typename Data::const_iterator ConstIterator; typedef typename Data::const_iterator ConstIterator;
typedef typename Struct<K>::Data::value_type ValueType; typedef typename Struct<K>::Data::value_type ValueType;
typedef typename Struct<K>::Data::size_type SizeType; typedef typename Struct<K>::Data::size_type SizeType;
typedef typename std::pair<typename Struct<K>::Iterator, bool> InsRetVal; typedef typename std::pair<typename Struct<K, M, S>::Iterator, bool> InsRetVal;
typedef typename Poco::SharedPtr<Struct<K> > Ptr; typedef typename Poco::SharedPtr<Struct<K, M, S> > Ptr;
Struct(): _data() Struct(): _data()
/// Creates an empty Struct /// Creates an empty Struct
@ -57,13 +59,19 @@ public:
template <typename T> template <typename T>
Struct(const std::map<K, T>& val) Struct(const std::map<K, T>& val)
{ {
typedef typename std::map<K, T>::const_iterator MapConstIterator; assignMap(val);
MapConstIterator it = val.begin();
MapConstIterator end = val.end();
for (; it != end; ++it) _data.insert(ValueType(it->first, Var(it->second)));
} }
#ifdef POCO_ENABLE_CPP11
template <typename T>
Struct(const OrderedMap<K, T>& val)
{
assignMap(val);
}
#endif // POCO_ENABLE_CPP11
virtual ~Struct() virtual ~Struct()
/// Destroys the Struct. /// Destroys the Struct.
{ {
@ -199,15 +207,30 @@ public:
} }
private: private:
template <typename T>
void assignMap(const T& map)
{
typedef typename T::const_iterator MapConstIterator;
MapConstIterator it = map.begin();
MapConstIterator end = map.end();
for (; it != end; ++it) _data.insert(ValueType(it->first, Var(it->second)));
}
Data _data; Data _data;
}; };
template <> template <>
class VarHolderImpl<Struct<std::string> >: public VarHolder class VarHolderImpl<Struct<std::string, std::map<std::string, Var>, std::set<std::string> > >: public VarHolder
{ {
public: public:
VarHolderImpl(const Struct<std::string>& val): _val(val) typedef std::string KeyType;
typedef std::map<KeyType, Var> MapType;
typedef std::set<KeyType> SetType;
typedef Struct<KeyType, MapType, SetType> ValueType;
VarHolderImpl(const ValueType& val): _val(val)
{ {
} }
@ -217,7 +240,7 @@ public:
const std::type_info& type() const const std::type_info& type() const
{ {
return typeid(Struct<std::string>); return typeid(ValueType);
} }
void convert(Int8&) const void convert(Int8&) const
@ -283,8 +306,8 @@ public:
void convert(std::string& val) const void convert(std::string& val) const
{ {
val.append("{ "); val.append("{ ");
Struct<std::string>::ConstIterator it = _val.begin(); ValueType::ConstIterator it = _val.begin();
Struct<std::string>::ConstIterator itEnd = _val.end(); ValueType::ConstIterator itEnd = _val.end();
if (!_val.empty()) if (!_val.empty())
{ {
Var key(it->first); Var key(it->first);
@ -324,7 +347,7 @@ public:
return cloneHolder(pVarHolder, _val); return cloneHolder(pVarHolder, _val);
} }
const Struct<std::string>& value() const const ValueType& value() const
{ {
return _val; return _val;
} }
@ -339,6 +362,11 @@ public:
return true; return true;
} }
bool isOrdered() const
{
return false;
}
bool isInteger() const bool isInteger() const
{ {
return false; return false;
@ -364,36 +392,41 @@ public:
return _val.size(); return _val.size();
} }
Var& operator [] (const std::string& name) Var& operator [] (const KeyType& name)
{ {
return _val[name]; return _val[name];
} }
const Var& operator [] (const std::string& name) const const Var& operator [] (const KeyType& name) const
{ {
return _val[name]; return _val[name];
} }
private: private:
Struct<std::string> _val; ValueType _val;
}; };
template <> template <>
class VarHolderImpl<Struct<int> >: public VarHolder class VarHolderImpl<Struct<int, std::map<int, Var>, std::set<int> > > : public VarHolder
{ {
public: public:
VarHolderImpl(const Struct<int>& val): _val(val) typedef int KeyType;
typedef std::map<KeyType, Var> MapType;
typedef std::set<KeyType> SetType;
typedef Struct<KeyType, MapType, SetType> ValueType;
VarHolderImpl(const ValueType& val) : _val(val)
{ {
} }
~VarHolderImpl() ~VarHolderImpl()
{ {
} }
const std::type_info& type() const const std::type_info& type() const
{ {
return typeid(Struct<int>); return typeid(ValueType);
} }
void convert(Int8&) const void convert(Int8&) const
@ -405,7 +438,7 @@ public:
{ {
throw BadCastException("Cannot cast Struct type to Int16"); throw BadCastException("Cannot cast Struct type to Int16");
} }
void convert(Int32&) const void convert(Int32&) const
{ {
throw BadCastException("Cannot cast Struct type to Int32"); throw BadCastException("Cannot cast Struct type to Int32");
@ -425,7 +458,7 @@ public:
{ {
throw BadCastException("Cannot cast Struct type to UInt16"); throw BadCastException("Cannot cast Struct type to UInt16");
} }
void convert(UInt32&) const void convert(UInt32&) const
{ {
throw BadCastException("Cannot cast Struct type to UInt32"); throw BadCastException("Cannot cast Struct type to UInt32");
@ -459,8 +492,8 @@ public:
void convert(std::string& val) const void convert(std::string& val) const
{ {
val.append("{ "); val.append("{ ");
Struct<int>::ConstIterator it = _val.begin(); ValueType::ConstIterator it = _val.begin();
Struct<int>::ConstIterator itEnd = _val.end(); ValueType::ConstIterator itEnd = _val.end();
if (!_val.empty()) if (!_val.empty())
{ {
Var key(it->first); Var key(it->first);
@ -477,7 +510,7 @@ public:
val.append(" : "); val.append(" : ");
Impl::appendJSONValue(val, it->second); Impl::appendJSONValue(val, it->second);
} }
val.append(" }"); val.append(" }");
} }
void convert(Poco::DateTime&) const void convert(Poco::DateTime&) const
@ -499,8 +532,8 @@ public:
{ {
return cloneHolder(pVarHolder, _val); return cloneHolder(pVarHolder, _val);
} }
const Struct<int>& value() const const ValueType& value() const
{ {
return _val; return _val;
} }
@ -515,6 +548,200 @@ public:
return true; return true;
} }
bool isOrdered() const
{
return false;
}
bool isInteger() const
{
return false;
}
bool isSigned() const
{
return false;
}
bool isNumeric() const
{
return false;
}
bool isString() const
{
return false;
}
std::size_t size() const
{
return _val.size();
}
Var& operator [] (const KeyType& name)
{
return _val[name];
}
const Var& operator [] (const KeyType& name) const
{
return _val[name];
}
private:
ValueType _val;
};
#ifdef POCO_ENABLE_CPP11
template <>
class VarHolderImpl<Struct<std::string, Poco::OrderedMap<std::string, Var>, Poco::OrderedSet<std::string> > > : public VarHolder
{
public:
typedef std::string KeyType;
typedef Poco::OrderedMap<KeyType, Var> MapType;
typedef Poco::OrderedSet<KeyType> SetType;
typedef Struct<KeyType, MapType, SetType> ValueType;
VarHolderImpl(const ValueType& val) : _val(val)
{
}
~VarHolderImpl()
{
}
const std::type_info& type() const
{
return typeid(ValueType);
}
void convert(Int8&) const
{
throw BadCastException("Cannot cast Struct type to Int8");
}
void convert(Int16&) const
{
throw BadCastException("Cannot cast Struct type to Int16");
}
void convert(Int32&) const
{
throw BadCastException("Cannot cast Struct type to Int32");
}
void convert(Int64&) const
{
throw BadCastException("Cannot cast Struct type to Int64");
}
void convert(UInt8&) const
{
throw BadCastException("Cannot cast Struct type to UInt8");
}
void convert(UInt16&) const
{
throw BadCastException("Cannot cast Struct type to UInt16");
}
void convert(UInt32&) const
{
throw BadCastException("Cannot cast Struct type to UInt32");
}
void convert(UInt64&) const
{
throw BadCastException("Cannot cast Struct type to UInt64");
}
void convert(bool&) const
{
throw BadCastException("Cannot cast Struct type to bool");
}
void convert(float&) const
{
throw BadCastException("Cannot cast Struct type to float");
}
void convert(double&) const
{
throw BadCastException("Cannot cast Struct type to double");
}
void convert(char&) const
{
throw BadCastException("Cannot cast Struct type to char");
}
void convert(std::string& val) const
{
val.append("{ ");
ValueType::ConstIterator it = _val.begin();
ValueType::ConstIterator itEnd = _val.end();
if (!_val.empty())
{
Var key(it->first);
Impl::appendJSONKey(val, key);
val.append(" : ");
Impl::appendJSONValue(val, it->second);
++it;
}
for (; it != itEnd; ++it)
{
val.append(", ");
Var key(it->first);
Impl::appendJSONKey(val, key);
val.append(" : ");
Impl::appendJSONValue(val, it->second);
}
val.append(" }");
}
void convert(Poco::DateTime&) const
{
throw BadCastException("Struct -> Poco::DateTime");
}
void convert(Poco::LocalDateTime&) const
{
throw BadCastException("Struct -> Poco::LocalDateTime");
}
void convert(Poco::Timestamp&) const
{
throw BadCastException("Struct -> Poco::Timestamp");
}
VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
{
return cloneHolder(pVarHolder, _val);
}
const ValueType& value() const
{
return _val;
}
bool isArray() const
{
return false;
}
bool isStruct() const
{
return true;
}
bool isOrdered() const
{
return true;
}
bool isInteger() const bool isInteger() const
{ {
return false; return false;
@ -540,26 +767,218 @@ public:
return _val.size(); return _val.size();
} }
Var& operator [] (int name) Var& operator [] (const KeyType& name)
{ {
return _val[name]; return _val[name];
} }
const Var& operator [] (int name) const const Var& operator [] (const KeyType& name) const
{ {
return _val[name]; return _val[name];
} }
private: private:
Struct<int> _val; ValueType _val;
}; };
template <>
class VarHolderImpl<Struct<int, Poco::OrderedMap<int, Var>, Poco::OrderedSet<int> > > : public VarHolder
{
public:
typedef int KeyType;
typedef Poco::OrderedMap<KeyType, Var> MapType;
typedef Poco::OrderedSet<KeyType> SetType;
typedef Struct<KeyType, MapType, SetType> ValueType;
VarHolderImpl(const ValueType& val) : _val(val)
{
}
~VarHolderImpl()
{
}
const std::type_info& type() const
{
return typeid(ValueType);
}
void convert(Int8&) const
{
throw BadCastException("Cannot cast Struct type to Int8");
}
void convert(Int16&) const
{
throw BadCastException("Cannot cast Struct type to Int16");
}
void convert(Int32&) const
{
throw BadCastException("Cannot cast Struct type to Int32");
}
void convert(Int64&) const
{
throw BadCastException("Cannot cast Struct type to Int64");
}
void convert(UInt8&) const
{
throw BadCastException("Cannot cast Struct type to UInt8");
}
void convert(UInt16&) const
{
throw BadCastException("Cannot cast Struct type to UInt16");
}
void convert(UInt32&) const
{
throw BadCastException("Cannot cast Struct type to UInt32");
}
void convert(UInt64&) const
{
throw BadCastException("Cannot cast Struct type to UInt64");
}
void convert(bool&) const
{
throw BadCastException("Cannot cast Struct type to bool");
}
void convert(float&) const
{
throw BadCastException("Cannot cast Struct type to float");
}
void convert(double&) const
{
throw BadCastException("Cannot cast Struct type to double");
}
void convert(char&) const
{
throw BadCastException("Cannot cast Struct type to char");
}
void convert(std::string& val) const
{
val.append("{ ");
ValueType::ConstIterator it = _val.begin();
ValueType::ConstIterator itEnd = _val.end();
if (!_val.empty())
{
Var key(it->first);
Impl::appendJSONKey(val, key);
val.append(" : ");
Impl::appendJSONValue(val, it->second);
++it;
}
for (; it != itEnd; ++it)
{
val.append(", ");
Var key(it->first);
Impl::appendJSONKey(val, key);
val.append(" : ");
Impl::appendJSONValue(val, it->second);
}
val.append(" }");
}
void convert(Poco::DateTime&) const
{
throw BadCastException("Struct -> Poco::DateTime");
}
void convert(Poco::LocalDateTime&) const
{
throw BadCastException("Struct -> Poco::LocalDateTime");
}
void convert(Poco::Timestamp&) const
{
throw BadCastException("Struct -> Poco::Timestamp");
}
VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
{
return cloneHolder(pVarHolder, _val);
}
const ValueType& value() const
{
return _val;
}
bool isArray() const
{
return false;
}
bool isStruct() const
{
return true;
}
bool isOrdered() const
{
return true;
}
bool isInteger() const
{
return false;
}
bool isSigned() const
{
return false;
}
bool isNumeric() const
{
return false;
}
bool isString() const
{
return false;
}
std::size_t size() const
{
return _val.size();
}
Var& operator [] (const KeyType& name)
{
return _val[name];
}
const Var& operator [] (const KeyType& name) const
{
return _val[name];
}
private:
ValueType _val;
};
#endif // POCO_ENABLE_CPP11
} // namespace Dynamic } // namespace Dynamic
typedef Dynamic::Struct<std::string> DynamicStruct; typedef Dynamic::Struct<std::string> DynamicStruct;
#ifdef POCO_ENABLE_CPP11
typedef Dynamic::Struct<std::string, Poco::OrderedMap<std::string, Dynamic::Var>, Poco::OrderedSet<std::string> > OrderedDynamicStruct;
#endif // POCO_ENABLE_CPP11
} // namespace Poco } // namespace Poco

View File

@ -21,16 +21,20 @@
#include "Poco/Foundation.h" #include "Poco/Foundation.h"
#include "Poco/Format.h" #include "Poco/Format.h"
#include "Poco/SharedPtr.h" #include "Poco/SharedPtr.h"
#include "Poco/OrderedMap.h"
#include "Poco/OrderedSet.h"
#include "Poco/Dynamic/VarHolder.h" #include "Poco/Dynamic/VarHolder.h"
#include "Poco/Dynamic/VarIterator.h" #include "Poco/Dynamic/VarIterator.h"
#include <typeinfo> #include <typeinfo>
#include <map>
#include <set>
namespace Poco { namespace Poco {
namespace Dynamic { namespace Dynamic {
template <typename T> template <typename K, typename M, typename S>
class Struct; class Struct;
@ -449,6 +453,10 @@ public:
bool isStruct() const; bool isStruct() const;
/// Returns true if Var represents a struct. /// Returns true if Var represents a struct.
bool isOrdered() const;
/// Returns true if Var represents an ordered struct,
/// false if struct is sorted.
char& at(std::size_t n); char& at(std::size_t n);
/// Returns character at position n. This function only works with /// Returns character at position n. This function only works with
/// Var containing a std::string. /// Var containing a std::string.
@ -595,7 +603,11 @@ private:
throw E(errorMessage); throw E(errorMessage);
} }
Var& structIndexOperator(VarHolderImpl<Struct<int> >* pStr, int n) const; template <typename T, typename N>
Var& structIndexOperator(T* pStr, N n) const
{
return pStr->operator[](n);
}
#ifdef POCO_NO_SOO #ifdef POCO_NO_SOO
@ -822,6 +834,13 @@ inline bool Var::isStruct() const
} }
inline bool Var::isOrdered() const
{
VarHolder* pHolder = content();
return pHolder ? pHolder->isOrdered() : false;
}
inline bool Var::isInteger() const inline bool Var::isInteger() const
{ {
VarHolder* pHolder = content(); VarHolder* pHolder = content();

View File

@ -241,6 +241,10 @@ public:
/// Returns false. Must be properly overriden in a type /// Returns false. Must be properly overriden in a type
/// specialization in order to support the diagnostic. /// specialization in order to support the diagnostic.
virtual bool isOrdered() const;
/// Returns false. Must be properly overriden in a type
/// specialization in order to support the diagnostic.
virtual bool isInteger() const; virtual bool isInteger() const;
/// Returns false. Must be properly overriden in a type /// Returns false. Must be properly overriden in a type
/// specialization in order to support the diagnostic. /// specialization in order to support the diagnostic.
@ -612,6 +616,13 @@ inline bool VarHolder::isStruct() const
return false; return false;
} }
inline bool VarHolder::isOrdered() const
{
return false;
}
inline bool VarHolder::isInteger() const inline bool VarHolder::isInteger() const
{ {
return false; return false;

View File

@ -113,6 +113,8 @@ class SharedPtr
/// is required. /// is required.
{ {
public: public:
typedef C Type;
SharedPtr(): _pCounter(new RC), _ptr(0) SharedPtr(): _pCounter(new RC), _ptr(0)
{ {
} }

View File

@ -362,8 +362,16 @@ Var& Var::getAt(std::size_t n)
return holderImpl<std::deque<Var>, return holderImpl<std::deque<Var>,
InvalidAccessException>("Not a deque.")->operator[](n); InvalidAccessException>("Not a deque.")->operator[](n);
else if (isStruct()) else if (isStruct())
return structIndexOperator(holderImpl<Struct<int>, {
InvalidAccessException>("Not a struct."), static_cast<int>(n)); #ifdef POCO_ENABLE_CPP11
if (isOrdered())
return structIndexOperator(holderImpl<Struct<int, OrderedMap<int, Var>, OrderedSet<int> >,
InvalidAccessException>("Not a struct."), static_cast<int>(n));
else
#endif // POCO_ENABLE_CPP11
return structIndexOperator(holderImpl<Struct<int, std::map<int, Var>, std::set<int> >,
InvalidAccessException>("Not a struct."), static_cast<int>(n));
}
else if (!isString() && !isEmpty() && (n == 0)) else if (!isString() && !isEmpty() && (n == 0))
return *this; return *this;
@ -385,8 +393,17 @@ char& Var::at(std::size_t n)
Var& Var::getAt(const std::string& name) Var& Var::getAt(const std::string& name)
{ {
return holderImpl<DynamicStruct, if (isStruct())
InvalidAccessException>("Not a struct.")->operator[](name); {
#ifdef POCO_ENABLE_CPP11
if (isOrdered())
return structIndexOperator(holderImpl<OrderedDynamicStruct, InvalidAccessException>("Not a struct."), name);
else
#endif // POCO_ENABLE_CPP11
return structIndexOperator(holderImpl<DynamicStruct, InvalidAccessException>("Not a struct."), name);
}
throw InvalidAccessException("Not a struct.");
} }
@ -626,11 +643,11 @@ std::string Var::toString(const Var& any)
return res; return res;
} }
/*
Var& Var::structIndexOperator(VarHolderImpl<Struct<int> >* pStr, int n) const Var& Var::structIndexOperator(VarHolderImpl<Struct<int> >* pStr, int n) const
{ {
return pStr->operator[](n); return pStr->operator[](n);
} }
*/
} } // namespace Poco::Dynamic } } // namespace Poco::Dynamic

View File

@ -14,7 +14,7 @@
#include "Poco/Dynamic/VarIterator.h" #include "Poco/Dynamic/VarIterator.h"
#include "Poco/Dynamic/Var.h" #include "Poco/Dynamic/Var.h"
#include "Poco/Dynamic/Struct.h" //#include "Poco/Dynamic/Struct.h"
#undef min #undef min
#undef max #undef max
#include <limits> #include <limits>

View File

@ -20,7 +20,6 @@
#include <utility> #include <utility>
#if defined(_MSC_VER) && _MSC_VER < 1400 #if defined(_MSC_VER) && _MSC_VER < 1400
#pragma warning(disable:4800)//forcing value to bool 'true' or 'false' #pragma warning(disable:4800)//forcing value to bool 'true' or 'false'
#endif #endif
@ -2256,6 +2255,34 @@ void VarTest::testDynamicStructBasics()
} }
void VarTest::testOrderedDynamicStructBasics()
{
#ifdef POCO_ENABLE_CPP11
OrderedDynamicStruct aStruct;
assertTrue(aStruct.empty());
assertTrue(aStruct.size() == 0);
assertTrue(aStruct.members().empty());
aStruct.insert("First Name", "Little");
assertTrue(!aStruct.empty());
assertTrue(aStruct.size() == 1);
assertTrue(*(aStruct.members().begin()) == "First Name");
assertTrue(aStruct["First Name"] == "Little");
aStruct.insert("Last Name", "POCO");
assertTrue(aStruct.members().size() == 2);
aStruct.erase("First Name");
assertTrue(aStruct.size() == 1);
assertTrue(*(aStruct.members().begin()) == "Last Name");
aStruct.insert("Age", 1);
assertTrue(aStruct["Age"] == 1);
assertTrue(aStruct.members().size() == 2);
assertTrue(*(aStruct.members().begin()) == "Last Name");
aStruct.clear();
assertTrue(aStruct.size() == 0);
#endif // POCO_ENABLE_CPP11
}
void VarTest::testDynamicStructString() void VarTest::testDynamicStructString()
{ {
DynamicStruct aStruct; DynamicStruct aStruct;
@ -2290,6 +2317,45 @@ void VarTest::testDynamicStructString()
} }
void VarTest::testOrderedDynamicStructString()
{
#ifdef POCO_ENABLE_CPP11
OrderedDynamicStruct aStruct;
aStruct["First Name"] = "Junior";
aStruct["Last Name"] = "POCO";
Var a1(aStruct);
assertTrue(a1["First Name"] == "Junior");
assertTrue(a1["Last Name"] == "POCO");
a1["First Name"] = "Senior";
assertTrue(a1["First Name"] == "Senior");
testGetIdxMustThrow(a1, 0);
typedef Struct<std::string, OrderedMap<std::string, Var>, OrderedSet<std::string> > OrderedStruct;
OrderedStruct s1;
s1["1"] = 1;
s1["2"] = 2;
s1["3"] = 3;
OrderedStruct s2(s1);
assertTrue(s2["1"] == 1);
assertTrue(s2["2"] == 2);
assertTrue(s2["3"] == 3);
OrderedMap<std::string, int> m1;
m1["2"] = 2;
m1["1"] = 1;
m1["3"] = 3;
assertTrue (m1.begin()->first == "2");
assertTrue(m1.begin()->second == 2);
OrderedStruct m2(m1);
assertTrue(m2["1"] == 1);
assertTrue(m2["2"] == 2);
assertTrue(m2["3"] == 3);
#endif // POCO_ENABLE_CPP11
}
void VarTest::testDynamicStructInt() void VarTest::testDynamicStructInt()
{ {
Dynamic::Struct<int> aStruct; Dynamic::Struct<int> aStruct;
@ -2325,6 +2391,47 @@ void VarTest::testDynamicStructInt()
} }
void VarTest::testOrderedDynamicStructInt()
{
#ifdef POCO_ENABLE_CPP11
typedef Struct<int, OrderedMap<int, Var>, OrderedSet<int> > OrderedStruct;
OrderedStruct aStruct;
aStruct[0] = "POCO";
aStruct[1] = "Junior";
aStruct[2] = 100;
aStruct[3] = 10;
Var a1(aStruct);
assertTrue(a1[0] == "POCO");
assertTrue(a1[1] == "Junior");
assertTrue(a1[2] == 100);
assertTrue(a1[3] == 10);
a1[0] = "Senior";
assertTrue(a1[0] == "Senior");
OrderedStruct s1;
s1[1] = "1";
s1[2] = "2";
s1[3] = "3";
OrderedStruct s2(s1);
assertTrue(s2[1] == "1");
assertTrue(s2[2] == "2");
assertTrue(s2[3] == "3");
OrderedMap<int, std::string> m1;
m1[1] = "2";
m1[2] = "1";
m1[3] = "3";
OrderedStruct m2(m1);
assertTrue(m2[1] == "2");
assertTrue(m2[2] == "1");
assertTrue(m2[3] == "3");
#endif // POCO_ENABLE_CPP11
}
void VarTest::testDynamicPair() void VarTest::testDynamicPair()
{ {
Pair<int> aPair; Pair<int> aPair;
@ -2414,6 +2521,22 @@ void VarTest::testStructToString()
} }
void VarTest::testOrderedStructToString()
{
#ifdef POCO_ENABLE_CPP11
OrderedDynamicStruct aStruct;
aStruct["First Name"] = "Junior";
aStruct["Last Name"] = "POCO";
aStruct["Age"] = 1;
Var a1(aStruct);
std::string res = a1.convert<std::string>();
std::string expected = "{ \"First Name\" : \"Junior\", \"Last Name\" : \"POCO\", \"Age\" : 1 }";
assertTrue(res == expected);
assertTrue(aStruct.toString() == res);
#endif // POCO_ENABLE_CPP11
}
void VarTest::testStructToStringEscape() void VarTest::testStructToStringEscape()
{ {
DynamicStruct aStruct; DynamicStruct aStruct;
@ -2959,11 +3082,15 @@ CppUnit::Test* VarTest::suite()
CppUnit_addTest(pSuite, VarTest, testArrayIdxOperator); CppUnit_addTest(pSuite, VarTest, testArrayIdxOperator);
CppUnit_addTest(pSuite, VarTest, testDynamicPair); CppUnit_addTest(pSuite, VarTest, testDynamicPair);
CppUnit_addTest(pSuite, VarTest, testDynamicStructBasics); CppUnit_addTest(pSuite, VarTest, testDynamicStructBasics);
CppUnit_addTest(pSuite, VarTest, testOrderedDynamicStructBasics);
CppUnit_addTest(pSuite, VarTest, testDynamicStructString); CppUnit_addTest(pSuite, VarTest, testDynamicStructString);
CppUnit_addTest(pSuite, VarTest, testOrderedDynamicStructString);
CppUnit_addTest(pSuite, VarTest, testDynamicStructInt); CppUnit_addTest(pSuite, VarTest, testDynamicStructInt);
CppUnit_addTest(pSuite, VarTest, testOrderedDynamicStructInt);
CppUnit_addTest(pSuite, VarTest, testArrayToString); CppUnit_addTest(pSuite, VarTest, testArrayToString);
CppUnit_addTest(pSuite, VarTest, testArrayToStringEscape); CppUnit_addTest(pSuite, VarTest, testArrayToStringEscape);
CppUnit_addTest(pSuite, VarTest, testStructToString); CppUnit_addTest(pSuite, VarTest, testStructToString);
CppUnit_addTest(pSuite, VarTest, testOrderedStructToString);
CppUnit_addTest(pSuite, VarTest, testStructToStringEscape); CppUnit_addTest(pSuite, VarTest, testStructToStringEscape);
CppUnit_addTest(pSuite, VarTest, testArrayOfStructsToString); CppUnit_addTest(pSuite, VarTest, testArrayOfStructsToString);
CppUnit_addTest(pSuite, VarTest, testStructWithArraysToString); CppUnit_addTest(pSuite, VarTest, testStructWithArraysToString);

View File

@ -55,11 +55,15 @@ public:
void testArrayIdxOperator(); void testArrayIdxOperator();
void testDynamicPair(); void testDynamicPair();
void testDynamicStructBasics(); void testDynamicStructBasics();
void testOrderedDynamicStructBasics();
void testDynamicStructString(); void testDynamicStructString();
void testOrderedDynamicStructString();
void testDynamicStructInt(); void testDynamicStructInt();
void testOrderedDynamicStructInt();
void testArrayToString(); void testArrayToString();
void testArrayToStringEscape(); void testArrayToStringEscape();
void testStructToString(); void testStructToString();
void testOrderedStructToString();
void testStructToStringEscape(); void testStructToStringEscape();
void testArrayOfStructsToString(); void testArrayOfStructsToString();
void testStructWithArraysToString(); void testStructWithArraysToString();

View File

@ -224,6 +224,16 @@ public:
static Poco::DynamicStruct makeStruct(const Object::Ptr& obj); static Poco::DynamicStruct makeStruct(const Object::Ptr& obj);
/// Utility function for creation of struct. /// Utility function for creation of struct.
#ifdef POCO_ENABLE_CPP11
static Poco::OrderedDynamicStruct makeOrderedStruct(const Object::Ptr& obj);
/// Utility function for creation of ordered struct.
operator const Poco::OrderedDynamicStruct& () const;
/// Cast operator to Poco::OrderedDynamiStruct.
#endif // POCO_ENABLE_CPP11
operator const Poco::DynamicStruct& () const; operator const Poco::DynamicStruct& () const;
/// Cast operator to Poco::DynamiStruct. /// Cast operator to Poco::DynamiStruct.
@ -235,10 +245,21 @@ public:
private: private:
typedef std::deque<ValueMap::const_iterator> KeyList; typedef std::deque<ValueMap::const_iterator> KeyList;
typedef Poco::DynamicStruct::Ptr StructPtr; typedef Poco::DynamicStruct::Ptr StructPtr;
#ifdef POCO_ENABLE_CPP11
typedef Poco::OrderedDynamicStruct::Ptr OrdStructPtr;
#endif // POCO_ENABLE_CPP11
void resetDynStruct() const;
void syncKeys(const KeyList& keys); void syncKeys(const KeyList& keys);
template <typename T>
void resetDynStruct(T& pStruct) const
{
if (!pStruct)
pStruct = new typename T::Type;
else
pStruct->clear();
}
template <typename C> template <typename C>
void doStringify(const C& container, std::ostream& out, unsigned int indent, unsigned int step) const void doStringify(const C& container, std::ostream& out, unsigned int indent, unsigned int step) const
{ {
@ -272,6 +293,59 @@ private:
out << '}'; out << '}';
} }
template <typename S>
static S makeStructImpl(const Object::Ptr& obj)
{
S ds;
if (obj->_preserveInsOrder)
{
KeyList::const_iterator it = obj->_keys.begin();
KeyList::const_iterator end = obj->_keys.end();
for (; it != end; ++it)
{
if (obj->isObject((*it)->first))
{
Object::Ptr pObj = obj->getObject((*it)->first);
S str = makeStructImpl<S>(pObj);
ds.insert((*it)->first, str);
}
else if (obj->isArray((*it)->first))
{
Array::Ptr pArr = obj->getArray((*it)->first);
std::vector<Poco::Dynamic::Var> v = Poco::JSON::Array::makeArray(pArr);
ds.insert((*it)->first, v);
}
else
ds.insert((*it)->first, (*it)->second);
}
}
else
{
ConstIterator it = obj->begin();
ConstIterator end = obj->end();
for (; it != end; ++it)
{
if (obj->isObject(it))
{
Object::Ptr pObj = obj->getObject(it->first);
S str = makeStructImpl<S>(pObj);
ds.insert(it->first, str);
}
else if (obj->isArray(it))
{
Array::Ptr pArr = obj->getArray(it->first);
std::vector<Poco::Dynamic::Var> v = Poco::JSON::Array::makeArray(pArr);
ds.insert(it->first, v);
}
else
ds.insert(it->first, it->second);
}
}
return ds;
}
const std::string& getKey(ValueMap::const_iterator& it) const; const std::string& getKey(ValueMap::const_iterator& it) const;
const Dynamic::Var& getValue(ValueMap::const_iterator& it) const; const Dynamic::Var& getValue(ValueMap::const_iterator& it) const;
const std::string& getKey(KeyList::const_iterator& it) const; const std::string& getKey(KeyList::const_iterator& it) const;
@ -285,8 +359,11 @@ private:
// because Object can be returned stringified from Dynamic::Var::toString(), // because Object can be returned stringified from Dynamic::Var::toString(),
// so it must know whether to escape unicode or not. // so it must know whether to escape unicode or not.
bool _escapeUnicode; bool _escapeUnicode;
mutable StructPtr _pStruct; mutable StructPtr _pStruct;
mutable bool _modified; #ifdef POCO_ENABLE_CPP11
mutable OrdStructPtr _pOrdStruct;
#endif // POCO_ENABLE_CPP11
mutable bool _modified;
}; };

View File

@ -220,52 +220,51 @@ Object& Object::set(const std::string& key, const Dynamic::Var& value)
Poco::DynamicStruct Object::makeStruct(const Object::Ptr& obj) Poco::DynamicStruct Object::makeStruct(const Object::Ptr& obj)
{ {
Poco::DynamicStruct ds; return makeStructImpl<Poco::DynamicStruct>(obj);
ConstIterator it = obj->begin();
ConstIterator end = obj->end();
for (; it != end; ++it)
{
if (obj->isObject(it))
{
Object::Ptr pObj = obj->getObject(it->first);
DynamicStruct str = makeStruct(pObj);
ds.insert(it->first, str);
}
else if (obj->isArray(it))
{
Array::Ptr pArr = obj->getArray(it->first);
std::vector<Poco::Dynamic::Var> v = Poco::JSON::Array::makeArray(pArr);
ds.insert(it->first, v);
}
else
ds.insert(it->first, it->second);
}
return ds;
} }
#ifdef POCO_ENABLE_CPP11
Poco::OrderedDynamicStruct Object::makeOrderedStruct(const Object::Ptr& obj)
{
return makeStructImpl<Poco::OrderedDynamicStruct>(obj);
}
/*
void Object::resetOrdDynStruct() const
{
if (!_pOrdStruct)
_pOrdStruct = new Poco::OrderedDynamicStruct;
else
_pOrdStruct->clear();
}
*/
#endif // POCO_ENABLE_CPP11
/*
void Object::resetDynStruct() const void Object::resetDynStruct() const
{ {
if (!_pStruct) if (!_pStruct)
_pStruct = new Poco::DynamicStruct; _pStruct = new Poco::DynamicStruct;
else else
_pStruct->clear(); _pStruct->clear();
} }*/
Object::operator const Poco::DynamicStruct& () const Object::operator const Poco::DynamicStruct& () const
{ {
if (!_values.size()) if (!_values.size())
{ {
resetDynStruct(); resetDynStruct(_pStruct);
} }
else if (_modified) else if (_modified)
{ {
ValueMap::const_iterator it = _values.begin(); ValueMap::const_iterator it = _values.begin();
ValueMap::const_iterator end = _values.end(); ValueMap::const_iterator end = _values.end();
resetDynStruct(); resetDynStruct(_pStruct);
for (; it != end; ++it) for (; it != end; ++it)
{ {
if (isObject(it)) if (isObject(it))
@ -287,6 +286,68 @@ Object::operator const Poco::DynamicStruct& () const
} }
#ifdef POCO_ENABLE_CPP11
Object::operator const Poco::OrderedDynamicStruct& () const
{
if (!_values.size())
{
resetDynStruct(_pOrdStruct);
}
else if (_modified)
{
if (_preserveInsOrder)
{
KeyList::const_iterator it = _keys.begin();
KeyList::const_iterator end = _keys.end();
resetDynStruct(_pOrdStruct);
for (; it != end; ++it)
{
if (isObject((*it)->first))
{
_pOrdStruct->insert((*it)->first, makeOrderedStruct(getObject((*it)->first)));
}
else if (isArray((*it)->first))
{
_pOrdStruct->insert((*it)->first, Poco::JSON::Array::makeArray(getArray((*it)->first)));
}
else
{
_pOrdStruct->insert((*it)->first, (*it)->second);
}
}
}
else
{
ValueMap::const_iterator it = _values.begin();
ValueMap::const_iterator end = _values.end();
resetDynStruct(_pOrdStruct);
for (; it != end; ++it)
{
if (isObject(it))
{
_pOrdStruct->insert(it->first, makeOrderedStruct(getObject(it->first)));
}
else if (isArray(it))
{
_pOrdStruct->insert(it->first, Poco::JSON::Array::makeArray(getArray(it->first)));
}
else
{
_pOrdStruct->insert(it->first, it->second);
}
}
}
}
return *_pOrdStruct;
}
#endif // POCO_ENABLE_CPP11
void Object::clear() void Object::clear()
{ {
_values.clear(); _values.clear();

View File

@ -1587,7 +1587,6 @@ void JSONTest::testStringifyPreserveOrder()
std::ostringstream ostr; std::ostringstream ostr;
Stringifier::condense(result, ostr); Stringifier::condense(result, ostr);
std::cout << ostr.str() << std::endl;
assertTrue (ostr.str() == "{\"Simpsons\":{\"husband\":{\"name\":\"Homer\",\"age\":38},\"wife\":{\"name\":\"Marge\",\"age\":36}," assertTrue (ostr.str() == "{\"Simpsons\":{\"husband\":{\"name\":\"Homer\",\"age\":38},\"wife\":{\"name\":\"Marge\",\"age\":36},"
"\"children\":[\"Bart\",\"Lisa\",\"Maggie\"]," "\"children\":[\"Bart\",\"Lisa\",\"Maggie\"],"
"\"address\":{\"number\":742,\"street\":\"Evergreen Terrace\",\"town\":\"Springfield\"}}}"); "\"address\":{\"number\":742,\"street\":\"Evergreen Terrace\",\"town\":\"Springfield\"}}}");
@ -1674,7 +1673,12 @@ void JSONTest::testStringifyPreserveOrder()
"}"); "}");
Poco::DynamicStruct ds = *result.extract<Object::Ptr>(); Poco::DynamicStruct ds = *result.extract<Object::Ptr>();
assertTrue(ds.toString() == "{ \"Simpsons\" : { \"address\" : { \"number\" : 742, \"street\" : \"Evergreen Terrace\", \"town\" : \"Springfield\" }, "
"\"children\" : [ \"Bart\", \"Lisa\", \"Maggie\" ], "
"\"husband\" : { \"age\" : 38, \"name\" : \"Homer\" }, "
"\"wife\" : { \"age\" : 36, \"name\" : \"Marge\" } } }");
assertTrue (ds["Simpsons"].isStruct()); assertTrue (ds["Simpsons"].isStruct());
assertFalse(ds["Simpsons"].isOrdered());
assertTrue (ds["Simpsons"]["husband"].isStruct()); assertTrue (ds["Simpsons"]["husband"].isStruct());
assertTrue (ds["Simpsons"]["husband"]["name"] == "Homer"); assertTrue (ds["Simpsons"]["husband"]["name"] == "Homer");
assertTrue (ds["Simpsons"]["husband"]["age"] == 38); assertTrue (ds["Simpsons"]["husband"]["age"] == 38);
@ -1692,6 +1696,17 @@ void JSONTest::testStringifyPreserveOrder()
assertTrue (ds["Simpsons"]["address"]["number"] == 742); assertTrue (ds["Simpsons"]["address"]["number"] == 742);
assertTrue (ds["Simpsons"]["address"]["street"] == "Evergreen Terrace"); assertTrue (ds["Simpsons"]["address"]["street"] == "Evergreen Terrace");
assertTrue (ds["Simpsons"]["address"]["town"] == "Springfield"); assertTrue (ds["Simpsons"]["address"]["town"] == "Springfield");
#ifdef POCO_ENABLE_CPP11
Poco::OrderedDynamicStruct ods = *result.extract<Object::Ptr>();
assertTrue(ods["Simpsons"].isStruct());
assertTrue(ods["Simpsons"].isOrdered());
assertTrue(ods.toString() == "{ \"Simpsons\" : { \"husband\" : { \"name\" : \"Homer\", \"age\" : 38 }, "
"\"wife\" : { \"name\" : \"Marge\", \"age\" : 36 }, "
"\"children\" : [ \"Bart\", \"Lisa\", \"Maggie\" ], "
"\"address\" : { \"number\" : 742, \"street\" : \"Evergreen Terrace\", "
"\"town\" : \"Springfield\" } } }");
#endif // POCO_ENABLE_CPP11
} }