Skip to content

Commit

Permalink
Process JSON - Remove Value, Array Push, Array Pop, Contains and Lamb…
Browse files Browse the repository at this point in the history
…da functions to reduce repetition.

PROCESS JSON -
  • Loading branch information
jetrotal committed Feb 3, 2025
1 parent 31f9cac commit 5782f85
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 63 deletions.
158 changes: 96 additions & 62 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5357,6 +5357,40 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
return true;
#else

// Helper lambda for getting values from variables
auto get_var_value = [](int var_type, int var_id) -> std::string {
switch (var_type) {
case 0: // Switch
return std::to_string(Main_Data::game_switches->Get(var_id));
case 1: // Variable
return std::to_string(Main_Data::game_variables->Get(var_id));
case 2: // String
return ToString(Main_Data::game_strings->Get(var_id));
default:
Output::Warning("CommandEasyRpgProcessJson: Unsupported var_type {}", var_type);
return {};
}
};

// Helper lambda for setting values to variables
auto set_var_value = [](int var_type, int var_id, const std::string& value) -> bool {
switch (var_type) {
case 0: // Switch
Main_Data::game_switches->Set(var_id, atoi(value.c_str()) != 0);
break;
case 1: // Variable
Main_Data::game_variables->Set(var_id, atoi(value.c_str()));
break;
case 2: // String
Main_Data::game_strings->Asg({ var_id }, value);
break;
default:
Output::Warning("CommandEasyRpgProcessJson: Unsupported var_type {}", var_type);
return false;
}
return true;
};

int operation = ValueOrVariable(com.parameters[0], com.parameters[1]);
int source_var_id = ValueOrVariable(com.parameters[2], com.parameters[3]);
int target_var_type = ValueOrVariable(com.parameters[4], com.parameters[5]);
Expand Down Expand Up @@ -5391,103 +5425,103 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
switch (operation) {
case 0: { // Get operation: Extract a value from JSON data
result = Json_Helper::GetValue(*json_data, json_path);

if (result) {
switch (target_var_type) {
case 0: // Switch
Main_Data::game_switches->Set(target_var_id, atoi(result->c_str()) != 0);
break;
case 1: // Variable
Main_Data::game_variables->Set(target_var_id, atoi(result->c_str()));
break;
case 2: // String
Main_Data::game_strings->Asg({ target_var_id }, *result);
break;
default:
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", target_var_type);
return true;
}
set_var_value(target_var_type, target_var_id, *result);
}
break;
}
case 1: { // Set operation: Update JSON data with a new value
std::string new_value;

switch (target_var_type) {
case 0: // Switch
new_value = std::to_string(Main_Data::game_switches->Get(target_var_id));
break;
case 1: // Variable
new_value = std::to_string(Main_Data::game_variables->Get(target_var_id));
break;
case 2: // String
new_value = ToString(Main_Data::game_strings->Get(target_var_id));
break;
default:
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", operation);
return true;
}

result = Json_Helper::SetValue(*json_data, json_path, new_value);

if (result) {
Main_Data::game_strings->Asg({ source_var_id }, *result);
std::string new_value = get_var_value(target_var_type, target_var_id);
if (!new_value.empty()) {
result = Json_Helper::SetValue(*json_data, json_path, new_value);
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;
std::string length_str;
if (target_var_type == 0) {
// For switches, true if length > 0
length_str = (*length > 0) ? "1" : "0";
}
else {
length_str = std::to_string(*length);
}
set_var_value(target_var_type, target_var_id, length_str);
}
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
if (keys) {
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 + "] }");
std::string json_str = "{ \"keys\": [" + keys_str + "] }";
set_var_value(target_var_type, target_var_id, json_str);
}
break;
}
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
std::string value;
if (target_var_type == 0) {
// For switches, true if it's an object or array
value = (*type == "object" || *type == "array") ? "1" : "0";
}
else if (target_var_type == 1) {
// For variables, numeric code for type
int type_code = 0;
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;
value = std::to_string(type_code);
}
else {
value = *type;
}
set_var_value(target_var_type, target_var_id, value);
}
break;
}
case 5: { // Remove operation: Remove value from JSON data
std::string result = Json_Helper::RemoveValue(*json_data, json_path);
if (!result.empty()) {
Main_Data::game_strings->Asg({ source_var_id }, result);
}
break;
}
case 6: { // Push operation: Add value to end of array
std::string value = get_var_value(target_var_type, target_var_id);
if (!value.empty()) {
std::string result = Json_Helper::PushValue(*json_data, json_path, value);
if (!result.empty()) {
Main_Data::game_strings->Asg({ source_var_id }, result);
}
}
break;
}
case 7: { // Pop operation: Remove and return last element of array
std::string result = Json_Helper::PopValue(*json_data, json_path);
if (!result.empty()) {
set_var_value(target_var_type, target_var_id, result);
}
break;
}
case 8: { // Contains operation: Check if path exists
auto exists = Json_Helper::Contains(*json_data, json_path);
if (exists) {
set_var_value(target_var_type, target_var_id, *exists ? "1" : "0");
}
break;
}
Expand Down
144 changes: 143 additions & 1 deletion src/json_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,148 @@ namespace Json_Helper {
return json_obj.dump(std::max(0, indent));
}

} // namespace Json_Helper
std::string RemoveValue(nlohmann::ordered_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);

if (!json_obj.contains(ptr)) {
return {};
}

// Get parent path and key/index to remove
std::string parent_path;
std::string key;
size_t last_slash = path_str.find_last_of('/');

if (last_slash != std::string::npos) {
parent_path = path_str.substr(0, last_slash);
key = path_str.substr(last_slash + 1);
}
else {
// Top level removal
json_obj.erase(ptr);
return json_obj.dump();
}

json::json_pointer parent_ptr(parent_path);
json& parent = json_obj[parent_ptr];

if (parent.is_object()) {
parent.erase(key);
}
else if (parent.is_array()) {
// Check if key is a valid positive number
if (!key.empty() && key.find_first_not_of("0123456789") == std::string::npos) {
size_t index = 0;
std::istringstream(key) >> index;
if (index < parent.size()) {
parent.erase(index);
}
}
else {
Output::Warning("JSON: Invalid array index at: {}", json_path);
return {};
}
}

return json_obj.dump();
}

std::string PushValue(nlohmann::ordered_json& json_obj, std::string_view json_path, std::string_view value) {
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);

if (!json_obj.contains(ptr)) {
return {};
}

json& array = json_obj[ptr];
if (!array.is_array()) {
Output::Warning("JSON: Path does not point to an array: {}", json_path);
return {};
}

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

return json_obj.dump();
}

std::string PopValue(nlohmann::ordered_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);

if (!json_obj.contains(ptr)) {
return {};
}

json& array = json_obj[ptr];
if (!array.is_array() || array.empty()) {
Output::Warning("JSON: Path does not point to a non-empty array: {}", json_path);
return {};
}

json popped = array[array.size() - 1];
array.erase(array.size() - 1);

return GetValueAsString(popped);
}

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

// Validate JSON pointer syntax (must start with / and contain valid tokens)
std::string path_str = std::string(json_path);
if (path_str[0] != '/') {
Output::Warning("JSON: Invalid pointer syntax - must start with /: {}", json_path);
return {};
}

// Split path and validate each token
std::string::size_type start = 1;
std::string::size_type pos;
while ((pos = path_str.find('/', start)) != std::string::npos) {
if (pos == start) {
Output::Warning("JSON: Invalid pointer syntax - empty reference token: {}", json_path);
return {};
}
start = pos + 1;
}

// Last token check
if (start < path_str.length() && path_str.back() == '/') {
Output::Warning("JSON: Invalid pointer syntax - trailing slash: {}", json_path);
return {};
}

json::json_pointer ptr(path_str);
return json_obj.contains(ptr);
}

} // namespace Json_Helper

#endif // HAVE_NLOHMANN_JSON
33 changes: 33 additions & 0 deletions src/json_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,39 @@ std::optional<std::string> GetPath(const nlohmann::ordered_json& json_obj, const
*/
std::string PrettyPrint(const nlohmann::ordered_json& json_obj, int indent = 2);

/**
* Removes a value at the specified path from a JSON object
* @param json_obj The JSON object to modify
* @param json_path The JSON pointer path to the value to remove
* @return The modified JSON object as a string, or empty string if path is invalid
*/
std::string RemoveValue(nlohmann::ordered_json& json_obj, std::string_view json_path);

/**
* Pushes a value to the end of an array at the specified path
* @param json_obj The JSON object containing the array
* @param json_path The JSON pointer path to the array
* @param value The value to push (will be parsed as JSON if valid)
* @return The modified JSON object as a string, or empty string if path is invalid or not an array
*/
std::string PushValue(nlohmann::ordered_json& json_obj, std::string_view json_path, std::string_view value);

/**
* Removes and returns the last element from an array at the specified path
* @param json_obj The JSON object containing the array
* @param json_path The JSON pointer path to the array
* @return The popped value as a string, or empty string if path is invalid or array is empty
*/
std::string PopValue(nlohmann::ordered_json& json_obj, std::string_view json_path);

/**
* Checks if a key or array index exists at the specified path
* @param json_obj The JSON object to check
* @param json_path The JSON pointer path to check
* @return true if exists, false if not, empty if parent path is invalid
*/
std::optional<bool> Contains(const nlohmann::ordered_json& json_obj, std::string_view json_path);

} // namespace Json_Helper

#endif // HAVE_NLOHMANN_JSON
Expand Down

0 comments on commit 5782f85

Please # to comment.