2121
2222#include " output.h"
2323#include < nlohmann/json.hpp>
24- #include < sstream>
25- #include < unordered_map>
2624#include < charconv>
2725#include " string_view.h"
2826
@@ -47,6 +45,23 @@ namespace {
4745 return json_obj.dump ();
4846 }
4947
48+ // Helper to get a reference to the target json value, handling the root path case.
49+ template <typename JsonType>
50+ JsonType* GetJsonTarget (JsonType& json_obj, std::string_view json_path) {
51+ if (json_path == " /" ) {
52+ return &json_obj;
53+ }
54+
55+ std::string path_str (json_path);
56+ json::json_pointer ptr (path_str);
57+
58+ if (json_obj.contains (ptr)) {
59+ return &json_obj[ptr];
60+ }
61+
62+ return nullptr ;
63+ }
64+
5065} // namespace
5166
5267namespace Json_Helper {
@@ -74,99 +89,76 @@ namespace Json_Helper {
7489 }
7590
7691 std::string GetValue (json& json_obj, std::string_view json_path) {
77- std::string path_str = std::string (json_path);
78- json::json_pointer ptr (path_str);
79-
80- if (!json_obj.contains (ptr)) {
81- return {};
92+ if (auto * target = GetJsonTarget (json_obj, json_path); target) {
93+ return GetValueAsString (*target);
8294 }
83-
84- const json& value = json_obj[ptr];
85- auto val = GetValueAsString (value);
86- return val;
95+ return {};
8796 }
8897
89-
9098 std::string SetValue (json& json_obj, std::string_view json_path, std::string_view value) {
91- std::string path_str = std::string (json_path);
92- json::json_pointer ptr (path_str);
93-
9499 json obj_value = json::parse (value, nullptr , false );
95100 if (obj_value.is_discarded ()) {
96101 // If parsing fails, treat it as a string value
97- json_obj[ptr] = std::string (value);
102+ obj_value = std::string (value);
103+ }
104+
105+ if (json_path == " /" ) {
106+ json_obj = obj_value;
98107 }
99108 else {
109+ std::string path_str (json_path);
110+ json::json_pointer ptr (path_str);
100111 json_obj[ptr] = obj_value;
101112 }
102113
103114 return json_obj.dump ();
104115 }
105116
106117 size_t GetLength (const json& json_obj, std::string_view json_path) {
107- std::string path_str = std::string (json_path);
108- json::json_pointer ptr (path_str);
109-
110- if (!json_obj.contains (ptr)) {
111- return 0 ;
112- }
113-
114- const json& value = json_obj[ptr];
115- if (!value.is_array () && !value.is_object ()) {
116- return 0 ;
118+ if (auto * target = GetJsonTarget (json_obj, json_path); target) {
119+ if (target->is_array () || target->is_object ()) {
120+ return target->size ();
121+ }
117122 }
118-
119- return value.size ();
123+ return 0 ;
120124 }
121125
122126 std::vector<std::string> GetKeys (const json& json_obj, std::string_view json_path) {
123- std::string path_str = std::string (json_path);
124- json::json_pointer ptr (path_str);
125- if (!json_obj.contains (ptr)) {
126- return {};
127- }
128-
129- const json& value = json_obj[ptr];
130-
131127 std::vector<std::string> keys;
132-
133- if (value.is_object ()) {
134- for (const auto & item : value.items ()) {
135- keys.push_back (item.key ());
128+ if (auto * target = GetJsonTarget (json_obj, json_path); target) {
129+ if (target->is_object ()) {
130+ for (const auto & item : target->items ()) {
131+ keys.push_back (item.key ());
132+ }
136133 }
137- }
138- else if (value. is_array () ) {
139- for ( size_t i = 0 ; i < value. size (); ++i) {
140- keys. push_back ( std::to_string (i));
134+ else if (target-> is_array ()) {
135+ for ( size_t i = 0 ; i < target-> size (); ++i ) {
136+ keys. push_back ( std::to_string (i));
137+ }
141138 }
142139 }
143140 return keys;
144141 }
145142
146143 std::string GetType (const json& json_obj, std::string_view json_path) {
147- std::string path_str = std::string (json_path);
148- json::json_pointer ptr (path_str);
149- if (!json_obj.contains (ptr)) {
150- return {};
144+ if (const auto * value = GetJsonTarget (json_obj, json_path)) {
145+ if (value->is_object ()) return std::string (" object" );
146+ if (value->is_array ()) return std::string (" array" );
147+ if (value->is_string ()) return std::string (" string" );
148+ if (value->is_number ()) return std::string (" number" );
149+ if (value->is_boolean ()) return std::string (" boolean" );
150+ if (value->is_null ()) return std::string (" null" );
151+ return std::string (" unknown" );
151152 }
152-
153- const json& value = json_obj[ptr];
154-
155- if (value.is_object ()) return std::string (" object" );
156- if (value.is_array ()) return std::string (" array" );
157- if (value.is_string ()) return std::string (" string" );
158- if (value.is_number ()) return std::string (" number" );
159- if (value.is_boolean ()) return std::string (" boolean" );
160- if (value.is_null ()) return std::string (" null" );
161- return std::string (" unknown" );
153+ return {};
162154 }
163155
164156 std::string GetPath (const json& json_obj, const json& search_value) {
165157 std::function<std::string (const json&, const json&, const std::string&)> find_path;
166158
167159 find_path = [&find_path](const json& obj, const json& target, const std::string& current_path) -> std::string {
168160 if (obj == target) {
169- return current_path;
161+ return current_path. empty () ? " / " : current_path ;
170162 }
171163
172164 if (obj.is_object ()) {
@@ -192,32 +184,37 @@ namespace Json_Helper {
192184 }
193185
194186 std::string RemoveValue (json& json_obj, std::string_view json_path) {
195- std::string path_str = std::string (json_path);
187+ // Per user feedback, removing the root clears the object.
188+ if (json_path == " /" ) {
189+ // .clear() correctly handles both objects ({}) and arrays ([]).
190+ json_obj.clear ();
191+ return json_obj.dump ();
192+ }
193+
194+ std::string path_str (json_path);
196195 json::json_pointer ptr (path_str);
197196
198197 if (!json_obj.contains (ptr)) {
199198 return {};
200199 }
201200
202- // Get parent path and key/index to remove
203201 auto parent_ptr = ptr.parent_pointer ();
204-
205202 json& parent = json_obj[parent_ptr];
206- json_path. remove_prefix (parent_ptr. to_string (). size () + 1 );
203+ const std::string& key = ptr. back ( );
207204
208205 if (parent.is_object ()) {
209- parent.erase (std::string (json_path) );
206+ parent.erase (key );
210207 }
211208 else if (parent.is_array ()) {
212- // Check if key is a valid positive number
213209 unsigned index;
214- auto ec = std::from_chars (json_path .data (), json_path .data () + json_path .size (), index). ec ;
210+ auto [p, ec] = std::from_chars (key .data (), key .data () + key .size (), index);
215211 if (ec == std::errc ()) {
216212 if (index < parent.size ()) {
217213 parent.erase (index);
218214 }
219- } else {
220- Output::Warning (" JSON: Invalid array index at: {}" , json_path);
215+ }
216+ else {
217+ Output::Warning (" JSON: Invalid array index for removal at: {}" , json_path);
221218 return {};
222219 }
223220 }
@@ -226,53 +223,46 @@ namespace Json_Helper {
226223 }
227224
228225 std::string PushValue (json& json_obj, std::string_view json_path, std::string_view value) {
229- std::string path_str = std::string (json_path);
230- json::json_pointer ptr (path_str);
231-
232- if (!json_obj.contains (ptr)) {
233- return {};
234- }
226+ if (auto * target = GetJsonTarget (json_obj, json_path); target) {
227+ if (!target->is_array ()) {
228+ Output::Warning (" JSON: Path does not point to an array: {}" , json_path);
229+ return {};
230+ }
235231
236- json& array = json_obj[ptr];
237- if (!array.is_array ()) {
238- Output::Warning (" JSON: Path does not point to an array: {}" , json_path);
239- return {};
240- }
232+ json obj_value = json::parse (value, nullptr , false );
233+ if (obj_value.is_discarded ()) {
234+ // If parsing fails, treat it as a string value
235+ target->push_back (std::string (value));
236+ }
237+ else {
238+ target->push_back (obj_value);
239+ }
241240
242- json obj_value = json::parse (value, nullptr , false );
243- if (obj_value.is_discarded ()) {
244- // If parsing fails, treat it as a string value
245- array.push_back (std::string (value));
241+ return json_obj.dump ();
246242 }
247- else {
248- array.push_back (obj_value);
249- }
250-
251- return json_obj.dump ();
243+ return {};
252244 }
253245
254246 std::tuple<std::string, std::string> PopValue (json& json_obj, std::string_view json_path) {
255- std::string path_str = std::string (json_path);
256- json::json_pointer ptr (path_str);
247+ if (auto * target = GetJsonTarget (json_obj, json_path); target) {
248+ if (!target->is_array () || target->empty ()) {
249+ Output::Warning (" JSON: Path does not point to a non-empty array: {}" , json_path);
250+ return {};
251+ }
257252
258- if (!json_obj.contains (ptr)) {
259- return {};
260- }
253+ json popped = target->back ();
254+ target->erase (target->size () - 1 );
261255
262- json& array = json_obj[ptr];
263- if (!array.is_array () || array.empty ()) {
264- Output::Warning (" JSON: Path does not point to a non-empty array: {}" , json_path);
265- return {};
256+ return { json_obj.dump (), GetValueAsString (popped) };
266257 }
267-
268- json popped = array.back ();
269- array.erase (array.size () - 1 );
270-
271- return {json_obj.dump (), GetValueAsString (popped)};
258+ return {};
272259 }
273260
274261 bool Contains (const json& json_obj, std::string_view json_path) {
275- std::string path_str = std::string (json_path);
262+ if (json_path == " /" ) {
263+ return true ;
264+ }
265+ std::string path_str (json_path);
276266 json::json_pointer ptr (path_str);
277267
278268 return json_obj.contains (ptr);
0 commit comments