Kaynağa Gözat

#IQRV #comment [libRestfulFunc] 支持 Json2Object 的实现

gifur 3 yıl önce
ebeveyn
işleme
bcac6c3ec6

+ 6 - 3
Other/libRestfulFunc/CMakeLists.txt

@@ -6,28 +6,31 @@ set(${MODULE_PREFIX}_SRCS
     RestfulFuncImpl.cpp
     HttpProbeImpl.cpp
     PingImpl.cpp
+    JsonConvertHelper.hpp
 )
 
 add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
 
-
 # 依赖libscreencodec、acmstrdec、acmstrenc
 target_include_directories(${MODULE_NAME} PRIVATE
 	${RVC_COMMON_INCLUDE_DIR}
     ${CONAN_INCLUDE_DIRS_OPENSSL}
     ${CONAN_INCLUDE_DIRS_CPPRESTSDK}
     ${CONAN_BOOST_ROOT}
+    ${CONAN_INCLUDE_DIRS_JSONCPP}
     )
 
 target_link_directories(${MODULE_NAME} PRIVATE 
     ${CONAN_LIB_DIRS_OPENSSL} 
-    ${CONAN_LIB_DIRS_CPPRESTSDK})
+    ${CONAN_LIB_DIRS_CPPRESTSDK}
+    ${CONAN_LIB_DIRS_JSONCPP}
+    )
 
 target_compile_definitions(${MODULE_NAME} PUBLIC "RESTFULPUBLIC_EXPORTS")
 
 # 添加需要依赖的其他共享库(包括系统库)
 target_link_libraries(${MODULE_NAME} PRIVATE 
-    ${CONAN_PKG_LIBS_OPENSSL} ${CONAN_LIBS_CPPRESTSDK})
+    ${CONAN_PKG_LIBS_OPENSSL} ${CONAN_LIBS_CPPRESTSDK} ${CONAN_PKG_LIBS_JSONCPP})
 
 if(MSVC)
 	install(TARGETS ${MODULE_NAME} 

+ 260 - 0
Other/libRestfulFunc/JsonConvertHelper.hpp

@@ -0,0 +1,260 @@
+#ifndef _SP_UTILITY_JSON_CONVERT_HELPER_
+#define _SP_UTILITY_JSON_CONVERT_HELPER_
+
+#include "json/json.h"
+#include <string>
+#include <vector>
+#include <initializer_list>
+
+#define JSONCONVERT2OBJECT_MEMEBER_REGISTER(...)  \
+bool JSONCONVERT2OBJECT_MEMEBER_REGISTER_RESERVERD_IMPLE(const Json::Value& jsonTypeValue, std::vector<std::string> &names) \
+{     \
+    if(names.size() <= 0)   \
+        names = Member2KeyParseWithStr(#__VA_ARGS__); \
+    return JsonParse(names, 0, jsonTypeValue, __VA_ARGS__); \
+} 
+
+#define JSONCONVERT2OBJECT_MEMEBER_RENAME_REGISTER(...)  \
+std::vector<std::string> JSONCONVERT2OBJECT_MEMEBER_RENAME_REGISTER_RESERVERD_IMPLE() const \
+{     \
+    return Member2KeyParseWithMultiParam({ __VA_ARGS__ }); \
+}
+
+template <bool, class TYPE = void>
+struct enable_if
+{
+};
+template <class TYPE>
+struct enable_if<true, TYPE>
+{
+    typedef TYPE type;
+};
+
+template <typename T>
+struct HasConverFunction
+{
+    template <typename TT>
+    static char func(decltype(&TT::JSONCONVERT2OBJECT_MEMEBER_REGISTER_RESERVERD_IMPLE)); //@1
+    
+    template <typename TT>
+    static int func(...); //@2
+    /*
+     * 如果类型T没有 JSONCONVERT2OBJECT_MEMEBER_REGISTER_RESERVERD_IMPLE 方法,
+     * func<T>(NULL) 匹配 @1 时会产生错误,由于 SFINAE 准则,只能匹配@2
+     * 的func,此时返回值 4 个字节,has 变量为 false,反之,has 变量为 true
+     */
+    const static bool has = (sizeof(func<T>(NULL)) == sizeof(char));
+
+    template <typename TT>
+    static char func2(decltype(&TT::JSONCONVERT2OBJECT_MEMEBER_RENAME_REGISTER_RESERVERD_IMPLE)); //@1
+    template <typename TT>
+    static int func2(...); //@2
+    const static bool has2 = (sizeof(func2<T>(NULL)) == sizeof(char));
+};
+
+static std::vector<std::string> Member2KeyParseWithMultiParam(std::initializer_list<std::string> il)
+{
+    std::vector<std::string> result;
+    for (auto it = il.begin(); it != il.end(); it++) {
+        result.push_back(*it);
+    }
+    return result;
+}
+
+inline static std::string NormalStringTrim(std::string const& str)
+{
+    static char const* whitespaceChars = "\n\r\t ";
+    std::string::size_type start = str.find_first_not_of(whitespaceChars);
+    std::string::size_type end = str.find_last_not_of(whitespaceChars);
+    return start != std::string::npos ? str.substr(start, 1 + end - start) : std::string();
+}
+
+inline static std::vector<std::string> NormalStringSplit(std::string str, char splitElem)
+{
+    std::vector<std::string> strs;
+    std::string::size_type pos1, pos2;
+    pos2 = str.find(splitElem);
+    pos1 = 0;
+    while (std::string::npos != pos2) {
+        strs.push_back(str.substr(pos1, pos2 - pos1));
+        pos1 = pos2 + 1;
+        pos2 = str.find(splitElem, pos1);
+    }
+    strs.push_back(str.substr(pos1));
+    return strs;
+}
+
+static std::vector<std::string> Member2KeyParseWithStr(const std::string& values)
+{
+    std::vector<std::string> result;
+    auto enumValues = NormalStringSplit(values, ',');
+    result.reserve(enumValues.size());
+    for (auto const& enumValue : enumValues) {
+        result.push_back(NormalStringTrim(enumValue));
+    }
+    return result;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static bool Json2Object(bool& aimObj, const Json::Value& jsonTypeValue)
+{
+    if (jsonTypeValue.isNull() || !jsonTypeValue.isBool()) {
+        return false;
+    } else {
+        aimObj = jsonTypeValue.asBool();
+        return true;
+    }
+}
+static bool Json2Object(int& aimObj, const Json::Value& jsonTypeValue)
+{
+    if (jsonTypeValue.isNull() || !jsonTypeValue.isInt()) {
+        return false;
+    } else {
+        aimObj = jsonTypeValue.asInt();
+        return true;
+    }
+}
+static bool Json2Object(unsigned int& aimObj, const Json::Value& jsonTypeValue)
+{
+    if (jsonTypeValue.isNull() || !jsonTypeValue.isUInt()) {
+        return false;
+    } else {
+        aimObj = jsonTypeValue.asUInt();
+        return true;
+    }
+}
+static bool Json2Object(double& aimObj, const Json::Value& jsonTypeValue)
+{
+    if (jsonTypeValue.isNull() || !jsonTypeValue.isDouble()) {
+        return false;
+    } else {
+        aimObj = jsonTypeValue.asDouble();
+        return true;
+    }
+}
+static bool Json2Object(std::string& aimObj, const Json::Value& jsonTypeValue)
+{
+    if (jsonTypeValue.isNull() || !jsonTypeValue.isString()) {
+        return false;
+    } else {
+        aimObj = jsonTypeValue.asString();
+        return true;
+    }
+}
+
+
+template <typename TClass, typename enable_if<HasConverFunction<TClass>::has2, int>::type = 0>
+static inline std::vector<std::string> PreGetCustomMemberNameIfExists(TClass& aimObj)
+{
+    return aimObj.JSONCONVERT2OBJECT_MEMEBER_RENAME_REGISTER_RESERVERD_IMPLE();
+}
+
+template <typename TClass, typename enable_if<!HasConverFunction<TClass>::has2, int>::type = 0>
+static inline std::vector<std::string> PreGetCustomMemberNameIfExists(TClass& aimObj)
+{
+    return std::vector<std::string>();
+}
+
+template <typename TClass, typename enable_if<HasConverFunction<TClass>::has, int>::type = 0>
+static inline bool Json2Object(TClass& aimObj, const Json::Value& jsonTypeValue)
+{
+    std::vector<std::string> names = PreGetCustomMemberNameIfExists(aimObj);
+    return aimObj.JSONCONVERT2OBJECT_MEMEBER_REGISTER_RESERVERD_IMPLE(jsonTypeValue, names);
+}
+
+template <typename TClass, typename enable_if<!HasConverFunction<TClass>::has, int>::type = 0>
+static inline bool Json2Object(TClass& aimObj, const Json::Value& jsonTypeValue)
+{
+    return false;
+}
+
+
+template <typename T>
+static bool Json2Object(std::vector<T>& aimObj, const Json::Value& jsonTypeValue)
+{
+    if (jsonTypeValue.isNull() || !jsonTypeValue.isArray()) {
+        return false;
+    } else {
+        
+        aimObj.clear();
+
+        bool result(true);
+
+        for (int i = 0; i < jsonTypeValue.size(); ++i) {
+            T item;
+            if (!Json2Object(item, jsonTypeValue[i])) {
+                result = false;
+            }
+            aimObj.push_back(item);
+        }
+
+        return result;
+    }
+}
+
+template <typename T>
+static bool JsonParse(const std::vector<std::string>& names, int index, const Json::Value& jsonTypeValue, T& arg)
+{
+    const auto key = names[index];
+    if (!jsonTypeValue.isMember(key) || Json2Object(arg, jsonTypeValue[key])) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+template <typename T, typename... Args>
+static bool JsonParse(const std::vector<std::string>& names, int index, const Json::Value& jsonTypeValue, T& arg, Args&... args)
+{
+    if (!JsonParse(names, index, jsonTypeValue, arg)) {
+        return false;
+    } else {
+        return JsonParse(names, index + 1, jsonTypeValue, args...);
+    }
+}
+
+/** Provider interface*/
+template<typename TClass>
+bool Json2Object(TClass& aimObj, const std::string& jsonTypeStr)
+{
+    Json::Reader reader;
+    Json::Value root;
+
+    if (!reader.parse(jsonTypeStr, root) || root.isNull()) {
+        return false;
+    }
+    return Json2Object(aimObj, root);
+}
+////////////////////////////////////////////////////////////////////////
+
+
+/** Provider interface*/
+template<typename T>
+bool Object2Json(std::string& jsonTypeStr, const T& obj)
+{
+    return false;
+}
+
+#define __func_1(func,member)   func(member);
+#define __func_2(func,member,...) __func_1(func,member)  __func_1(func,__VA_ARGS__)
+#define __func_3(func,member,...) __func_1(func,member)  __func_2(func,__VA_ARGS__)
+#define __func_4(func,member,...) __func_1(func,member)  __func_3(func,__VA_ARGS__)
+#define __func_5(func,member,...) __func_1(func,member)  __func_4(func,__VA_ARGS__)
+
+//eg: COUNT(a,b,c)  === 3
+#define COUNT(...) __count__(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define __count__(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+
+#define __macro_cat__(a,b)  a##b
+#define MACRO_CAT(a,b)  __macro_cat__(a,b)
+
+#define FOR_EACH(func,...) \
+    MACRO_CAT(__func_,COUNT(__VA_ARGS__))(func, __VA_ARGS__)
+
+#define JSON2OBJECT_EACH_FILED__(field)               \
+        if(!::Json2Object(field, jsonTypeValue[#field])){   \
+            result = false;                                                   \
+        }
+
+#endif //_SP_UTILITY_JSON_CONVERT_HELPER_

+ 7 - 2
Other/libRestfulFunc/test/CMakeLists.txt

@@ -6,8 +6,13 @@ file(GLOB ${MODULE_PREFIX}_TESTS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
 foreach(test ${${MODULE_PREFIX}_TESTS})
 	get_filename_component(test_name ${test} NAME_WE)
 	add_executable(${test_name} ${test})
-	target_include_directories(${test_name} PRIVATE ${CONAN_INCLUDE_DIRS_CATCH} ${OTHER_LIB_BASE_DIR}/libRestfulFunc)
-	target_link_libraries(${test_name} PRIVATE RestfulFunc)
+	target_include_directories(${test_name} PRIVATE
+		${CONAN_INCLUDE_DIRS_CATCH} 
+		${OTHER_LIB_BASE_DIR}/libRestfulFunc 
+		${CONAN_INCLUDE_DIRS_JSONCPP})
+	target_link_directories(${test_name} PRIVATE 
+		${CONAN_LIB_DIRS_JSONCPP})
+	target_link_libraries(${test_name} PRIVATE RestfulFunc ${CONAN_PKG_LIBS_JSONCPP})
 	set_property(TARGET ${test_name} PROPERTY FOLDER "test/other")
 	set_target_properties(${test_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
 	message(STATUS "add test case: ${test_name}...")

+ 61 - 0
Other/libRestfulFunc/test/testJsonConvert.cpp

@@ -0,0 +1,61 @@
+#define CATCH_CONFIG_MAIN
+#include <catch2.hpp>
+#include "JsonConvertHelper.hpp"
+
+TEST_CASE("test normal class", "[json]")
+{
+    struct DemoObjct
+    {
+        bool boolValue;
+        int intValue;
+        std::string strValue;
+
+        JSONCONVERT2OBJECT_MEMEBER_REGISTER(boolValue,intValue,strValue)
+    };
+
+    DemoObjct demoObj;
+    REQUIRE(Json2Object(demoObj, std::string("{\"boolValue\":true, \"intValue\":1234, \"strValue\":\"demo object!\"}")));
+    REQUIRE(demoObj.boolValue == true);
+    REQUIRE(demoObj.intValue == 1234);
+    REQUIRE(demoObj.strValue == "demo object!");
+}
+
+TEST_CASE("test normal space class", "[json]")
+{
+    struct DemoObjct
+    {
+        bool boolValue;
+        int intValue;
+        std::string strValue;
+
+        JSONCONVERT2OBJECT_MEMEBER_REGISTER(boolValue, intValue, strValue)
+    };
+
+    DemoObjct demoObj;
+    REQUIRE(Json2Object(demoObj, std::string("{\"boolValue\":true, \"intValue\":1234, \"strValue\":\"demo object!\"}")));
+    REQUIRE(demoObj.boolValue == true);
+    REQUIRE(demoObj.intValue == 1234);
+    REQUIRE(demoObj.strValue == "demo object!");
+}
+
+TEST_CASE("test rename class", "[json]")
+{
+    struct DemoObjct
+    {
+        bool boolValue;
+        int intValue;
+        std::string strValue;
+
+        JSONCONVERT2OBJECT_MEMEBER_REGISTER(boolValue, intValue, strValue)
+        JSONCONVERT2OBJECT_MEMEBER_RENAME_REGISTER("bValue", "iValue", "sValue")
+    };
+
+    DemoObjct demoObj;
+    REQUIRE(Json2Object(demoObj, std::string("{\"bValue\":true, \"iValue\":1234, \"sValue\":\"demo object!\"}")));
+    REQUIRE(demoObj.boolValue == true);
+    REQUIRE(demoObj.intValue == 1234);
+    REQUIRE(demoObj.strValue == "demo object!");
+}
+
+
+