diff --git a/CHANGELOG b/CHANGELOG index 7999d04ac..7b92330c7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -76,6 +76,7 @@ Release 1.5.2 (2013-06-xx) - fixed GH #185: Poco::NumberFormatter::format(double value, int precision) ignore precision == 0 - fixed GH #138: FreeBSD JSON tests fail +- fixed GH #99: JSON::Query an JSON::Object Release 1.5.1 (2013-01-11) diff --git a/Foundation/include/Poco/Any.h b/Foundation/include/Poco/Any.h index ef2bb2fd3..2a21db528 100644 --- a/Foundation/include/Poco/Any.h +++ b/Foundation/include/Poco/Any.h @@ -533,23 +533,6 @@ const ValueType* AnyCast(const Any* operand) } -template -ValueType AnyCast(const Any& operand) - /// AnyCast operator used to extract a copy of the ValueType from an const Any&. - /// - /// Example Usage: - /// MyType tmp = AnyCast(anAny). - /// Will throw a BadCastException if the cast fails. - /// Dont use an AnyCast in combination with references, i.e. MyType& tmp = ... or const MyType& = ... - /// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in - /// these cases. -{ - typedef typename TypeWrapper::TYPE NonRef; - - return AnyCast(const_cast(operand)); -} - - template ValueType AnyCast(Any& operand) /// AnyCast operator used to extract a copy of the ValueType from an Any&. @@ -569,6 +552,23 @@ ValueType AnyCast(Any& operand) } +template +ValueType AnyCast(const Any& operand) + /// AnyCast operator used to extract a copy of the ValueType from an const Any&. + /// + /// Example Usage: + /// MyType tmp = AnyCast(anAny). + /// Will throw a BadCastException if the cast fails. + /// Dont use an AnyCast in combination with references, i.e. MyType& tmp = ... or const MyType& = ... + /// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in + /// these cases. +{ + typedef typename TypeWrapper::TYPE NonRef; + + return AnyCast(const_cast(operand)); +} + + template const ValueType& RefAnyCast(const Any & operand) /// AnyCast operator used to return a const reference to the internal data. diff --git a/JSON/include/Poco/JSON/Array.h b/JSON/include/Poco/JSON/Array.h index a841fac08..cfec4476c 100644 --- a/JSON/include/Poco/JSON/Array.h +++ b/JSON/include/Poco/JSON/Array.h @@ -186,6 +186,9 @@ public: static Poco::Dynamic::Array makeArray(const JSON::Array::Ptr& arr); /// Utility function for creation of array. + void clear(); + /// Clears the contents of the array. + private: typedef SharedPtr ArrayPtr; @@ -389,6 +392,145 @@ private: }; +template <> +class VarHolderImpl: public VarHolder +{ +public: + VarHolderImpl(const JSON::Array& val): _val(val) + { + } + + ~VarHolderImpl() + { + } + + const std::type_info& type() const + { + return typeid(JSON::Array); + } + + void convert(Int8&) const + { + throw BadCastException(); + } + + void convert(Int16&) const + { + throw BadCastException(); + } + + void convert(Int32&) const + { + throw BadCastException(); + } + + void convert(Int64&) const + { + throw BadCastException(); + } + + void convert(UInt8&) const + { + throw BadCastException(); + } + + void convert(UInt16&) const + { + throw BadCastException(); + } + + void convert(UInt32&) const + { + throw BadCastException(); + } + + void convert(UInt64&) const + { + throw BadCastException(); + } + + void convert(bool& value) const + { + value = _val.size() > 0; + } + + void convert(float&) const + { + throw BadCastException(); + } + + void convert(double&) const + { + throw BadCastException(); + } + + void convert(char&) const + { + throw BadCastException(); + } + + void convert(std::string& s) const + { + std::ostringstream oss; + _val.stringify(oss, 2); + s = oss.str(); + } + + void convert(DateTime& /*val*/) const + { + throw BadCastException("Cannot convert Array to DateTime"); + } + + void convert(LocalDateTime& /*ldt*/) const + { + throw BadCastException("Cannot convert Array to LocalDateTime"); + } + + void convert(Timestamp& /*ts*/) const + { + throw BadCastException("Cannot convert Array to Timestamp"); + } + + VarHolder* clone(Placeholder* pVarHolder = 0) const + { + return cloneHolder(pVarHolder, _val); + } + + const JSON::Array& value() const + { + return _val; + } + + bool isArray() const + { + return false; + } + + bool isInteger() const + { + return false; + } + + bool isSigned() const + { + return false; + } + + bool isNumeric() const + { + return false; + } + + bool isString() const + { + return false; + } + +private: + JSON::Array _val; +}; + + }} // namespace Poco::JSON diff --git a/JSON/include/Poco/JSON/Object.h b/JSON/include/Poco/JSON/Object.h index f417bd6e2..b54cc1aac 100644 --- a/JSON/include/Poco/JSON/Object.h +++ b/JSON/include/Poco/JSON/Object.h @@ -206,6 +206,10 @@ public: operator const Poco::DynamicStruct& () const; /// Cast operator to Poco::DynamiStruct. + void clear(); + /// Clears the contents of the object. Insertion order + /// preservation property is left intact. + private: template void doStringify(const C& container, std::ostream& out, unsigned int indent, int step) const @@ -472,6 +476,148 @@ private: }; +template <> +class VarHolderImpl: public VarHolder +{ +public: + VarHolderImpl(const JSON::Object& val): _val(val) + { + } + + ~VarHolderImpl() + { + } + + const std::type_info& type() const + { + return typeid(JSON::Object); + } + + void convert(Int8&) const + { + throw BadCastException(); + } + + void convert(Int16&) const + { + throw BadCastException(); + } + + void convert(Int32&) const + { + throw BadCastException(); + } + + void convert(Int64&) const + { + throw BadCastException(); + } + + void convert(UInt8&) const + { + throw BadCastException(); + } + + void convert(UInt16&) const + { + throw BadCastException(); + } + + void convert(UInt32&) const + { + throw BadCastException(); + } + + void convert(UInt64&) const + { + throw BadCastException(); + } + + void convert(bool& value) const + { + value = _val.size() > 0; + } + + void convert(float&) const + { + throw BadCastException(); + } + + void convert(double&) const + { + throw BadCastException(); + } + + void convert(char&) const + { + throw BadCastException(); + } + + void convert(std::string& s) const + { + std::ostringstream oss; + _val.stringify(oss, 2); + s = oss.str(); + } + + void convert(DateTime& /*val*/) const + { + //TODO: val = _val; + throw NotImplementedException("Conversion not implemented: JSON:Object => DateTime"); + } + + void convert(LocalDateTime& /*ldt*/) const + { + //TODO: ldt = _val.timestamp(); + throw NotImplementedException("Conversion not implemented: JSON:Object => LocalDateTime"); + } + + void convert(Timestamp& /*ts*/) const + { + //TODO: ts = _val.timestamp(); + throw NotImplementedException("Conversion not implemented: JSON:Object => Timestamp"); + } + + VarHolder* clone(Placeholder* pVarHolder = 0) const + { + return cloneHolder(pVarHolder, _val); + } + + const JSON::Object& value() const + { + return _val; + } + + bool isArray() const + { + return false; + } + + bool isInteger() const + { + return false; + } + + bool isSigned() const + { + return false; + } + + bool isNumeric() const + { + return false; + } + + bool isString() const + { + return false; + } + +private: + JSON::Object _val; +}; + + }} // namespace Poco::JSON diff --git a/JSON/include/Poco/JSON/Query.h b/JSON/include/Poco/JSON/Query.h index d5cd03efe..af93b897f 100644 --- a/JSON/include/Poco/JSON/Query.h +++ b/JSON/include/Poco/JSON/Query.h @@ -53,18 +53,38 @@ class JSON_API Query { public: Query(const Dynamic::Var& source); - /// Constructor. Pass the start object/array. + /// Constructor. Pass the start object/array or Ptr thereof. + /// Creating Query holding Ptr will typically result in faster + /// performance. virtual ~Query(); /// Destructor Object::Ptr findObject(const std::string& path) const; - /// Search for an object. When the object can't be found, an empty - /// SharedPtr is returned. + /// Search for an object. When the object can't be found, a zero Ptr + /// is returned; otherwise, a shared pointer to internally held object + /// is returned. + /// If object (as opposed to a pointer to object) is held + /// internally, a shared pointer to new (heap-allocated) Object is + /// returned; this may be expensive operation. + + Object& findObject(const std::string& path, Object& obj) const; + /// Search for an object. If object is found, it is assigned to the + /// Object through the reference passed in. When the object can't be + /// found, the provided Object is emptied and returned. Array::Ptr findArray(const std::string& path) const; - /// Search for an array. When the array can't be found, an empty - /// SharedPtr is returned. + /// Search for an array. When the array can't be found, a zero Ptr + /// is returned; otherwise, a shared pointer to internally held array + /// is returned. + /// If array (as opposed to a pointer to array) is held + /// internally, a shared pointer to new (heap-allocated) Object is + /// returned; this may be expensive operation. + + Array& findArray(const std::string& path, Array& obj) const; + /// Search for an array. If array is found, it is assigned to the + /// Object through the reference passed in. When the array can't be + /// found, the provided Object is emptied and returned. Dynamic::Var find(const std::string& path) const; /// Searches a value diff --git a/JSON/src/Array.cpp b/JSON/src/Array.cpp index cdc2c86b3..56f905210 100644 --- a/JSON/src/Array.cpp +++ b/JSON/src/Array.cpp @@ -222,4 +222,11 @@ Poco::Dynamic::Array Array::makeArray(const JSON::Array::Ptr& arr) } +void Array::clear() +{ + _values.clear(); + _pArray = 0; +} + + } } // namespace Poco::JSON diff --git a/JSON/src/Object.cpp b/JSON/src/Object.cpp index 5ba2caa2a..ce2ea5e47 100644 --- a/JSON/src/Object.cpp +++ b/JSON/src/Object.cpp @@ -206,4 +206,12 @@ Object::operator const Poco::DynamicStruct& () const } +void Object::clear() +{ + _values.clear(); + _keys.clear(); + _pStruct = 0; +} + + } } // namespace Poco::JSON diff --git a/JSON/src/Query.cpp b/JSON/src/Query.cpp index 2b30059bb..e54388fd7 100644 --- a/JSON/src/Query.cpp +++ b/JSON/src/Query.cpp @@ -50,7 +50,6 @@ namespace JSON { Query::Query(const Var& source): _source(source) { - } @@ -61,24 +60,56 @@ Query::~Query() Object::Ptr Query::findObject(const std::string& path) const { - Object::Ptr obj; Var result = find(path); - if ( result.type() == typeid(Object::Ptr) ) - { - obj = result.extract(); - } + + if (result.type() == typeid(Object::Ptr)) + return result.extract(); + else if (result.type() == typeid(Object)) + return new Object(result.extract()); + + return 0; +} + + +Object& Query::findObject(const std::string& path, Object& obj) const +{ + obj.clear(); + + Var result = find(path); + + if (result.type() == typeid(Object::Ptr)) + obj = *result.extract(); + else if (result.type() == typeid(Object)) + obj = result.extract(); + return obj; } Array::Ptr Query::findArray(const std::string& path) const { - Array::Ptr arr; Var result = find(path); - if ( result.type() == typeid(Array::Ptr) ) - { - arr = result.extract(); - } + + if (result.type() == typeid(Array::Ptr)) + return result.extract(); + else if (result.type() == typeid(Array)) + return new Array(result.extract()); + + return 0; +} + + +Array& Query::findArray(const std::string& path, Array& arr) const +{ + arr.clear(); + + Var result = find(path); + + if (result.type() == typeid(Array::Ptr)) + arr = *result.extract(); + else if (result.type() == typeid(Array)) + arr = result.extract(); + return arr; } @@ -89,16 +120,16 @@ Var Query::find(const std::string& path) const StringTokenizer tokenizer(path, "."); for(StringTokenizer::Iterator token = tokenizer.begin(); token != tokenizer.end(); token++) { - if ( !result.isEmpty() ) + if (!result.isEmpty()) { std::vector indexes; RegularExpression::MatchVec matches; int firstOffset = -1; int offset = 0; RegularExpression regex("\\[([0-9]+)\\]"); - while(regex.match(*token, offset, matches) > 0 ) + while(regex.match(*token, offset, matches) > 0) { - if ( firstOffset == -1 ) + if (firstOffset == -1) { firstOffset = static_cast(matches[0].offset); } @@ -108,33 +139,40 @@ Var Query::find(const std::string& path) const } std::string name(*token); - if ( firstOffset != -1 ) + if (firstOffset != -1) { name = name.substr(0, firstOffset); } - if ( name.length() > 0 ) + if (name.length() > 0) { - if ( result.type() == typeid(Object::Ptr) ) + if (result.type() == typeid(Object::Ptr)) { Object::Ptr o = result.extract(); result = o->get(name); } + else if (result.type() == typeid(Object)) + { + Object o = result.extract(); + result = o.get(name); + } } - if (!result.isEmpty() - && !indexes.empty() ) + if (!result.isEmpty() && !indexes.empty()) { - for(std::vector::iterator it = indexes.begin(); it != indexes.end(); ++it ) + for(std::vector::iterator it = indexes.begin(); it != indexes.end(); ++it) { - if ( result.type() == typeid(Array::Ptr) ) + if (result.type() == typeid(Array::Ptr)) { Array::Ptr array = result.extract(); result = array->get(*it); - if ( result.isEmpty() ) - { - break; - } + if (result.isEmpty()) break; + } + else if (result.type() == typeid(Array)) + { + Array array = result.extract(); + result = array.get(*it); + if (result.isEmpty()) break; } } } diff --git a/JSON/testsuite/src/JSONTest.cpp b/JSON/testsuite/src/JSONTest.cpp index 2c1c232d3..3914f0654 100644 --- a/JSON/testsuite/src/JSONTest.cpp +++ b/JSON/testsuite/src/JSONTest.cpp @@ -1033,7 +1033,7 @@ void JSONTest::testOptValue() void JSONTest::testQuery() { - std::string json = "{ \"name\" : \"Franky\", \"children\" : [ \"Jonas\", \"Ellen\" ] }"; + std::string json = "{ \"name\" : \"Franky\", \"children\" : [ \"Jonas\", \"Ellen\" ], \"address\": { \"street\": \"A Street\", \"number\": 123, \"city\":\"The City\"} }"; Parser parser; Var result; @@ -1059,6 +1059,43 @@ void JSONTest::testQuery() assert (ds["children"].size() == 2); assert (ds["children"][0] == "Jonas"); assert (ds["children"][1] == "Ellen"); + + Object::Ptr pAddress = query.findObject("address"); + assert (pAddress->getValue("street") == "A Street"); + pAddress = query.findObject("bad address"); + assert (pAddress.isNull()); + + Object address; + address.set("dummy", 123); + query.findObject("bad address", address); + assert (!address.has("dummy")); + Object& rAddress = query.findObject("address", address); + assert (rAddress.getValue("number") == 123); + + using Poco::JSON::Array; + + Array::Ptr pChildren = query.findArray("children"); + assert (pChildren->getElement(0) == "Jonas"); + pChildren = query.findArray("no children"); + assert (pChildren.isNull()); + + Array children; + children.add("dummy"); + query.findArray("no children", children); + assert (children.size() == 0); + Array& rChildren = query.findArray("children", children); + assert (rChildren.getElement(1) == "Ellen"); + + Object::Ptr pObj = new Poco::JSON::Object; + pObj->set("Id", 22); + + Query queryPointer(pObj); + Var idQueryPointer = queryPointer.find("Id"); + assert(22 == idQueryPointer); + + Query queryObj(*pObj); + Var idQueryObj = queryObj.find("Id"); + assert (22 == idQueryObj); }