diff --git a/include/mujincontrollerclient/mujinjson.h b/include/mujincontrollerclient/mujinjson.h index b9ea3ec8..bc8a66b4 100644 --- a/include/mujincontrollerclient/mujinjson.h +++ b/include/mujincontrollerclient/mujinjson.h @@ -37,6 +37,15 @@ #include #include +#ifndef MUJINCLIENTJSON_LOAD_REQUIRED_JSON_VALUE_BY_KEY +#define MUJINCLIENTJSON_LOAD_REQUIRED_JSON_VALUE_BY_KEY(rValue, key, param) \ + { \ + if (!(mujinclient::mujinjson_external::LoadJsonValueByKey(rValue, key, param))) \ + { \ + throw mujinclient::mujinjson_external::MujinJSONException(boost::str(boost::format(("[%s, %u] assert(mujinclient::mujinjson_external::LoadJsonValueByKey(%s, %s, %s))"))%__FILE__%__LINE__%#rValue%key%#param), mujinclient::mujinjson_external::MJE_Failed); \ + } \ + } +#endif // MUJINCLIENTJSON_LOAD_REQUIRED_JSON_VALUE_BY_KEY namespace mujinclient { namespace mujinjson_external { @@ -567,13 +576,25 @@ template inline void SaveJsonValue(rapidjson::Document& v, const T& t) template inline void SetJsonValueByKey(rapidjson::Value& v, const U& key, const T& t, rapidjson::Document::AllocatorType& alloc); //get one json value by key, and store it in local data structures -template void inline LoadJsonValueByKey(const rapidjson::Value& v, const char* key, T& t) { +//returns false if the key does not exist or is null, else returns true +template bool inline LoadJsonValueByKey(const rapidjson::Value& v, const char* key, T& t) { if (!v.IsObject()) { throw MujinJSONException("Cannot load value of non-object.", MJE_Failed); } - if (v.HasMember(key)) { - LoadJsonValue(v[key], t); + rapidjson::Value::ConstMemberIterator itMember = v.FindMember(key); + if( itMember != v.MemberEnd() ) { + const rapidjson::Value& rMember = itMember->value; + if( !rMember.IsNull() ) { + try { + LoadJsonValue(rMember, t); + return true; + } + catch (const MujinJSONException& ex) { + throw MujinJSONException("Got \"" + ex.GetSubMessage() + "\" while parsing the value of \"" + key + "\"", MJE_Failed); + } + } } + return false; } template inline void LoadJsonValueByKey(const rapidjson::Value& v, const char* key, T& t, const U& d) { if (!v.IsObject()) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9e599c24..f8d72cfe 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,3 +29,4 @@ build_test(settaskparameters) build_test(showresults) build_test(uploadregister) build_test(uploadregistercec) +build_test(testjsonutils) diff --git a/test/testjsonutils.cpp b/test/testjsonutils.cpp new file mode 100644 index 00000000..9fe7fa48 --- /dev/null +++ b/test/testjsonutils.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +// The tests will expect json documents containing these keys +using namespace mujinclient::mujinjson_external; +const std::string expectedJsonKey = "key1"; + +void TestExistingKey() +{ + rapidjson::Document document; + document.SetObject(); + int expectedResult = 1; + // Expected key and an extra key are in the document + SetJsonValueByKey(document, expectedJsonKey.c_str(), expectedResult, document.GetAllocator()); + SetJsonValueByKey(document, "extraKey", expectedResult, document.GetAllocator()); + int result = -1; + MUJINCLIENTJSON_LOAD_REQUIRED_JSON_VALUE_BY_KEY(document, expectedJsonKey.c_str(), result); + if(result == expectedResult) { + std::cout << "OK: For existing key test, we got result = " << result; + } + else { + std::cout << "FAILED: For existing key test, we got result = " << result; + } + std::cout << " when expected result was " << expectedResult << "\n"; +} + +void TestNonExistentKey() +{ + rapidjson::Document document; + document.SetObject(); + // Expected key is not in the document + SetJsonValueByKey(document, "extraKey", 0, document.GetAllocator()); + int result = -1; + int exceptionLine = -1; + try + { + exceptionLine = __LINE__ + 1; + MUJINCLIENTJSON_LOAD_REQUIRED_JSON_VALUE_BY_KEY(document, expectedJsonKey.c_str(), result); + } + catch(const MujinJSONException& e) + { + std::stringstream ss; + ss << "mujin (): [" << __FILE__<<", "<< exceptionLine <<"] assert(mujinclient::mujinjson_external::LoadJsonValueByKey(document, key1, result))"; + if(ss.str() == std::string(e.what())) { + std::cout << "OK: "; + } + else{ + std::cout << "FAILED: \n"; + std::cout << "Expected = " << ss.str() << "\n"; + } + std::cout << "Got exception message = " << e.what() << "\n"; + } + catch(const std::exception& e) + { + std::cout << "FAILED: Caught an exception, but not the right type.\n" << "Exception message = " << e.what() << "\n"; + } +} + +int main() +{ + TestExistingKey(); + TestNonExistentKey(); +}