fixed GH #99: JSON::Query an JSON::Object

- fixed GH #99: JSON::Query an JSON::Object
- swapped order of AnyCast(const Any&) and AnyCast(Any&) definitions
This commit is contained in:
Aleksandar Fabijanic
2013-06-07 23:15:02 -05:00
parent 1d2a95e110
commit 5e6ef1c14d
9 changed files with 447 additions and 48 deletions

View File

@@ -76,6 +76,7 @@ Release 1.5.2 (2013-06-xx)
- fixed GH #185: Poco::NumberFormatter::format(double value, int precision) - fixed GH #185: Poco::NumberFormatter::format(double value, int precision)
ignore precision == 0 ignore precision == 0
- fixed GH #138: FreeBSD JSON tests fail - fixed GH #138: FreeBSD JSON tests fail
- fixed GH #99: JSON::Query an JSON::Object
Release 1.5.1 (2013-01-11) Release 1.5.1 (2013-01-11)

View File

@@ -533,23 +533,6 @@ const ValueType* AnyCast(const Any* operand)
} }
template <typename ValueType>
ValueType AnyCast(const Any& operand)
/// AnyCast operator used to extract a copy of the ValueType from an const Any&.
///
/// Example Usage:
/// MyType tmp = AnyCast<MyType>(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<ValueType>::TYPE NonRef;
return AnyCast<NonRef&>(const_cast<Any&>(operand));
}
template <typename ValueType> template <typename ValueType>
ValueType AnyCast(Any& operand) ValueType AnyCast(Any& operand)
/// AnyCast operator used to extract a copy of the ValueType from an Any&. /// AnyCast operator used to extract a copy of the ValueType from an Any&.
@@ -569,6 +552,23 @@ ValueType AnyCast(Any& operand)
} }
template <typename ValueType>
ValueType AnyCast(const Any& operand)
/// AnyCast operator used to extract a copy of the ValueType from an const Any&.
///
/// Example Usage:
/// MyType tmp = AnyCast<MyType>(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<ValueType>::TYPE NonRef;
return AnyCast<NonRef&>(const_cast<Any&>(operand));
}
template <typename ValueType> template <typename ValueType>
const ValueType& RefAnyCast(const Any & operand) const ValueType& RefAnyCast(const Any & operand)
/// AnyCast operator used to return a const reference to the internal data. /// AnyCast operator used to return a const reference to the internal data.

View File

@@ -186,6 +186,9 @@ public:
static Poco::Dynamic::Array makeArray(const JSON::Array::Ptr& arr); static Poco::Dynamic::Array makeArray(const JSON::Array::Ptr& arr);
/// Utility function for creation of array. /// Utility function for creation of array.
void clear();
/// Clears the contents of the array.
private: private:
typedef SharedPtr<Poco::Dynamic::Array> ArrayPtr; typedef SharedPtr<Poco::Dynamic::Array> ArrayPtr;
@@ -389,6 +392,145 @@ private:
}; };
template <>
class VarHolderImpl<JSON::Array>: 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<VarHolder>* 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 }} // namespace Poco::JSON

View File

@@ -206,6 +206,10 @@ public:
operator const Poco::DynamicStruct& () const; operator const Poco::DynamicStruct& () const;
/// Cast operator to Poco::DynamiStruct. /// Cast operator to Poco::DynamiStruct.
void clear();
/// Clears the contents of the object. Insertion order
/// preservation property is left intact.
private: private:
template <typename C> template <typename C>
void doStringify(const C& container, std::ostream& out, unsigned int indent, int step) const void doStringify(const C& container, std::ostream& out, unsigned int indent, int step) const
@@ -472,6 +476,148 @@ private:
}; };
template <>
class VarHolderImpl<JSON::Object>: 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<VarHolder>* 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 }} // namespace Poco::JSON

View File

@@ -53,18 +53,38 @@ class JSON_API Query
{ {
public: public:
Query(const Dynamic::Var& source); 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(); virtual ~Query();
/// Destructor /// Destructor
Object::Ptr findObject(const std::string& path) const; Object::Ptr findObject(const std::string& path) const;
/// Search for an object. When the object can't be found, an empty /// Search for an object. When the object can't be found, a zero Ptr
/// SharedPtr is returned. /// 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; Array::Ptr findArray(const std::string& path) const;
/// Search for an array. When the array can't be found, an empty /// Search for an array. When the array can't be found, a zero Ptr
/// SharedPtr is returned. /// 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; Dynamic::Var find(const std::string& path) const;
/// Searches a value /// Searches a value

View File

@@ -222,4 +222,11 @@ Poco::Dynamic::Array Array::makeArray(const JSON::Array::Ptr& arr)
} }
void Array::clear()
{
_values.clear();
_pArray = 0;
}
} } // namespace Poco::JSON } } // namespace Poco::JSON

View File

@@ -206,4 +206,12 @@ Object::operator const Poco::DynamicStruct& () const
} }
void Object::clear()
{
_values.clear();
_keys.clear();
_pStruct = 0;
}
} } // namespace Poco::JSON } } // namespace Poco::JSON

View File

@@ -50,7 +50,6 @@ namespace JSON {
Query::Query(const Var& source): _source(source) Query::Query(const Var& source): _source(source)
{ {
} }
@@ -61,24 +60,56 @@ Query::~Query()
Object::Ptr Query::findObject(const std::string& path) const Object::Ptr Query::findObject(const std::string& path) const
{ {
Object::Ptr obj;
Var result = find(path); Var result = find(path);
if ( result.type() == typeid(Object::Ptr) )
{ if (result.type() == typeid(Object::Ptr))
obj = result.extract<Object::Ptr>(); return result.extract<Object::Ptr>();
} else if (result.type() == typeid(Object))
return new Object(result.extract<Object>());
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<Object::Ptr>();
else if (result.type() == typeid(Object))
obj = result.extract<Object>();
return obj; return obj;
} }
Array::Ptr Query::findArray(const std::string& path) const Array::Ptr Query::findArray(const std::string& path) const
{ {
Array::Ptr arr;
Var result = find(path); Var result = find(path);
if ( result.type() == typeid(Array::Ptr) )
{ if (result.type() == typeid(Array::Ptr))
arr = result.extract<Array::Ptr>(); return result.extract<Array::Ptr>();
} else if (result.type() == typeid(Array))
return new Array(result.extract<Array>());
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<Array::Ptr>();
else if (result.type() == typeid(Array))
arr = result.extract<Array>();
return arr; return arr;
} }
@@ -89,16 +120,16 @@ Var Query::find(const std::string& path) const
StringTokenizer tokenizer(path, "."); StringTokenizer tokenizer(path, ".");
for(StringTokenizer::Iterator token = tokenizer.begin(); token != tokenizer.end(); token++) for(StringTokenizer::Iterator token = tokenizer.begin(); token != tokenizer.end(); token++)
{ {
if ( !result.isEmpty() ) if (!result.isEmpty())
{ {
std::vector<int> indexes; std::vector<int> indexes;
RegularExpression::MatchVec matches; RegularExpression::MatchVec matches;
int firstOffset = -1; int firstOffset = -1;
int offset = 0; int offset = 0;
RegularExpression regex("\\[([0-9]+)\\]"); 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<int>(matches[0].offset); firstOffset = static_cast<int>(matches[0].offset);
} }
@@ -108,33 +139,40 @@ Var Query::find(const std::string& path) const
} }
std::string name(*token); std::string name(*token);
if ( firstOffset != -1 ) if (firstOffset != -1)
{ {
name = name.substr(0, firstOffset); 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<Object::Ptr>(); Object::Ptr o = result.extract<Object::Ptr>();
result = o->get(name); result = o->get(name);
} }
else if (result.type() == typeid(Object))
{
Object o = result.extract<Object>();
result = o.get(name);
}
} }
if (!result.isEmpty() if (!result.isEmpty() && !indexes.empty())
&& !indexes.empty() )
{ {
for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end(); ++it ) for(std::vector<int>::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<Array::Ptr>(); Array::Ptr array = result.extract<Array::Ptr>();
result = array->get(*it); result = array->get(*it);
if ( result.isEmpty() ) if (result.isEmpty()) break;
{ }
break; else if (result.type() == typeid(Array))
} {
Array array = result.extract<Array>();
result = array.get(*it);
if (result.isEmpty()) break;
} }
} }
} }

View File

@@ -1033,7 +1033,7 @@ void JSONTest::testOptValue()
void JSONTest::testQuery() 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; Parser parser;
Var result; Var result;
@@ -1059,6 +1059,43 @@ void JSONTest::testQuery()
assert (ds["children"].size() == 2); assert (ds["children"].size() == 2);
assert (ds["children"][0] == "Jonas"); assert (ds["children"][0] == "Jonas");
assert (ds["children"][1] == "Ellen"); assert (ds["children"][1] == "Ellen");
Object::Ptr pAddress = query.findObject("address");
assert (pAddress->getValue<std::string>("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<int>("number") == 123);
using Poco::JSON::Array;
Array::Ptr pChildren = query.findArray("children");
assert (pChildren->getElement<std::string>(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<std::string>(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);
} }