@@ -47,6 +47,23 @@ namespace {
4747 return json_obj.dump ();
4848 }
4949
50+ // Helper to get a reference to the target json value, handling the root path case.
51+ template <typename JsonType>
52+ auto GetJsonTarget (JsonType& json_obj, std::string_view json_path) -> decltype(&json_obj) {
53+ if (json_path == " /" ) {
54+ return &json_obj;
55+ }
56+
57+ std::string path_str (json_path);
58+ json::json_pointer ptr (path_str);
59+
60+ if (json_obj.contains (ptr)) {
61+ return &json_obj[ptr];
62+ }
63+
64+ return nullptr ;
65+ }
66+
5067} // namespace
5168
5269namespace Json_Helper {
@@ -74,99 +91,75 @@ namespace Json_Helper {
7491 }
7592
7693 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 {};
94+ if (auto * target = GetJsonTarget (json_obj, json_path)) {
95+ return GetValueAsString (*target);
8296 }
83-
84- const json& value = json_obj[ptr];
85- auto val = GetValueAsString (value);
86- return val;
97+ return {};
8798 }
8899
89-
90100 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-
94101 json obj_value = json::parse (value, nullptr , false );
95102 if (obj_value.is_discarded ()) {
96- // If parsing fails, treat it as a string value
97- json_obj[ptr] = std::string (value);
103+ obj_value = std::string (value);
104+ }
105+
106+ if (json_path == " /" ) {
107+ json_obj = obj_value;
98108 }
99109 else {
110+ std::string path_str (json_path);
111+ json::json_pointer ptr (path_str);
100112 json_obj[ptr] = obj_value;
101113 }
102114
103115 return json_obj.dump ();
104116 }
105117
106118 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 ;
119+ if (const auto * target = GetJsonTarget (json_obj, json_path)) {
120+ if (target->is_array () || target->is_object ()) {
121+ return target->size ();
122+ }
117123 }
118-
119- return value.size ();
124+ return 0 ;
120125 }
121126
122127 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-
131128 std::vector<std::string> keys;
132-
133- if (value.is_object ()) {
134- for (const auto & item : value.items ()) {
135- keys.push_back (item.key ());
129+ if (const auto * target = GetJsonTarget (json_obj, json_path)) {
130+ if (target->is_object ()) {
131+ for (const auto & item : target->items ()) {
132+ keys.push_back (item.key ());
133+ }
136134 }
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));
135+ else if (target-> is_array ()) {
136+ for ( size_t i = 0 ; i < target-> size (); ++i ) {
137+ keys. push_back ( std::to_string (i));
138+ }
141139 }
142140 }
143141 return keys;
144142 }
145143
146144 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 {};
145+ if (const auto * value = GetJsonTarget (json_obj, json_path)) {
146+ if (value->is_object ()) return std::string (" object" );
147+ if (value->is_array ()) return std::string (" array" );
148+ if (value->is_string ()) return std::string (" string" );
149+ if (value->is_number ()) return std::string (" number" );
150+ if (value->is_boolean ()) return std::string (" boolean" );
151+ if (value->is_null ()) return std::string (" null" );
152+ return std::string (" unknown" );
151153 }
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" );
154+ return {};
162155 }
163156
164157 std::string GetPath (const json& json_obj, const json& search_value) {
165158 std::function<std::string (const json&, const json&, const std::string&)> find_path;
166159
167160 find_path = [&find_path](const json& obj, const json& target, const std::string& current_path) -> std::string {
168161 if (obj == target) {
169- return current_path;
162+ return current_path. empty () ? " / " : current_path ;
170163 }
171164
172165 if (obj.is_object ()) {
@@ -192,32 +185,35 @@ namespace Json_Helper {
192185 }
193186
194187 std::string RemoveValue (json& json_obj, std::string_view json_path) {
195- std::string path_str = std::string (json_path);
188+ // Per user feedback, removing the root clears the object.
189+ if (json_path == " /" ) {
190+ // .clear() correctly handles both objects ({}) and arrays ([]).
191+ json_obj.clear ();
192+ return json_obj.dump ();
193+ }
194+
195+ std::string path_str (json_path);
196196 json::json_pointer ptr (path_str);
197197
198198 if (!json_obj.contains (ptr)) {
199199 return {};
200200 }
201201
202- // Get parent path and key/index to remove
203202 auto parent_ptr = ptr.parent_pointer ();
204-
205203 json& parent = json_obj[parent_ptr];
206- json_path. remove_prefix (parent_ptr. to_string (). size () + 1 );
204+ const std::string& key = ptr. back ( );
207205
208206 if (parent.is_object ()) {
209- parent.erase (std::string (json_path) );
207+ parent.erase (key );
210208 }
211209 else if (parent.is_array ()) {
212- // Check if key is a valid positive number
213210 unsigned index;
214- auto ec = std::from_chars (json_path.data (), json_path.data () + json_path.size (), index).ec ;
215- if (ec == std::errc ()) {
216- if (index < parent.size ()) {
217- parent.erase (index);
218- }
219- } else {
220- Output::Warning (" JSON: Invalid array index at: {}" , json_path);
211+ auto [p, ec] = std::from_chars (key.data (), key.data () + key.size (), index);
212+ if (ec == std::errc () && index < parent.size ()) {
213+ parent.erase (index);
214+ }
215+ else {
216+ Output::Warning (" JSON: Invalid array index for removal at: {}" , json_path);
221217 return {};
222218 }
223219 }
@@ -226,53 +222,45 @@ namespace Json_Helper {
226222 }
227223
228224 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- }
225+ if (auto * target = GetJsonTarget (json_obj, json_path)) {
226+ if (!target->is_array ()) {
227+ Output::Warning (" JSON: Path does not point to an array: {}" , json_path);
228+ return {};
229+ }
235230
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- }
231+ json obj_value = json::parse (value, nullptr , false );
232+ if (obj_value.is_discarded ()) {
233+ target->push_back (std::string (value));
234+ }
235+ else {
236+ target->push_back (obj_value);
237+ }
241238
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));
239+ return json_obj.dump ();
246240 }
247- else {
248- array.push_back (obj_value);
249- }
250-
251- return json_obj.dump ();
241+ return {};
252242 }
253243
254244 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);
245+ if (auto * target = GetJsonTarget (json_obj, json_path)) {
246+ if (!target->is_array () || target->empty ()) {
247+ Output::Warning (" JSON: Path does not point to a non-empty array: {}" , json_path);
248+ return {};
249+ }
257250
258- if (!json_obj.contains (ptr)) {
259- return {};
260- }
251+ json popped = target->back ();
252+ target->erase (target->size () - 1 );
261253
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 {};
254+ return { json_obj.dump (), GetValueAsString (popped) };
266255 }
267-
268- json popped = array.back ();
269- array.erase (array.size () - 1 );
270-
271- return {json_obj.dump (), GetValueAsString (popped)};
256+ return {};
272257 }
273258
274259 bool Contains (const json& json_obj, std::string_view json_path) {
275- std::string path_str = std::string (json_path);
260+ if (json_path == " /" ) {
261+ return true ;
262+ }
263+ std::string path_str (json_path);
276264 json::json_pointer ptr (path_str);
277265
278266 return json_obj.contains (ptr);
0 commit comments