Skip to content

Commit badd8c6

Browse files
committed
Process JSON - Remove Value, Array Push, Array Pop, Contains and Lambda functions to reduce repetition.
1 parent 31f9cac commit badd8c6

File tree

3 files changed

+275
-63
lines changed

3 files changed

+275
-63
lines changed

src/game_interpreter.cpp

Lines changed: 99 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5357,6 +5357,40 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
53575357
return true;
53585358
#else
53595359

5360+
// Helper lambda for getting values from variables
5361+
auto get_var_value = [](int var_type, int var_id) -> std::string {
5362+
switch (var_type) {
5363+
case 0: // Switch
5364+
return std::to_string(Main_Data::game_switches->Get(var_id));
5365+
case 1: // Variable
5366+
return std::to_string(Main_Data::game_variables->Get(var_id));
5367+
case 2: // String
5368+
return ToString(Main_Data::game_strings->Get(var_id));
5369+
default:
5370+
Output::Warning("CommandEasyRpgProcessJson: Unsupported var_type {}", var_type);
5371+
return {};
5372+
}
5373+
};
5374+
5375+
// Helper lambda for setting values to variables
5376+
auto set_var_value = [](int var_type, int var_id, const std::string& value) -> bool {
5377+
switch (var_type) {
5378+
case 0: // Switch
5379+
Main_Data::game_switches->Set(var_id, atoi(value.c_str()) != 0);
5380+
break;
5381+
case 1: // Variable
5382+
Main_Data::game_variables->Set(var_id, atoi(value.c_str()));
5383+
break;
5384+
case 2: // String
5385+
Main_Data::game_strings->Asg({ var_id }, value);
5386+
break;
5387+
default:
5388+
Output::Warning("CommandEasyRpgProcessJson: Unsupported var_type {}", var_type);
5389+
return false;
5390+
}
5391+
return true;
5392+
};
5393+
53605394
int operation = ValueOrVariable(com.parameters[0], com.parameters[1]);
53615395
int source_var_id = ValueOrVariable(com.parameters[2], com.parameters[3]);
53625396
int target_var_type = ValueOrVariable(com.parameters[4], com.parameters[5]);
@@ -5391,103 +5425,106 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
53915425
switch (operation) {
53925426
case 0: { // Get operation: Extract a value from JSON data
53935427
result = Json_Helper::GetValue(*json_data, json_path);
5394-
53955428
if (result) {
5396-
switch (target_var_type) {
5397-
case 0: // Switch
5398-
Main_Data::game_switches->Set(target_var_id, atoi(result->c_str()) != 0);
5399-
break;
5400-
case 1: // Variable
5401-
Main_Data::game_variables->Set(target_var_id, atoi(result->c_str()));
5402-
break;
5403-
case 2: // String
5404-
Main_Data::game_strings->Asg({ target_var_id }, *result);
5405-
break;
5406-
default:
5407-
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", target_var_type);
5408-
return true;
5409-
}
5429+
set_var_value(target_var_type, target_var_id, *result);
54105430
}
54115431
break;
54125432
}
54135433
case 1: { // Set operation: Update JSON data with a new value
5414-
std::string new_value;
5415-
5416-
switch (target_var_type) {
5417-
case 0: // Switch
5418-
new_value = std::to_string(Main_Data::game_switches->Get(target_var_id));
5419-
break;
5420-
case 1: // Variable
5421-
new_value = std::to_string(Main_Data::game_variables->Get(target_var_id));
5422-
break;
5423-
case 2: // String
5424-
new_value = ToString(Main_Data::game_strings->Get(target_var_id));
5425-
break;
5426-
default:
5427-
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", operation);
5428-
return true;
5429-
}
5430-
5431-
result = Json_Helper::SetValue(*json_data, json_path, new_value);
5432-
5433-
if (result) {
5434-
Main_Data::game_strings->Asg({ source_var_id }, *result);
5434+
std::string new_value = get_var_value(target_var_type, target_var_id);
5435+
if (!new_value.empty()) {
5436+
result = Json_Helper::SetValue(*json_data, json_path, new_value);
5437+
if (result) {
5438+
Main_Data::game_strings->Asg({ source_var_id }, *result);
5439+
}
54355440
}
54365441
break;
54375442
}
54385443
case 2: { // GetLength operation
54395444
auto length = Json_Helper::GetLength(*json_data, json_path);
54405445
if (length) {
5441-
switch (target_var_type) {
5442-
case 0: // Switch
5443-
Main_Data::game_switches->Set(target_var_id, *length > 0);
5444-
break;
5445-
case 1: // Variable
5446-
Main_Data::game_variables->Set(target_var_id, static_cast<int>(*length));
5447-
break;
5448-
case 2: // String
5449-
Main_Data::game_strings->Asg({ target_var_id }, std::to_string(*length));
5450-
break;
5446+
std::string length_str;
5447+
if (target_var_type == 0) {
5448+
// For switches, true if length > 0
5449+
length_str = (*length > 0) ? "1" : "0";
54515450
}
5451+
else {
5452+
length_str = std::to_string(*length);
5453+
}
5454+
set_var_value(target_var_type, target_var_id, length_str);
54525455
}
54535456
break;
54545457
}
54555458
case 3: { // GetKeys operation
54565459
auto keys = Json_Helper::GetKeys(*json_data, json_path);
5457-
if (keys && target_var_type == 2) { // Keys can only be stored in strings
5460+
if (keys) {
54585461
std::string keys_str;
54595462
for (size_t i = 0; i < keys->size(); ++i) {
54605463
if (i > 0) keys_str += ",";
54615464
keys_str += "\"" + (*keys)[i] + "\"";
54625465
}
5463-
Main_Data::game_strings->Asg({ target_var_id }, "{ \"keys\": [" + keys_str + "] }");
5466+
std::string json_str = "{ \"keys\": [" + keys_str + "] }";
5467+
set_var_value(target_var_type, target_var_id, json_str);
54645468
}
54655469
break;
54665470
}
54675471
case 4: { // GetType operation
54685472
auto type = Json_Helper::GetType(*json_data, json_path);
54695473
if (type) {
5470-
int type_code = 0;
5471-
switch (target_var_type) {
5472-
case 0: // Switch
5473-
// For switches, true if it's an object or array (for backward compatibility)
5474-
Main_Data::game_switches->Set(target_var_id, *type == "object" || *type == "array");
5475-
break;
5476-
case 1: // Variable
5477-
// For variables, return a numeric code for the type:
5478-
// 1=object, 2=array, 3=string, 4=number, 5=boolean, 6=null
5474+
std::string value;
5475+
if (target_var_type == 0) {
5476+
// For switches, true if it's an object or array
5477+
value = (*type == "object" || *type == "array") ? "1" : "0";
5478+
}
5479+
else if (target_var_type == 1) {
5480+
// For variables, numeric code for type
5481+
int type_code = 0;
54795482
if (*type == "object") type_code = 1;
54805483
else if (*type == "array") type_code = 2;
54815484
else if (*type == "string") type_code = 3;
54825485
else if (*type == "number") type_code = 4;
54835486
else if (*type == "boolean") type_code = 5;
54845487
else if (*type == "null") type_code = 6;
5485-
Main_Data::game_variables->Set(target_var_id, type_code);
5486-
break;
5487-
case 2: // String
5488-
Main_Data::game_strings->Asg({ target_var_id }, *type);
5489-
break;
5488+
value = std::to_string(type_code);
5489+
}
5490+
else {
5491+
value = *type;
54905492
}
5493+
set_var_value(target_var_type, target_var_id, value);
5494+
}
5495+
break;
5496+
}
5497+
case 5: { // Remove operation: Remove value from JSON data
5498+
std::string result = Json_Helper::RemoveValue(*json_data, json_path);
5499+
if (!result.empty()) {
5500+
Main_Data::game_strings->Asg({ source_var_id }, result);
5501+
}
5502+
break;
5503+
}
5504+
case 6: { // Push operation: Add value to end of array
5505+
std::string value = get_var_value(target_var_type, target_var_id);
5506+
if (!value.empty()) {
5507+
std::string result = Json_Helper::PushValue(*json_data, json_path, value);
5508+
if (!result.empty()) {
5509+
Main_Data::game_strings->Asg({ source_var_id }, result);
5510+
}
5511+
}
5512+
break;
5513+
}
5514+
case 7: { // Pop operation: Remove and return last element of array
5515+
std::string result = Json_Helper::PopValue(*json_data, json_path);
5516+
if (!result.empty()) {
5517+
// Set popped value to target variable
5518+
set_var_value(target_var_type, target_var_id, result);
5519+
// Update source with modified JSON after pop
5520+
Main_Data::game_strings->Asg({ source_var_id }, json_data->dump());
5521+
}
5522+
break;
5523+
}
5524+
case 8: { // Contains operation: Check if path exists
5525+
auto exists = Json_Helper::Contains(*json_data, json_path);
5526+
if (exists) {
5527+
set_var_value(target_var_type, target_var_id, *exists ? "1" : "0");
54915528
}
54925529
break;
54935530
}

src/json_helper.cpp

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,148 @@ namespace Json_Helper {
253253
return json_obj.dump(std::max(0, indent));
254254
}
255255

256-
} // namespace Json_Helper
256+
std::string RemoveValue(nlohmann::ordered_json& json_obj, std::string_view json_path) {
257+
if (json_path.empty()) {
258+
Output::Warning("JSON: Empty json pointer at: {}", json_path);
259+
return {};
260+
}
261+
262+
std::string path_str = std::string(json_path);
263+
json::json_pointer ptr(path_str);
264+
265+
if (!json_obj.contains(ptr)) {
266+
return {};
267+
}
268+
269+
// Get parent path and key/index to remove
270+
std::string parent_path;
271+
std::string key;
272+
size_t last_slash = path_str.find_last_of('/');
273+
274+
if (last_slash != std::string::npos) {
275+
parent_path = path_str.substr(0, last_slash);
276+
key = path_str.substr(last_slash + 1);
277+
}
278+
else {
279+
// Top level removal
280+
json_obj.erase(ptr);
281+
return json_obj.dump();
282+
}
283+
284+
json::json_pointer parent_ptr(parent_path);
285+
json& parent = json_obj[parent_ptr];
286+
287+
if (parent.is_object()) {
288+
parent.erase(key);
289+
}
290+
else if (parent.is_array()) {
291+
// Check if key is a valid positive number
292+
if (!key.empty() && key.find_first_not_of("0123456789") == std::string::npos) {
293+
size_t index = 0;
294+
std::istringstream(key) >> index;
295+
if (index < parent.size()) {
296+
parent.erase(index);
297+
}
298+
}
299+
else {
300+
Output::Warning("JSON: Invalid array index at: {}", json_path);
301+
return {};
302+
}
303+
}
304+
305+
return json_obj.dump();
306+
}
307+
308+
std::string PushValue(nlohmann::ordered_json& json_obj, std::string_view json_path, std::string_view value) {
309+
if (json_path.empty()) {
310+
Output::Warning("JSON: Empty json pointer at: {}", json_path);
311+
return {};
312+
}
313+
314+
std::string path_str = std::string(json_path);
315+
json::json_pointer ptr(path_str);
316+
317+
if (!json_obj.contains(ptr)) {
318+
return {};
319+
}
320+
321+
json& array = json_obj[ptr];
322+
if (!array.is_array()) {
323+
Output::Warning("JSON: Path does not point to an array: {}", json_path);
324+
return {};
325+
}
326+
327+
json obj_value = json::parse(value, nullptr, false);
328+
if (obj_value.is_discarded()) {
329+
// If parsing fails, treat it as a string value
330+
array.push_back(std::string(value));
331+
}
332+
else {
333+
array.push_back(obj_value);
334+
}
335+
336+
return json_obj.dump();
337+
}
338+
339+
std::string PopValue(nlohmann::ordered_json& json_obj, std::string_view json_path) {
340+
if (json_path.empty()) {
341+
Output::Warning("JSON: Empty json pointer at: {}", json_path);
342+
return {};
343+
}
344+
345+
std::string path_str = std::string(json_path);
346+
json::json_pointer ptr(path_str);
347+
348+
if (!json_obj.contains(ptr)) {
349+
return {};
350+
}
351+
352+
json& array = json_obj[ptr];
353+
if (!array.is_array() || array.empty()) {
354+
Output::Warning("JSON: Path does not point to a non-empty array: {}", json_path);
355+
return {};
356+
}
357+
358+
json popped = array[array.size() - 1];
359+
array.erase(array.size() - 1);
360+
361+
return GetValueAsString(popped);
362+
}
363+
364+
std::optional<bool> Contains(const nlohmann::ordered_json& json_obj, std::string_view json_path) {
365+
if (json_path.empty()) {
366+
Output::Warning("JSON: Empty json pointer at: {}", json_path);
367+
return {};
368+
}
369+
370+
// Validate JSON pointer syntax (must start with / and contain valid tokens)
371+
std::string path_str = std::string(json_path);
372+
if (path_str[0] != '/') {
373+
Output::Warning("JSON: Invalid pointer syntax - must start with /: {}", json_path);
374+
return {};
375+
}
376+
377+
// Split path and validate each token
378+
std::string::size_type start = 1;
379+
std::string::size_type pos;
380+
while ((pos = path_str.find('/', start)) != std::string::npos) {
381+
if (pos == start) {
382+
Output::Warning("JSON: Invalid pointer syntax - empty reference token: {}", json_path);
383+
return {};
384+
}
385+
start = pos + 1;
386+
}
387+
388+
// Last token check
389+
if (start < path_str.length() && path_str.back() == '/') {
390+
Output::Warning("JSON: Invalid pointer syntax - trailing slash: {}", json_path);
391+
return {};
392+
}
393+
394+
json::json_pointer ptr(path_str);
395+
return json_obj.contains(ptr);
396+
}
397+
398+
} // namespace Json_Helper
257399

258400
#endif // HAVE_NLOHMANN_JSON

src/json_helper.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,39 @@ std::optional<std::string> GetPath(const nlohmann::ordered_json& json_obj, const
113113
*/
114114
std::string PrettyPrint(const nlohmann::ordered_json& json_obj, int indent = 2);
115115

116+
/**
117+
* Removes a value at the specified path from a JSON object
118+
* @param json_obj The JSON object to modify
119+
* @param json_path The JSON pointer path to the value to remove
120+
* @return The modified JSON object as a string, or empty string if path is invalid
121+
*/
122+
std::string RemoveValue(nlohmann::ordered_json& json_obj, std::string_view json_path);
123+
124+
/**
125+
* Pushes a value to the end of an array at the specified path
126+
* @param json_obj The JSON object containing the array
127+
* @param json_path The JSON pointer path to the array
128+
* @param value The value to push (will be parsed as JSON if valid)
129+
* @return The modified JSON object as a string, or empty string if path is invalid or not an array
130+
*/
131+
std::string PushValue(nlohmann::ordered_json& json_obj, std::string_view json_path, std::string_view value);
132+
133+
/**
134+
* Removes and returns the last element from an array at the specified path
135+
* @param json_obj The JSON object containing the array
136+
* @param json_path The JSON pointer path to the array
137+
* @return The popped value as a string, or empty string if path is invalid or array is empty
138+
*/
139+
std::string PopValue(nlohmann::ordered_json& json_obj, std::string_view json_path);
140+
141+
/**
142+
* Checks if a key or array index exists at the specified path
143+
* @param json_obj The JSON object to check
144+
* @param json_path The JSON pointer path to check
145+
* @return true if exists, false if not, empty if parent path is invalid
146+
*/
147+
std::optional<bool> Contains(const nlohmann::ordered_json& json_obj, std::string_view json_path);
148+
116149
} // namespace Json_Helper
117150

118151
#endif // HAVE_NLOHMANN_JSON

0 commit comments

Comments
 (0)