We use rapidjson instead of jsoncpp.
Signed-off-by: Changgyu Choi <changyu.choi@samsung.com>
PKG_CHECK_MODULES(DLOG_DEPS REQUIRED dlog)
PKG_CHECK_MODULES(GLIB_DEPS REQUIRED glib-2.0)
PKG_CHECK_MODULES(GMOCK_DEPS REQUIRED gmock)
-PKG_CHECK_MODULES(JSONCPP_DEPS REQUIRED jsoncpp)
PKG_CHECK_MODULES(PKGMGR_PARSER_DEPS REQUIRED pkgmgr-parser)
PKG_CHECK_MODULES(PKGMGR_INFO_DEPS REQUIRED pkgmgr-info)
PKG_CHECK_MODULES(PKGMGR_INSTALLER_DEPS REQUIRED pkgmgr-installer)
BuildRequires: pkgconfig(dlog)
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(gmock)
-BuildRequires: pkgconfig(jsoncpp)
BuildRequires: pkgconfig(pkgmgr-parser)
BuildRequires: pkgconfig(pkgmgr-info)
BuildRequires: pkgconfig(pkgmgr-installer)
APPLY_PKG_CONFIG(${TARGET_TIZEN_ACTION_COMMON} PUBLIC
DLOG_DEPS
- JSONCPP_DEPS
TIZEN_DATABASE_DEPS
)
#include "action_model.h"
-#include <jsoncpp/json/json.h>
-
#include <utility>
#include "common/utils/logging.hh"
+#include "common/utils/safe_json.hpp"
namespace common {
-namespace {
-
-Json::Value ParseJsonFromString(const std::string& json_str) {
- Json::Value root;
- Json::CharReaderBuilder builder;
- JSONCPP_STRING err;
- const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
- if (!reader->parse(json_str.c_str(), json_str.c_str() + json_str.length(),
- &root, &err)) {
- LOG(ERROR) << "Failed to parse json string: " << err;
- return {};
- }
- return root;
-}
-
-} // namespace
ActionModel::ActionModel(std::string json_str)
: json_str_(std::move(json_str)) {
- Json::Value root = ParseJsonFromString(json_str_);
- action_id_ = root["name"].asString();
- for (auto const& it : root["params"].getMemberNames()) {
- std::string key = it;
- std::string value = root["params"][key].asString();
- parameters_.emplace_back(std::move(key), std::move(value));
+ SafeJson root(json_str_);
+ action_id_ = root.get<std::string>("name");
+ for (auto& [key, item] : root.members("params")) {
+ std::string value = item->GetString();
+ parameters_.emplace_back(key, std::move(value));
}
}
#include "common/action_schema.h"
-#include <json/json.h>
-
-#include <string>
#include <map>
+#include <string>
#include "common/utils/logging.hh"
+#include "common/utils/safe_json.hpp"
namespace {
-Json::Value ParseJsonFromString(const std::string& json_str) {
- Json::Value root;
- Json::CharReaderBuilder builder;
- JSONCPP_STRING err;
- const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
- if (!reader->parse(json_str.c_str(), json_str.c_str() + json_str.length(),
- &root, &err)) {
- LOG(ERROR) << "Failed to parse json string: " << err;
- return {};
- }
- return root;
-}
-
std::map<std::string, common::ParameterType> kTypeMap = {
- {"integer", common::ParameterType::IntType},
- {"string", common::ParameterType::StringType},
- {"double", common::ParameterType::DoubleType},
- {"bool", common::ParameterType::BoolType},
- {"json", common::ParameterType::JsonType},
+ {"integer", common::ParameterType::IntType},
+ {"string", common::ParameterType::StringType},
+ {"double", common::ParameterType::DoubleType},
+ {"bool", common::ParameterType::BoolType},
+ {"json", common::ParameterType::JsonType},
};
common::ParameterType ConvertStringToParameterType(const std::string& type) {
ActionSchema::ActionSchema() {}
+#include <iostream>
ActionSchema::ActionSchema(const std::string& json_str) {
try {
- Json::Value root = ParseJsonFromString(json_str);
-
- action_id_ = root["name"].asString();
- appid_ = root["appId"].asString(); // TODO: Change to details::appId
- category_ = root["category"].asString();
- description_ = root["desc"].asString();
- type_ = GetActionTypeFromString(root["type"].asString());
-
+ SafeJson root(json_str);
+ action_id_ = root.get<std::string>("name");
+ appid_ = root.get<std::string>("appId"); // TODO: Change to details::appId
+ category_ = root.get<std::string>("category");
+ description_ = root.get<std::string>("desc");
+ type_ = GetActionTypeFromString(root.get<std::string>("type"));
// params
- for (auto const& it : root["params"].getMemberNames()) {
- std::string name = it;
- common::ParameterType type =
- ConvertStringToParameterType(root["params"][it]["type"].asString());
- std::string description = root["params"][it]["desc"].asString();
- bool is_required = root["params"][it]["isRequired"].asBool();
+ for (const auto& [name, item] : root.members("params")) {
+ common::ParameterType type = ConvertStringToParameterType(
+ SafeJson::get<std::string>(*item, "type"));
+ std::string description = SafeJson::get<std::string>(*item, "desc");
+ bool is_required = false;
+ try {
+ is_required = SafeJson::get<bool>(*item, "isRequired");
+ } catch (std::runtime_error&) {
+ is_required = false;
+ }
+
parameters_.emplace_back(name, type, "", description, is_required);
}
// returns
- for (auto const& it : root["returns"].getMemberNames()) {
- std::string name = it;
- common::ParameterType type =
- ConvertStringToParameterType(root["returns"][it]["type"].asString());
- std::string description = root["returns"][it]["desc"].asString();
+ for (const auto& [name, value] : root.members("returns")) {
+ common::ParameterType type = ConvertStringToParameterType(
+ SafeJson::get<std::string>(*value, "type"));
+ std::string description = SafeJson::get<std::string>(*value, "desc");
// ActionParameterType -> another class???
returns_.emplace_back(name, type, "", description, false);
}
- for (auto const& it : root["required-privileges"])
- privileges_.push_back(it.asString());
- } catch (const Json::Exception& e) {
+ for (auto const& it : root.array("required-privileges"))
+ privileges_.push_back(it->GetString());
+ } catch (...) {
// TODO: how to handle invalid json case?
- LOG(ERROR) << "Failed to parse json string: " << e.what();
+ LOG(ERROR) << "Failed to parse json string";
}
}
-ActionSchema::ActionSchema(std::string action_id, std::string appid,
- std::string category, std::string description,
- std::vector<ActionParameter> parameters,
- std::vector<std::string> privileges)
- : action_id_(std::move(action_id)), appid_(std::move(appid)),
- category_(std::move(category)), description_(std::move(description)),
- parameters_(std::move(parameters)), privileges_(std::move(privileges)) {
-}
+ActionSchema::ActionSchema(std::string action_id,
+ std::string appid,
+ std::string category,
+ std::string description,
+ std::vector<ActionParameter> parameters,
+ std::vector<std::string> privileges)
+ : action_id_(std::move(action_id)),
+ appid_(std::move(appid)),
+ category_(std::move(category)),
+ description_(std::move(description)),
+ parameters_(std::move(parameters)),
+ privileges_(std::move(privileges)) {}
const std::string& ActionSchema::GetActionId() const {
return action_id_;
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COMMON_UTILS_SAFE_JSON_HH_
+#define COMMON_UTILS_SAFE_JSON_HH_
+
+#include <rapidjson/document.h>
+#include <sstream>
+#include <stdexcept>
+#include <vector>
+
+namespace common {
+
+class SafeJson {
+ public:
+ SafeJson() = default;
+
+ SafeJson(const std::string& json) {
+ doc_.Parse(json.c_str());
+ if (doc_.HasParseError()) {
+ throw std::runtime_error("Invalid JSON format");
+ }
+ }
+
+ template <typename T>
+ T get(std::vector<std::string> path) const {
+ const rapidjson::Value* val = traverse(path);
+ return extract<T>(*val);
+ }
+
+ template <typename T>
+ T get(const std::string& dotPath) const {
+ std::vector<std::string> path = split(dotPath, '.');
+ return get<T>(std::move(path));
+ }
+
+ const rapidjson::Value& getRaw(std::vector<std::string> path) const {
+ return *traverse(path);
+ }
+
+ std::vector<const rapidjson::Value*> array(
+ std::vector<std::string> path) const {
+ const rapidjson::Value* val = traverse(path);
+ if (!val->IsArray()) {
+ throw std::runtime_error("Expected array at path");
+ }
+ std::vector<const rapidjson::Value*> result;
+ for (auto& v : val->GetArray()) {
+ result.push_back(&v);
+ }
+ return result;
+ }
+
+ std::vector<const rapidjson::Value*> array(const std::string& dotPath) const {
+ std::vector<std::string> path = split(dotPath, '.');
+ return array(std::move(path));
+ }
+
+ std::vector<std::pair<std::string, const rapidjson::Value*>> members(
+ std::vector<std::string> path) const {
+ const rapidjson::Value* val = traverse(path);
+ if (!val->IsObject()) {
+ throw std::runtime_error("Expected object for member iteration");
+ }
+
+ std::vector<std::pair<std::string, const rapidjson::Value*>> result;
+ for (auto it = val->MemberBegin(); it != val->MemberEnd(); ++it) {
+ result.emplace_back(it->name.GetString(), &it->value);
+ }
+ return result;
+ }
+
+ std::vector<std::pair<std::string, const rapidjson::Value*>> members(
+ const std::string& dotPath) const {
+ std::vector<std::string> path = split(dotPath, '.');
+ return members(std::move(path));
+ }
+
+ template <typename T>
+ static T get(const rapidjson::Value& node, std::vector<std::string> path) {
+ const rapidjson::Value* curr = &node;
+ for (const auto& key : path) {
+ if (!curr->IsObject() || !curr->HasMember(key.c_str())) {
+ throw std::runtime_error("Missing key: " + key);
+ }
+ curr = &(*curr)[key.c_str()];
+ }
+ return SafeJson().extract<T>(*curr);
+ }
+
+ template <typename T>
+ static T get(const rapidjson::Value& node, const std::string& dotPath) {
+ std::vector<std::string> path = SafeJson().split(dotPath, '.');
+ return get<T>(node, std::move(path));
+ }
+
+ private:
+ rapidjson::Document doc_;
+
+ const rapidjson::Value* traverse(std::vector<std::string> path) const {
+ const rapidjson::Value* curr = &doc_;
+ for (const auto& key : path) {
+ if (!curr->IsObject()) {
+ throw std::runtime_error("Expected object at " + key);
+ }
+ if (!curr->HasMember(key.c_str())) {
+ throw std::runtime_error("Missing key: " + key);
+ }
+ curr = &(*curr)[key.c_str()];
+ }
+ return curr;
+ }
+
+ template <typename T>
+ T extract(const rapidjson::Value& val) const;
+
+ std::vector<std::string> split(const std::string& s, char delim) const {
+ std::vector<std::string> elems;
+ std::stringstream ss(s);
+ std::string item;
+ while (std::getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+ return elems;
+ }
+};
+
+// νΉμν
+template <>
+inline std::string SafeJson::extract<std::string>(
+ const rapidjson::Value& val) const {
+ if (!val.IsString())
+ throw std::runtime_error("Type mismatch: expected string");
+ return val.GetString();
+}
+
+template <>
+inline int SafeJson::extract<int>(const rapidjson::Value& val) const {
+ if (!val.IsInt())
+ throw std::runtime_error("Type mismatch: expected int");
+ return val.GetInt();
+}
+
+template <>
+inline double SafeJson::extract<double>(const rapidjson::Value& val) const {
+ if (!val.IsNumber())
+ throw std::runtime_error("Type mismatch: expected number");
+ return val.GetDouble();
+}
+
+template <>
+inline bool SafeJson::extract<bool>(const rapidjson::Value& val) const {
+ if (!val.IsBool())
+ throw std::runtime_error("Type mismatch: expected bool");
+ return val.GetBool();
+}
+
+} // namespace common
+
+#endif // COMMON_UTILS_SAFE_JSON_HH_
APPLY_PKG_CONFIG(${TARGET_TIZEN_ACTION_PLUGIN} PUBLIC
DLOG_DEPS
GLIB_DEPS
- JSONCPP_DEPS
PKGMGR_INFO_DEPS
PKGMGR_INSTALLER_DEPS
PKGMGR_PARSER_DEPS
#include "pkgmgr_plugin_parser/json_action_schema_parser.hh"
-#include <json/json.h>
-
#include <algorithm>
#include <fstream>
#include <string>
#include <vector>
#include "common/utils/logging.hh"
+#include "common/utils/safe_json.hpp"
#include "pkgmgr_plugin_parser/action_schema.hh"
-namespace {
-
-std::string JsonToString(const Json::Value& val) {
- Json::StreamWriterBuilder builder;
- builder["indentation"] = "";
- const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
- return Json::writeString(builder, val);
-}
-
-}
-
namespace parser {
ActionSchema JsonActionSchemaParser::Parse(const std::string& pkgid,
- const std::string& path) {
- Json::CharReaderBuilder rbuilder;
- rbuilder["collectComments"] = false;
-
+ const std::string& path) {
std::ifstream ifs(path);
- Json::Value root;
- std::string error;
- if (!Json::parseFromStream(rbuilder, ifs, &root, &error)) {
- LOG(ERROR) << "Failed to read json file: " << error;
- return {};
- }
+ std::string json((std::istreambuf_iterator<char>(ifs)),
+ std::istreambuf_iterator<char>());
- // TODO
- std::string name = root["name"].asString();
- std::string category = root["category"].asString();
- const auto priv_array = root["required-privileges"];
+ common::SafeJson root(json);
+ std::string name = root.get<std::string>("name");
+ std::string category = root.get<std::string>("category");
+ const auto priv_array = root.array("required-privileges");
std::vector<std::string> privs;
privs.reserve(priv_array.size());
std::transform(priv_array.begin(), priv_array.end(),
- std::back_inserter(privs),
- [](const auto& priv) { return priv.asString(); });
- std::string json_str = JsonToString(root);
-
+ std::back_inserter(privs),
+ [](const auto& priv) { return priv->GetString(); });
LOG(DEBUG) << "Parased action for pkgid[ " << pkgid << " ] : " << name;
- LOG(DEBUG) << json_str;
+ LOG(DEBUG) << json;
return ActionSchema(std::move(pkgid), std::move(name), std::move(category),
- std::move(privs), std::move(json_str));
+ std::move(privs), std::move(json));
}
} // namespace parser
std::string json = R"({
"name": "test_action",
"params": {
- "param": "value",
+ "param": "value"
}
})";
"name": "new_action",
"params": {
"param1": "hello1",
- "param2": "hello2",
+ "param2": "hello2"
}
})");
CAPI_APPFW_APP_COMMON_DEPS
DLOG_DEPS
GLIB_DEPS
- JSONCPP_DEPS
PKGMGR_INFO_DEPS
PKGMGR_INSTALLER_DEPS
PKGMGR_PARSER_DEPS
#include "action_tool.hh"
#include "log_private.hh"
-#include <jsoncpp/json/json.h>
-
#include <memory>
#include <utility>
constexpr const char kStubAppId[] = "org.tizen.action-framework.service";
-// Json::Value ParseJsonFromString(const std::string& json_str) {
-// Json::Value root;
-// Json::CharReaderBuilder builder;
-// JSONCPP_STRING err;
-// const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
-// if (!reader->parse(json_str.c_str(), json_str.c_str() + json_str.length(),
-// &root, &err)) {
-// _E("Failed to parse json string");
-// return {};
-// }
-// return root;
-// }
-
} // namespace
namespace rpc = rpc_port::tizen_action_service_proxy;