Skip to content

Commit

Permalink
PROCESS JSON COMMAND - New Features
Browse files Browse the repository at this point in the history
The Operations were updated to:

0: GET
1: SET
2: GET LENGTH
3: GET KEYS
4: GET VAR TYPE

There are also new Flags:
 - Extract data from json path - similar to how stringvars extracts.
 - Prettify json - to make its output easier to read.

Update json_helper.cpp
  • Loading branch information
jetrotal committed Jan 23, 2025
1 parent e07493f commit b1c6177
Show file tree
Hide file tree
Showing 3 changed files with 332 additions and 40 deletions.
86 changes: 82 additions & 4 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5363,6 +5363,17 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
int target_var_id = ValueOrVariable(com.parameters[6], com.parameters[7]);

std::string json_path = ToString(CommandStringOrVariable(com, 8, 9));

int extract_data_from_string = com.parameters[10];
bool pretty_print = com.parameters[11] == 1;

if (extract_data_from_string == 1) { // as string
json_path = Game_Strings::Extract(json_path, false);
}
if (extract_data_from_string == 2) { // as hex
json_path = Game_Strings::Extract(json_path, true);
}

auto* json_data = Main_Data::game_strings->ParseJson(source_var_id);

if (!json_data) {
Expand All @@ -5377,7 +5388,8 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c

std::optional<std::string> result;

if (operation == 0) { // Get operation: Extract a value from JSON data
switch (operation) {
case 0: { // Get operation: Extract a value from JSON data
result = Json_Helper::GetValue(*json_data, json_path);

if (result) {
Expand All @@ -5392,12 +5404,13 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
Main_Data::game_strings->Asg({ target_var_id }, *result);
break;
default:
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", operation);
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", target_var_type);
return true;
}
}
break;
}
else if (operation == 1) { // Set operation: Update JSON data with a new value
case 1: { // Set operation: Update JSON data with a new value
std::string new_value;

switch (target_var_type) {
Expand All @@ -5420,11 +5433,76 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
if (result) {
Main_Data::game_strings->Asg({ source_var_id }, *result);
}
break;
}
case 2: { // GetLength operation
auto length = Json_Helper::GetLength(*json_data, json_path);
if (length) {
switch (target_var_type) {
case 0: // Switch
Main_Data::game_switches->Set(target_var_id, *length > 0);
break;
case 1: // Variable
Main_Data::game_variables->Set(target_var_id, static_cast<int>(*length));
break;
case 2: // String
Main_Data::game_strings->Asg({ target_var_id }, std::to_string(*length));
break;
}
}
break;
}
case 3: { // GetKeys operation
auto keys = Json_Helper::GetKeys(*json_data, json_path);
if (keys && target_var_type == 2) { // Keys can only be stored in strings
std::string keys_str;
for (size_t i = 0; i < keys->size(); ++i) {
if (i > 0) keys_str += ",";
keys_str += "\"" + (*keys)[i] + "\"";
}
Main_Data::game_strings->Asg({ target_var_id }, "{ \"keys\": [" + keys_str + "] }");
}
break;
}
else {
case 4: { // GetType operation
auto type = Json_Helper::GetType(*json_data, json_path);
if (type) {
int type_code = 0;
switch (target_var_type) {
case 0: // Switch
// For switches, true if it's an object or array (for backward compatibility)
Main_Data::game_switches->Set(target_var_id, *type == "object" || *type == "array");
break;
case 1: // Variable
// For variables, return a numeric code for the type:
// 1=object, 2=array, 3=string, 4=number, 5=boolean, 6=null
if (*type == "object") type_code = 1;
else if (*type == "array") type_code = 2;
else if (*type == "string") type_code = 3;
else if (*type == "number") type_code = 4;
else if (*type == "boolean") type_code = 5;
else if (*type == "null") type_code = 6;
Main_Data::game_variables->Set(target_var_id, type_code);
break;
case 2: // String
Main_Data::game_strings->Asg({ target_var_id }, *type);
break;
}
}
break;
}
default:
Output::Warning("CommandEasyRpgProcessJson: Invalid Operation {}", operation);
}

if (target_var_type == 2 && pretty_print == 1) { // Only works with strings
std::string target_str = ToString(Main_Data::game_strings->Get(target_var_id));
if (auto parsed_json = Json_Helper::Parse(target_str)) {
std::string formatted = Json_Helper::PrettyPrint(*parsed_json, 2);
Main_Data::game_strings->Asg({ target_var_id }, formatted);
}
}

return true;

#endif // !HAVE_NLOHMANN_JSON
Expand Down
195 changes: 163 additions & 32 deletions src/json_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,73 +28,204 @@
using json = nlohmann::json;

namespace {
std::string GetValueAsString(const json& json_obj) {
std::string result;

if (json_obj.is_string()) {
result = json_obj.get<std::string>();
std::string GetValueAsString(const json& json_obj) {
if (json_obj.is_null()) {
return "null";
}
else if (json_obj.is_number_integer()) {
result = std::to_string(json_obj.get<int>());
if (json_obj.is_string()) {
return json_obj.get<std::string>();
}
else if (json_obj.is_number_float()) {
result = std::to_string(json_obj.get<float>());
if (json_obj.is_number_integer()) {
return std::to_string(json_obj.get<int64_t>());
}
else if (json_obj.is_boolean()) {
result = json_obj.get<bool>() ? "true" : "false";
if (json_obj.is_number_float()) {
return std::to_string(json_obj.get<double>());
}
else {
result = json_obj.dump();
if (json_obj.is_boolean()) {
return json_obj.get<bool>() ? "true" : "false";
}

return result;
return json_obj.dump();
}
}

} // namespace

namespace Json_Helper {
std::optional<nlohmann::json> Parse(std::string_view json_data) {

std::optional<json> Parse(std::string_view json_data) {
json json_obj = json::parse(json_data, nullptr, false);
if (json_obj.is_discarded()) {
return {};
return {};
}

return json_obj;
}

std::optional<std::string> GetValue(nlohmann::json& json_obj, std::string_view json_path) {
json::json_pointer ptr((std::string(json_path)));

if (ptr.empty()) {
Output::Warning("JSON: Bad json pointer {}", json_path);
if (json_path.empty()) {
Output::Warning("JSON: Empty json pointer at: {}", json_path);
return {};
}

if (!json_obj.contains(ptr)) {
std::string path_str = std::string(json_path);
json::json_pointer ptr(path_str);
const json& value = json_obj[ptr];
if (value.is_null()) {
return "";
}

return GetValueAsString(json_obj[ptr]);
return GetValueAsString(value);
}

std::string SetValue(nlohmann::json& json_obj, std::string_view json_path, std::string_view value) {
json::json_pointer ptr((std::string(json_path)));

if (ptr.empty()) {
Output::Warning("JSON: Bad json pointer {}", json_path);
if (json_path.empty()) {
Output::Warning("JSON: Empty json pointer at: {}", json_path);
return {};
}

json obj_value = json::parse(value, nullptr, false);
std::string path_str = std::string(json_path);
json::json_pointer ptr(path_str);

json obj_value = json::parse(value, nullptr, false);
if (obj_value.is_discarded()) {
// If parsing fails, treat it as a string value
json_obj[ptr] = std::string(value);
} else {
}
else {
json_obj[ptr] = obj_value;
}

return json_obj.dump();
}
}

std::optional<size_t> GetLength(const nlohmann::json& json_obj, std::string_view json_path) {
if (json_path.empty()) {
Output::Warning("JSON: Empty json pointer at: {}", json_path);
return {};
}

std::string path_str = std::string(json_path);
json::json_pointer ptr(path_str);
const json& value = json_obj[ptr];
if (value.is_null()) {
return 0;
}

if (!value.is_array() && !value.is_object()) {
return 0;
}
return value.size();
}

std::optional<std::vector<std::string>> GetKeys(const nlohmann::json& json_obj, std::string_view json_path) {
if (json_path.empty()) {
Output::Warning("JSON: Empty json pointer at: {}", json_path);
return {};
}

std::string path_str = std::string(json_path);
json::json_pointer ptr(path_str);
const json& value = json_obj[ptr];
if (value.is_null()) {
return std::vector<std::string>();
}

std::vector<std::string> keys;

if (value.is_object()) {
for (const auto& item : value.items()) {
keys.push_back(item.key());
}
}
else if (value.is_array()) {
for (size_t i = 0; i < value.size(); ++i) {
keys.push_back(std::to_string(i));
}
}
return keys;
}

std::optional<bool> IsObject(const nlohmann::json& json_obj, std::string_view json_path) {
if (json_path.empty()) {
Output::Warning("JSON: Empty json pointer at: {}", json_path);
return {};
}

std::string path_str = std::string(json_path);
json::json_pointer ptr(path_str);
const json& value = json_obj[ptr];
if (value.is_null()) {
return false;
}
return value.is_object();
}

std::optional<bool> IsArray(const nlohmann::json& json_obj, std::string_view json_path) {
if (json_path.empty()) {
Output::Warning("JSON: Empty json pointer at: {}", json_path);
return {};
}

std::string path_str = std::string(json_path);
json::json_pointer ptr(path_str);
const json& value = json_obj[ptr];
if (value.is_null()) {
return false;
}
return value.is_array();
}

std::optional<std::string> GetType(const nlohmann::json& json_obj, std::string_view json_path) {
if (json_path.empty()) {
Output::Warning("JSON: Empty json pointer at: {}", json_path);
return {};
}

std::string path_str = std::string(json_path);
json::json_pointer ptr(path_str);
const json& value = json_obj[ptr];
if (value.is_null()) {
return std::string("null");
}

if (value.is_object()) return std::string("object");
if (value.is_array()) return std::string("array");
if (value.is_string()) return std::string("string");
if (value.is_number()) return std::string("number");
if (value.is_boolean()) return std::string("boolean");
if (value.is_null()) return std::string("null"); // Should not reach here, but for completeness
return std::string("unknown");
}

std::optional<std::string> GetPath(const nlohmann::json& json_obj, const nlohmann::json& search_value) {
std::function<std::optional<std::string>(const json&, const json&, const std::string&)> find_path;

find_path = [&find_path](const json& obj, const json& target, const std::string& current_path) -> std::optional<std::string> {
if (obj == target) {
return current_path;
}

if (obj.is_object()) {
for (const auto& item : obj.items()) {
auto path = find_path(item.value(), target, current_path + "/" + item.key());
if (path) return path;
}
}
else if (obj.is_array()) {
for (size_t i = 0; i < obj.size(); ++i) {
auto path = find_path(obj[i], target, current_path + "/" + std::to_string(i));
if (path) return path;
}
}
return std::nullopt;
};

auto path = find_path(json_obj, search_value, "");
return path.value_or("");
}

std::string PrettyPrint(const nlohmann::json& json_obj, int indent) {
return json_obj.dump(std::max(0, indent));
}

} // namespace Json_Helper

#endif // HAVE_NLOHMANN_JSON
Loading

0 comments on commit b1c6177

Please sign in to comment.