/src/jsoncons/include/jsoncons_ext/jsonpointer/jsonpointer.hpp
Line | Count | Source |
1 | | // Copyright 2013-2025 Daniel Parker |
2 | | // Distributed under the Boost license, Version 1.0. |
3 | | // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
4 | | |
5 | | // See https://github.com/danielaparker/jsoncons for latest version |
6 | | |
7 | | #ifndef JSONCONS_EXT_JSONPOINTER_JSONPOINTER_HPP |
8 | | #define JSONCONS_EXT_JSONPOINTER_JSONPOINTER_HPP |
9 | | |
10 | | #include <cstddef> |
11 | | #include <memory> |
12 | | #include <ostream> |
13 | | #include <string> |
14 | | #include <system_error> // system_error |
15 | | #include <type_traits> // std::enable_if, std::true_type |
16 | | #include <utility> // std::move |
17 | | #include <vector> |
18 | | |
19 | | #include <jsoncons/utility/write_number.hpp> |
20 | | #include <jsoncons/json_type.hpp> |
21 | | #include <jsoncons/utility/more_type_traits.hpp> |
22 | | |
23 | | #include <jsoncons_ext/jsonpointer/jsonpointer_error.hpp> |
24 | | |
25 | | namespace jsoncons { |
26 | | namespace jsonpointer { |
27 | | |
28 | | namespace detail { |
29 | | |
30 | | enum class pointer_state |
31 | | { |
32 | | start, |
33 | | escaped, |
34 | | new_token, |
35 | | part |
36 | | }; |
37 | | |
38 | | } // namespace detail |
39 | | |
40 | | template <typename CharT,typename Allocator=std::allocator<CharT>> |
41 | | std::basic_string<CharT,std::char_traits<CharT>,Allocator> escape(jsoncons::basic_string_view<CharT> s, const Allocator& = Allocator()) |
42 | 0 | { |
43 | 0 | std::basic_string<CharT,std::char_traits<CharT>,Allocator> result; |
44 | |
|
45 | 0 | for (auto c : s) |
46 | 0 | { |
47 | 0 | if (JSONCONS_UNLIKELY(c == '~')) |
48 | 0 | { |
49 | 0 | result.push_back('~'); |
50 | 0 | result.push_back('0'); |
51 | 0 | } |
52 | 0 | else if (JSONCONS_UNLIKELY(c == '/')) |
53 | 0 | { |
54 | 0 | result.push_back('~'); |
55 | 0 | result.push_back('1'); |
56 | 0 | } |
57 | 0 | else |
58 | 0 | { |
59 | 0 | result.push_back(c); |
60 | 0 | } |
61 | 0 | } |
62 | 0 | return result; |
63 | 0 | } |
64 | | |
65 | | template <typename CharT> |
66 | | std::basic_string<CharT> escape_string(const std::basic_string<CharT>& s) |
67 | | { |
68 | | std::basic_string<CharT> result; |
69 | | for (auto c : s) |
70 | | { |
71 | | switch (c) |
72 | | { |
73 | | case '~': |
74 | | result.push_back('~'); |
75 | | result.push_back('0'); |
76 | | break; |
77 | | case '/': |
78 | | result.push_back('~'); |
79 | | result.push_back('1'); |
80 | | break; |
81 | | default: |
82 | | result.push_back(c); |
83 | | break; |
84 | | } |
85 | | } |
86 | | return result; |
87 | | } |
88 | | |
89 | | // basic_json_pointer |
90 | | |
91 | | template <typename CharT> |
92 | | class basic_json_pointer |
93 | | { |
94 | | public: |
95 | | // Member types |
96 | | using char_type = CharT; |
97 | | using string_type = std::basic_string<char_type>; |
98 | | using string_view_type = jsoncons::basic_string_view<char_type>; |
99 | | using const_iterator = typename std::vector<string_type>::const_iterator; |
100 | | using iterator = const_iterator; |
101 | | using const_reverse_iterator = typename std::vector<string_type>::const_reverse_iterator; |
102 | | using reverse_iterator = const_reverse_iterator; |
103 | | private: |
104 | | std::vector<string_type> tokens_; |
105 | | public: |
106 | | // Constructors |
107 | | basic_json_pointer() |
108 | | { |
109 | | } |
110 | | |
111 | | basic_json_pointer(const std::vector<string_type>& tokens) |
112 | | : tokens_(tokens) |
113 | | { |
114 | | } |
115 | | |
116 | | basic_json_pointer(std::vector<string_type>&& tokens) |
117 | | : tokens_(std::move(tokens)) |
118 | | { |
119 | | } |
120 | | |
121 | | explicit basic_json_pointer(const string_view_type& s) |
122 | | { |
123 | | std::error_code ec; |
124 | | auto jp = parse(s, ec); |
125 | | if (JSONCONS_UNLIKELY(ec)) |
126 | | { |
127 | | JSONCONS_THROW(jsonpointer_error(ec)); |
128 | | } |
129 | | tokens_ = std::move(jp.tokens_); |
130 | | } |
131 | | |
132 | | explicit basic_json_pointer(const string_view_type& s, std::error_code& ec) |
133 | | { |
134 | | auto jp = parse(s, ec); |
135 | | if (!ec) |
136 | | { |
137 | | tokens_ = std::move(jp.tokens_); |
138 | | } |
139 | | } |
140 | | |
141 | | basic_json_pointer(const basic_json_pointer&) = default; |
142 | | |
143 | | basic_json_pointer(basic_json_pointer&&) = default; |
144 | | |
145 | | static basic_json_pointer parse(const string_view_type& input, std::error_code& ec) |
146 | | { |
147 | | std::vector<string_type> tokens; |
148 | | if (input.empty()) |
149 | | { |
150 | | return basic_json_pointer<CharT>(); |
151 | | } |
152 | | |
153 | | const char_type* p = input.data(); |
154 | | const char_type* pend = input.data() + input.size(); |
155 | | string_type unescaped; |
156 | | |
157 | | auto state = jsonpointer::detail::pointer_state::start; |
158 | | string_type buffer; |
159 | | |
160 | | while (p < pend) |
161 | | { |
162 | | switch (state) |
163 | | { |
164 | | case jsonpointer::detail::pointer_state::start: |
165 | | switch (*p) |
166 | | { |
167 | | case '/': |
168 | | state = jsonpointer::detail::pointer_state::new_token; |
169 | | break; |
170 | | default: |
171 | | ec = jsonpointer_errc::expected_slash; |
172 | | return basic_json_pointer(); |
173 | | }; |
174 | | break; |
175 | | case jsonpointer::detail::pointer_state::part: |
176 | | state = jsonpointer::detail::pointer_state::new_token; |
177 | | JSONCONS_FALLTHROUGH; |
178 | | |
179 | | case jsonpointer::detail::pointer_state::new_token: |
180 | | switch (*p) |
181 | | { |
182 | | case '/': |
183 | | tokens.push_back(buffer); |
184 | | buffer.clear(); |
185 | | state = jsonpointer::detail::pointer_state::part; |
186 | | break; |
187 | | case '~': |
188 | | state = jsonpointer::detail::pointer_state::escaped; |
189 | | break; |
190 | | default: |
191 | | buffer.push_back(*p); |
192 | | break; |
193 | | }; |
194 | | break; |
195 | | case jsonpointer::detail::pointer_state::escaped: |
196 | | switch (*p) |
197 | | { |
198 | | case '0': |
199 | | buffer.push_back('~'); |
200 | | state = jsonpointer::detail::pointer_state::new_token; |
201 | | break; |
202 | | case '1': |
203 | | buffer.push_back('/'); |
204 | | state = jsonpointer::detail::pointer_state::new_token; |
205 | | break; |
206 | | default: |
207 | | ec = jsonpointer_errc::expected_0_or_1; |
208 | | return basic_json_pointer(); |
209 | | }; |
210 | | break; |
211 | | } |
212 | | ++p; |
213 | | } |
214 | | if (state == jsonpointer::detail::pointer_state::escaped) |
215 | | { |
216 | | ec = jsonpointer_errc::expected_0_or_1; |
217 | | return basic_json_pointer(); |
218 | | } |
219 | | if (state == jsonpointer::detail::pointer_state::new_token || state == jsonpointer::detail::pointer_state::part) |
220 | | { |
221 | | tokens.push_back(buffer); |
222 | | } |
223 | | return basic_json_pointer(tokens); |
224 | | } |
225 | | |
226 | | // operator= |
227 | | basic_json_pointer& operator=(const basic_json_pointer&) = default; |
228 | | |
229 | | basic_json_pointer& operator=(basic_json_pointer&&) = default; |
230 | | |
231 | | // Modifiers |
232 | | |
233 | | void clear() |
234 | | { |
235 | | tokens_.clear(); |
236 | | } |
237 | | |
238 | | basic_json_pointer& append(const string_type& s) |
239 | | { |
240 | | tokens_.push_back(s); |
241 | | return *this; |
242 | | } |
243 | | |
244 | | template <typename IntegerType> |
245 | | typename std::enable_if<ext_traits::is_integer<IntegerType>::value, basic_json_pointer&>::type |
246 | | append(IntegerType val) |
247 | | { |
248 | | string_type s; |
249 | | jsoncons::utility::from_integer(val, s); |
250 | | tokens_.push_back(s); |
251 | | |
252 | | return *this; |
253 | | } |
254 | | |
255 | | basic_json_pointer& operator/=(const string_type& s) |
256 | | { |
257 | | tokens_.push_back(s); |
258 | | return *this; |
259 | | } |
260 | | |
261 | | template <typename IntegerType> |
262 | | typename std::enable_if<ext_traits::is_integer<IntegerType>::value, basic_json_pointer&>::type |
263 | | operator/=(IntegerType val) |
264 | | { |
265 | | string_type s; |
266 | | jsoncons::utility::from_integer(val, s); |
267 | | tokens_.push_back(s); |
268 | | |
269 | | return *this; |
270 | | } |
271 | | |
272 | | basic_json_pointer& operator+=(const basic_json_pointer& p) |
273 | | { |
274 | | for (const auto& s : p.tokens_) |
275 | | { |
276 | | tokens_.push_back(s); |
277 | | } |
278 | | return *this; |
279 | | } |
280 | | |
281 | | // Accessors |
282 | | bool empty() const |
283 | | { |
284 | | return tokens_.empty(); |
285 | | } |
286 | | |
287 | | string_type string() const |
288 | | { |
289 | | return to_string(); |
290 | | } |
291 | | |
292 | | string_type to_string() const |
293 | 0 | { |
294 | 0 | string_type buffer; |
295 | 0 | for (const auto& token : tokens_) |
296 | 0 | { |
297 | 0 | buffer.push_back('/'); |
298 | 0 | for (auto c : token) |
299 | 0 | { |
300 | 0 | switch (c) |
301 | 0 | { |
302 | 0 | case '~': |
303 | 0 | buffer.push_back('~'); |
304 | 0 | buffer.push_back('0'); |
305 | 0 | break; |
306 | 0 | case '/': |
307 | 0 | buffer.push_back('~'); |
308 | 0 | buffer.push_back('1'); |
309 | 0 | break; |
310 | 0 | default: |
311 | 0 | buffer.push_back(c); |
312 | 0 | break; |
313 | 0 | } |
314 | 0 | } |
315 | 0 | } |
316 | 0 | return buffer; |
317 | 0 | } Unexecuted instantiation: jsoncons::jsonpointer::basic_json_pointer<char>::to_string() const Unexecuted instantiation: jsoncons::jsonpointer::basic_json_pointer<wchar_t>::to_string() const |
318 | | |
319 | | // Iterators |
320 | | iterator begin() const |
321 | | { |
322 | | return tokens_.begin(); |
323 | | } |
324 | | iterator end() const |
325 | | { |
326 | | return tokens_.end(); |
327 | | } |
328 | | |
329 | | reverse_iterator rbegin() const |
330 | | { |
331 | | return tokens_.rbegin(); |
332 | | } |
333 | | reverse_iterator rend() const |
334 | | { |
335 | | return tokens_.rend(); |
336 | | } |
337 | | |
338 | | // Non-member functions |
339 | | friend basic_json_pointer<CharT> operator/(const basic_json_pointer<CharT>& lhs, const string_type& rhs) |
340 | | { |
341 | | basic_json_pointer<CharT> p(lhs); |
342 | | p /= rhs; |
343 | | return p; |
344 | | } |
345 | | |
346 | | friend basic_json_pointer<CharT> operator+( const basic_json_pointer<CharT>& lhs, const basic_json_pointer<CharT>& rhs ) |
347 | | { |
348 | | basic_json_pointer<CharT> p(lhs); |
349 | | p += rhs; |
350 | | return p; |
351 | | } |
352 | | |
353 | | friend bool operator==( const basic_json_pointer& lhs, const basic_json_pointer& rhs ) |
354 | | { |
355 | | return lhs.tokens_ == rhs.tokens_; |
356 | | } |
357 | | |
358 | | friend bool operator!=( const basic_json_pointer& lhs, const basic_json_pointer& rhs ) |
359 | | { |
360 | | return lhs.tokens_ != rhs.tokens_; |
361 | | } |
362 | | |
363 | | friend std::basic_ostream<CharT>& |
364 | | operator<<(std::basic_ostream<CharT>& os, const basic_json_pointer<CharT>& p ) |
365 | | { |
366 | | os << p.to_string(); |
367 | | return os; |
368 | | } |
369 | | }; |
370 | | |
371 | | template <typename CharT,typename IntegerType> |
372 | | typename std::enable_if<ext_traits::is_integer<IntegerType>::value, basic_json_pointer<CharT>>::type |
373 | | operator/(const basic_json_pointer<CharT>& lhs, IntegerType rhs) |
374 | | { |
375 | | basic_json_pointer<CharT> p(lhs); |
376 | | p /= rhs; |
377 | | return p; |
378 | | } |
379 | | |
380 | | using json_pointer = basic_json_pointer<char>; |
381 | | using wjson_pointer = basic_json_pointer<wchar_t>; |
382 | | |
383 | | inline |
384 | | std::string to_string(const json_pointer& ptr) |
385 | 0 | { |
386 | 0 | return ptr.to_string(); |
387 | 0 | } |
388 | | |
389 | | inline |
390 | | std::wstring to_wstring(const wjson_pointer& ptr) |
391 | 0 | { |
392 | 0 | return ptr.to_string(); |
393 | 0 | } |
394 | | |
395 | | namespace detail { |
396 | | |
397 | | template <typename Json> |
398 | | const Json* resolve(const Json* current, const typename Json::string_view_type& buffer, std::error_code& ec) |
399 | | { |
400 | | if (current->is_array()) |
401 | | { |
402 | | if (buffer.size() == 1 && buffer[0] == '-') |
403 | | { |
404 | | ec = jsonpointer_errc::index_exceeds_array_size; |
405 | | return current; |
406 | | } |
407 | | std::size_t index{0}; |
408 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
409 | | if (!result) |
410 | | { |
411 | | ec = jsonpointer_errc::invalid_index; |
412 | | return current; |
413 | | } |
414 | | if (index >= current->size()) |
415 | | { |
416 | | ec = jsonpointer_errc::index_exceeds_array_size; |
417 | | return current; |
418 | | } |
419 | | current = std::addressof(current->at(index)); |
420 | | } |
421 | | else if (current->is_object()) |
422 | | { |
423 | | if (!current->contains(buffer)) |
424 | | { |
425 | | ec = jsonpointer_errc::key_not_found; |
426 | | return current; |
427 | | } |
428 | | current = std::addressof(current->at(buffer)); |
429 | | } |
430 | | else |
431 | | { |
432 | | ec = jsonpointer_errc::expected_object_or_array; |
433 | | return current; |
434 | | } |
435 | | return current; |
436 | | } |
437 | | |
438 | | template <typename Json> |
439 | | Json* resolve(Json* current, const typename Json::string_view_type& buffer, bool create_if_missing, std::error_code& ec) |
440 | | { |
441 | | if (current->is_array()) |
442 | | { |
443 | | if (buffer.size() == 1 && buffer[0] == '-') |
444 | | { |
445 | | ec = jsonpointer_errc::index_exceeds_array_size; |
446 | | return current; |
447 | | } |
448 | | std::size_t index{0}; |
449 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
450 | | if (!result) |
451 | | { |
452 | | ec = jsonpointer_errc::invalid_index; |
453 | | return current; |
454 | | } |
455 | | if (index >= current->size()) |
456 | | { |
457 | | ec = jsonpointer_errc::index_exceeds_array_size; |
458 | | return current; |
459 | | } |
460 | | current = std::addressof(current->at(index)); |
461 | | } |
462 | | else if (current->is_object()) |
463 | | { |
464 | | if (!current->contains(buffer)) |
465 | | { |
466 | | if (create_if_missing) |
467 | | { |
468 | | auto r = current->try_emplace(buffer, Json()); |
469 | | current = std::addressof(r.first->value()); |
470 | | } |
471 | | else |
472 | | { |
473 | | ec = jsonpointer_errc::key_not_found; |
474 | | return current; |
475 | | } |
476 | | } |
477 | | else |
478 | | { |
479 | | current = std::addressof(current->at(buffer)); |
480 | | } |
481 | | } |
482 | | else |
483 | | { |
484 | | ec = jsonpointer_errc::expected_object_or_array; |
485 | | return current; |
486 | | } |
487 | | return current; |
488 | | } |
489 | | |
490 | | } // namespace detail |
491 | | |
492 | | // get |
493 | | |
494 | | template <typename Json> |
495 | | Json& get(Json& root, |
496 | | const basic_json_pointer<typename Json::char_type>& location, |
497 | | bool create_if_missing, |
498 | | std::error_code& ec) |
499 | | { |
500 | | if (location.empty()) |
501 | | { |
502 | | return root; |
503 | | } |
504 | | |
505 | | Json* current = std::addressof(root); |
506 | | auto it = location.begin(); |
507 | | auto end = location.end(); |
508 | | while (it != end) |
509 | | { |
510 | | current = jsoncons::jsonpointer::detail::resolve(current, *it, create_if_missing, ec); |
511 | | if (JSONCONS_UNLIKELY(ec)) |
512 | | return *current; |
513 | | ++it; |
514 | | } |
515 | | return *current; |
516 | | } |
517 | | |
518 | | template <typename Json,typename StringSource> |
519 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type |
520 | | get(Json& root, |
521 | | const StringSource& location_str, |
522 | | bool create_if_missing, |
523 | | std::error_code& ec) |
524 | | { |
525 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
526 | | if (JSONCONS_UNLIKELY(ec)) |
527 | | { |
528 | | return root; |
529 | | } |
530 | | return get(root, jsonptr, create_if_missing, ec); |
531 | | } |
532 | | |
533 | | template <typename Json> |
534 | | const Json& get(const Json& root, |
535 | | const basic_json_pointer<typename Json::char_type>& location, |
536 | | std::error_code& ec) |
537 | | { |
538 | | if (location.empty()) |
539 | | { |
540 | | return root; |
541 | | } |
542 | | |
543 | | const Json* current = std::addressof(root); |
544 | | auto it = location.begin(); |
545 | | auto end = location.end(); |
546 | | while (it != end) |
547 | | { |
548 | | current = jsoncons::jsonpointer::detail::resolve(current, *it, ec); |
549 | | if (JSONCONS_UNLIKELY(ec)) |
550 | | return *current; |
551 | | ++it; |
552 | | } |
553 | | return *current; |
554 | | } |
555 | | |
556 | | template <typename Json,typename StringSource> |
557 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,const Json&>::type |
558 | | get(const Json& root, |
559 | | const StringSource& location_str, |
560 | | std::error_code& ec) |
561 | | { |
562 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
563 | | if (JSONCONS_UNLIKELY(ec)) |
564 | | { |
565 | | return root; |
566 | | } |
567 | | return get(root, jsonptr, ec); |
568 | | } |
569 | | |
570 | | template <typename Json> |
571 | | Json& get(Json& root, |
572 | | const basic_json_pointer<typename Json::char_type>& location, |
573 | | std::error_code& ec) |
574 | | { |
575 | | return get(root, location, false, ec); |
576 | | } |
577 | | |
578 | | template <typename Json,typename StringSource> |
579 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type |
580 | | get(Json& root, |
581 | | const StringSource& location_str, |
582 | | std::error_code& ec) |
583 | | { |
584 | | return get(root, location_str, false, ec); |
585 | | } |
586 | | |
587 | | template <typename Json> |
588 | | Json& get(Json& root, |
589 | | const basic_json_pointer<typename Json::char_type>& location, |
590 | | bool create_if_missing = false) |
591 | | { |
592 | | std::error_code ec; |
593 | | Json& j = get(root, location, create_if_missing, ec); |
594 | | if (JSONCONS_UNLIKELY(ec)) |
595 | | { |
596 | | JSONCONS_THROW(jsonpointer_error(ec)); |
597 | | } |
598 | | return j; |
599 | | } |
600 | | |
601 | | template <typename Json,typename StringSource> |
602 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type |
603 | | get(Json& root, |
604 | | const StringSource& location_str, |
605 | | bool create_if_missing = false) |
606 | | { |
607 | | std::error_code ec; |
608 | | Json& result = get(root, location_str, create_if_missing, ec); |
609 | | if (JSONCONS_UNLIKELY(ec)) |
610 | | { |
611 | | JSONCONS_THROW(jsonpointer_error(ec)); |
612 | | } |
613 | | return result; |
614 | | } |
615 | | |
616 | | template <typename Json> |
617 | | const Json& get(const Json& root, const basic_json_pointer<typename Json::char_type>& location) |
618 | | { |
619 | | std::error_code ec; |
620 | | const Json& j = get(root, location, ec); |
621 | | if (JSONCONS_UNLIKELY(ec)) |
622 | | { |
623 | | JSONCONS_THROW(jsonpointer_error(ec)); |
624 | | } |
625 | | return j; |
626 | | } |
627 | | |
628 | | template <typename Json,typename StringSource> |
629 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,const Json&>::type |
630 | | get(const Json& root, const StringSource& location_str) |
631 | | { |
632 | | std::error_code ec; |
633 | | const Json& j = get(root, location_str, ec); |
634 | | if (JSONCONS_UNLIKELY(ec)) |
635 | | { |
636 | | JSONCONS_THROW(jsonpointer_error(ec)); |
637 | | } |
638 | | return j; |
639 | | } |
640 | | |
641 | | // contains |
642 | | |
643 | | template <typename Json> |
644 | | bool contains(const Json& root, const basic_json_pointer<typename Json::char_type>& location) |
645 | | { |
646 | | std::error_code ec; |
647 | | get(root, location, ec); |
648 | | return !ec ? true : false; |
649 | | } |
650 | | |
651 | | template <typename Json,typename StringSource> |
652 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,bool>::type |
653 | | contains(const Json& root, const StringSource& location_str) |
654 | | { |
655 | | std::error_code ec; |
656 | | get(root, location_str, ec); |
657 | | return !ec ? true : false; |
658 | | } |
659 | | |
660 | | template <typename Json,typename T> |
661 | | void add(Json& root, |
662 | | const basic_json_pointer<typename Json::char_type>& location, |
663 | | T&& value, |
664 | | bool create_if_missing, |
665 | | std::error_code& ec) |
666 | | { |
667 | | if (location.empty()) |
668 | | { |
669 | | root = std::forward<T>(value); |
670 | | return; |
671 | | } |
672 | | |
673 | | Json* current = std::addressof(root); |
674 | | |
675 | | std::basic_string<typename Json::char_type> buffer; |
676 | | auto it = location.begin(); |
677 | | auto end = location.end(); |
678 | | while (it != end) |
679 | | { |
680 | | buffer = *it; |
681 | | ++it; |
682 | | if (it != end) |
683 | | { |
684 | | current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); |
685 | | if (JSONCONS_UNLIKELY(ec)) |
686 | | return; |
687 | | } |
688 | | } |
689 | | if (current->is_array()) |
690 | | { |
691 | | if (buffer.size() == 1 && buffer[0] == '-') |
692 | | { |
693 | | current->emplace_back(std::forward<T>(value)); |
694 | | current = std::addressof(current->at(current->size()-1)); |
695 | | } |
696 | | else |
697 | | { |
698 | | std::size_t index{0}; |
699 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
700 | | if (!result) |
701 | | { |
702 | | ec = jsonpointer_errc::invalid_index; |
703 | | return; |
704 | | } |
705 | | if (index > current->size()) |
706 | | { |
707 | | ec = jsonpointer_errc::index_exceeds_array_size; |
708 | | return; |
709 | | } |
710 | | if (index == current->size()) |
711 | | { |
712 | | current->emplace_back(std::forward<T>(value)); |
713 | | current = std::addressof(current->at(current->size()-1)); |
714 | | } |
715 | | else |
716 | | { |
717 | | auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value)); |
718 | | current = std::addressof(*it2); |
719 | | } |
720 | | } |
721 | | } |
722 | | else if (current->is_object()) |
723 | | { |
724 | | auto r = current->insert_or_assign(buffer,std::forward<T>(value)); |
725 | | current = std::addressof(r.first->value()); |
726 | | } |
727 | | else |
728 | | { |
729 | | ec = jsonpointer_errc::expected_object_or_array; |
730 | | return; |
731 | | } |
732 | | } |
733 | | |
734 | | // add |
735 | | template <typename Json,typename StringSource,typename T> |
736 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
737 | | add(Json& root, |
738 | | const StringSource& location_str, |
739 | | T&& value, |
740 | | bool create_if_missing, |
741 | | std::error_code& ec) |
742 | | { |
743 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
744 | | if (JSONCONS_UNLIKELY(ec)) |
745 | | { |
746 | | return; |
747 | | } |
748 | | add(root, jsonptr, std::forward<T>(value), create_if_missing, ec); |
749 | | } |
750 | | |
751 | | template <typename Json,typename T> |
752 | | void add(Json& root, |
753 | | const basic_json_pointer<typename Json::char_type>& location, |
754 | | T&& value, |
755 | | std::error_code& ec) |
756 | | { |
757 | | add(root, location, std::forward<T>(value), false, ec); |
758 | | } |
759 | | |
760 | | template <typename Json,typename StringSource,typename T> |
761 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
762 | | add(Json& root, |
763 | | const StringSource& location_str, |
764 | | T&& value, |
765 | | std::error_code& ec) |
766 | | { |
767 | | add(root, location_str, std::forward<T>(value), false, ec); |
768 | | } |
769 | | |
770 | | template <typename Json,typename T> |
771 | | void add(Json& root, |
772 | | const basic_json_pointer<typename Json::char_type>& location, |
773 | | T&& value, |
774 | | bool create_if_missing = false) |
775 | | { |
776 | | std::error_code ec; |
777 | | add(root, location, std::forward<T>(value), create_if_missing, ec); |
778 | | if (JSONCONS_UNLIKELY(ec)) |
779 | | { |
780 | | JSONCONS_THROW(jsonpointer_error(ec)); |
781 | | } |
782 | | } |
783 | | |
784 | | template <typename Json,typename StringSource,typename T> |
785 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
786 | | add(Json& root, |
787 | | const StringSource& location_str, |
788 | | T&& value, |
789 | | bool create_if_missing = false) |
790 | | { |
791 | | std::error_code ec; |
792 | | add(root, location_str, std::forward<T>(value), create_if_missing, ec); |
793 | | if (JSONCONS_UNLIKELY(ec)) |
794 | | { |
795 | | JSONCONS_THROW(jsonpointer_error(ec)); |
796 | | } |
797 | | } |
798 | | |
799 | | // add_if_absent |
800 | | |
801 | | template <typename Json,typename T> |
802 | | void add_if_absent(Json& root, |
803 | | const basic_json_pointer<typename Json::char_type>& location, |
804 | | T&& value, |
805 | | bool create_if_missing, |
806 | | std::error_code& ec) |
807 | | { |
808 | | if (location.empty()) |
809 | | { |
810 | | root = std::forward<T>(value); |
811 | | return; |
812 | | } |
813 | | Json* current = std::addressof(root); |
814 | | |
815 | | std::basic_string<typename Json::char_type> buffer; |
816 | | auto it = location.begin(); |
817 | | auto end = location.end(); |
818 | | |
819 | | while (it != end) |
820 | | { |
821 | | buffer = *it; |
822 | | ++it; |
823 | | if (it != end) |
824 | | { |
825 | | current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); |
826 | | if (JSONCONS_UNLIKELY(ec)) |
827 | | return; |
828 | | } |
829 | | } |
830 | | if (current->is_array()) |
831 | | { |
832 | | if (buffer.size() == 1 && buffer[0] == '-') |
833 | | { |
834 | | current->emplace_back(std::forward<T>(value)); |
835 | | current = std::addressof(current->at(current->size()-1)); |
836 | | } |
837 | | else |
838 | | { |
839 | | std::size_t index{0}; |
840 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
841 | | if (!result) |
842 | | { |
843 | | ec = jsonpointer_errc::invalid_index; |
844 | | return; |
845 | | } |
846 | | if (index > current->size()) |
847 | | { |
848 | | ec = jsonpointer_errc::index_exceeds_array_size; |
849 | | return; |
850 | | } |
851 | | if (index == current->size()) |
852 | | { |
853 | | current->emplace_back(std::forward<T>(value)); |
854 | | current = std::addressof(current->at(current->size()-1)); |
855 | | } |
856 | | else |
857 | | { |
858 | | auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value)); |
859 | | current = std::addressof(*it2); |
860 | | } |
861 | | } |
862 | | } |
863 | | else if (current->is_object()) |
864 | | { |
865 | | if (current->contains(buffer)) |
866 | | { |
867 | | ec = jsonpointer_errc::key_already_exists; |
868 | | return; |
869 | | } |
870 | | else |
871 | | { |
872 | | auto r = current->try_emplace(buffer,std::forward<T>(value)); |
873 | | current = std::addressof(r.first->value()); |
874 | | } |
875 | | } |
876 | | else |
877 | | { |
878 | | ec = jsonpointer_errc::expected_object_or_array; |
879 | | return; |
880 | | } |
881 | | } |
882 | | |
883 | | template <typename Json,typename StringSource,typename T> |
884 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
885 | | add_if_absent(Json& root, |
886 | | const StringSource& location_str, |
887 | | T&& value, |
888 | | bool create_if_missing, |
889 | | std::error_code& ec) |
890 | | { |
891 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
892 | | if (JSONCONS_UNLIKELY(ec)) |
893 | | { |
894 | | return; |
895 | | } |
896 | | add_if_absent(root, jsonptr, std::forward<T>(value), create_if_missing, ec); |
897 | | } |
898 | | |
899 | | template <typename Json,typename StringSource,typename T> |
900 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
901 | | add_if_absent(Json& root, |
902 | | const StringSource& location, |
903 | | T&& value, |
904 | | std::error_code& ec) |
905 | | { |
906 | | add_if_absent(root, location, std::forward<T>(value), false, ec); |
907 | | } |
908 | | |
909 | | template <typename Json,typename StringSource,typename T> |
910 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
911 | | add_if_absent(Json& root, |
912 | | const StringSource& location_str, |
913 | | T&& value, |
914 | | bool create_if_missing = false) |
915 | | { |
916 | | std::error_code ec; |
917 | | add_if_absent(root, location_str, std::forward<T>(value), create_if_missing, ec); |
918 | | if (JSONCONS_UNLIKELY(ec)) |
919 | | { |
920 | | JSONCONS_THROW(jsonpointer_error(ec)); |
921 | | } |
922 | | } |
923 | | |
924 | | template <typename Json,typename T> |
925 | | void add_if_absent(Json& root, |
926 | | const basic_json_pointer<typename Json::char_type>& location, |
927 | | T&& value, |
928 | | std::error_code& ec) |
929 | | { |
930 | | add_if_absent(root, location, std::forward<T>(value), false, ec); |
931 | | } |
932 | | |
933 | | template <typename Json,typename T> |
934 | | void add_if_absent(Json& root, |
935 | | const basic_json_pointer<typename Json::char_type>& location, |
936 | | T&& value, |
937 | | bool create_if_missing = false) |
938 | | { |
939 | | std::error_code ec; |
940 | | add_if_absent(root, location, std::forward<T>(value), create_if_missing, ec); |
941 | | if (JSONCONS_UNLIKELY(ec)) |
942 | | { |
943 | | JSONCONS_THROW(jsonpointer_error(ec)); |
944 | | } |
945 | | } |
946 | | |
947 | | // remove |
948 | | |
949 | | template <typename Json> |
950 | | void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location, std::error_code& ec) |
951 | | { |
952 | | if (location.empty()) |
953 | | { |
954 | | ec = jsonpointer_errc::cannot_remove_root; |
955 | | return; |
956 | | } |
957 | | |
958 | | Json* current = std::addressof(root); |
959 | | |
960 | | std::basic_string<typename Json::char_type> buffer; |
961 | | auto it = location.begin(); |
962 | | auto end = location.end(); |
963 | | |
964 | | while (it != end) |
965 | | { |
966 | | buffer = *it; |
967 | | ++it; |
968 | | if (it != end) |
969 | | { |
970 | | current = jsoncons::jsonpointer::detail::resolve(current, buffer, false, ec); |
971 | | if (JSONCONS_UNLIKELY(ec)) |
972 | | return; |
973 | | } |
974 | | } |
975 | | if (current->is_array()) |
976 | | { |
977 | | if (buffer.size() == 1 && buffer[0] == '-') |
978 | | { |
979 | | ec = jsonpointer_errc::index_exceeds_array_size; |
980 | | return; |
981 | | } |
982 | | else |
983 | | { |
984 | | std::size_t index{0}; |
985 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
986 | | if (!result) |
987 | | { |
988 | | ec = jsonpointer_errc::invalid_index; |
989 | | return; |
990 | | } |
991 | | if (index >= current->size()) |
992 | | { |
993 | | ec = jsonpointer_errc::index_exceeds_array_size; |
994 | | return; |
995 | | } |
996 | | current->erase(current->array_range().begin()+index); |
997 | | } |
998 | | } |
999 | | else if (current->is_object()) |
1000 | | { |
1001 | | if (!current->contains(buffer)) |
1002 | | { |
1003 | | ec = jsonpointer_errc::key_not_found; |
1004 | | return; |
1005 | | } |
1006 | | else |
1007 | | { |
1008 | | current->erase(buffer); |
1009 | | } |
1010 | | } |
1011 | | else |
1012 | | { |
1013 | | ec = jsonpointer_errc::expected_object_or_array; |
1014 | | return; |
1015 | | } |
1016 | | } |
1017 | | |
1018 | | template <typename Json,typename StringSource> |
1019 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1020 | | remove(Json& root, const StringSource& location_str, std::error_code& ec) |
1021 | | { |
1022 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
1023 | | if (JSONCONS_UNLIKELY(ec)) |
1024 | | { |
1025 | | return; |
1026 | | } |
1027 | | remove(root, jsonptr, ec); |
1028 | | } |
1029 | | |
1030 | | template <typename Json,typename StringSource> |
1031 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1032 | | remove(Json& root, const StringSource& location_str) |
1033 | | { |
1034 | | std::error_code ec; |
1035 | | remove(root, location_str, ec); |
1036 | | if (JSONCONS_UNLIKELY(ec)) |
1037 | | { |
1038 | | JSONCONS_THROW(jsonpointer_error(ec)); |
1039 | | } |
1040 | | } |
1041 | | |
1042 | | template <typename Json> |
1043 | | void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location) |
1044 | | { |
1045 | | std::error_code ec; |
1046 | | remove(root, location, ec); |
1047 | | if (JSONCONS_UNLIKELY(ec)) |
1048 | | { |
1049 | | JSONCONS_THROW(jsonpointer_error(ec)); |
1050 | | } |
1051 | | } |
1052 | | |
1053 | | // replace |
1054 | | |
1055 | | template <typename Json,typename T> |
1056 | | void replace(Json& root, |
1057 | | const basic_json_pointer<typename Json::char_type>& location, |
1058 | | T&& value, |
1059 | | bool create_if_missing, |
1060 | | std::error_code& ec) |
1061 | | { |
1062 | | if (location.empty()) |
1063 | | { |
1064 | | root = std::forward<T>(value); |
1065 | | return; |
1066 | | } |
1067 | | Json* current = std::addressof(root); |
1068 | | |
1069 | | std::basic_string<typename Json::char_type> buffer; |
1070 | | auto it = location.begin(); |
1071 | | auto end = location.end(); |
1072 | | |
1073 | | while (it != end) |
1074 | | { |
1075 | | buffer = *it; |
1076 | | ++it; |
1077 | | if (it != end) |
1078 | | { |
1079 | | current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); |
1080 | | if (JSONCONS_UNLIKELY(ec)) |
1081 | | return; |
1082 | | } |
1083 | | } |
1084 | | if (current->is_array()) |
1085 | | { |
1086 | | if (buffer.size() == 1 && buffer[0] == '-') |
1087 | | { |
1088 | | ec = jsonpointer_errc::index_exceeds_array_size; |
1089 | | return; |
1090 | | } |
1091 | | else |
1092 | | { |
1093 | | std::size_t index{}; |
1094 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
1095 | | if (!result) |
1096 | | { |
1097 | | ec = jsonpointer_errc::invalid_index; |
1098 | | return; |
1099 | | } |
1100 | | if (index >= current->size()) |
1101 | | { |
1102 | | ec = jsonpointer_errc::index_exceeds_array_size; |
1103 | | return; |
1104 | | } |
1105 | | current->at(index) = std::forward<T>(value); |
1106 | | } |
1107 | | } |
1108 | | else if (current->is_object()) |
1109 | | { |
1110 | | if (!current->contains(buffer)) |
1111 | | { |
1112 | | if (create_if_missing) |
1113 | | { |
1114 | | current->try_emplace(buffer,std::forward<T>(value)); |
1115 | | } |
1116 | | else |
1117 | | { |
1118 | | ec = jsonpointer_errc::key_not_found; |
1119 | | return; |
1120 | | } |
1121 | | } |
1122 | | else |
1123 | | { |
1124 | | auto r = current->insert_or_assign(buffer,std::forward<T>(value)); |
1125 | | current = std::addressof(r.first->value()); |
1126 | | } |
1127 | | } |
1128 | | else |
1129 | | { |
1130 | | ec = jsonpointer_errc::expected_object_or_array; |
1131 | | return; |
1132 | | } |
1133 | | } |
1134 | | |
1135 | | template <typename Json,typename StringSource,typename T> |
1136 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1137 | | replace(Json& root, |
1138 | | const StringSource& location_str, |
1139 | | T&& value, |
1140 | | bool create_if_missing, |
1141 | | std::error_code& ec) |
1142 | | { |
1143 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
1144 | | if (JSONCONS_UNLIKELY(ec)) |
1145 | | { |
1146 | | return; |
1147 | | } |
1148 | | replace(root, jsonptr, std::forward<T>(value), create_if_missing, ec); |
1149 | | } |
1150 | | |
1151 | | template <typename Json,typename StringSource,typename T> |
1152 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1153 | | replace(Json& root, |
1154 | | const StringSource& location_str, |
1155 | | T&& value, |
1156 | | std::error_code& ec) |
1157 | | { |
1158 | | replace(root, location_str, std::forward<T>(value), false, ec); |
1159 | | } |
1160 | | |
1161 | | template <typename Json,typename StringSource,typename T> |
1162 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1163 | | replace(Json& root, |
1164 | | const StringSource& location_str, |
1165 | | T&& value, |
1166 | | bool create_if_missing = false) |
1167 | | { |
1168 | | std::error_code ec; |
1169 | | replace(root, location_str, std::forward<T>(value), create_if_missing, ec); |
1170 | | if (JSONCONS_UNLIKELY(ec)) |
1171 | | { |
1172 | | JSONCONS_THROW(jsonpointer_error(ec)); |
1173 | | } |
1174 | | } |
1175 | | |
1176 | | template <typename Json,typename T> |
1177 | | void replace(Json& root, |
1178 | | const basic_json_pointer<typename Json::char_type>& location, |
1179 | | T&& value, |
1180 | | std::error_code& ec) |
1181 | | { |
1182 | | replace(root, location, std::forward<T>(value), false, ec); |
1183 | | } |
1184 | | |
1185 | | template <typename Json,typename T> |
1186 | | void replace(Json& root, |
1187 | | const basic_json_pointer<typename Json::char_type>& location, |
1188 | | T&& value, |
1189 | | bool create_if_missing = false) |
1190 | | { |
1191 | | std::error_code ec; |
1192 | | replace(root, location, std::forward<T>(value), create_if_missing, ec); |
1193 | | if (JSONCONS_UNLIKELY(ec)) |
1194 | | { |
1195 | | JSONCONS_THROW(jsonpointer_error(ec)); |
1196 | | } |
1197 | | } |
1198 | | |
1199 | | template <typename String,typename Result> |
1200 | | typename std::enable_if<std::is_convertible<typename String::value_type,typename Result::value_type>::value>::type |
1201 | | escape(const String& s, Result& result) |
1202 | | { |
1203 | | for (auto c : s) |
1204 | | { |
1205 | | if (c == '~') |
1206 | | { |
1207 | | result.push_back('~'); |
1208 | | result.push_back('0'); |
1209 | | } |
1210 | | else if (c == '/') |
1211 | | { |
1212 | | result.push_back('~'); |
1213 | | result.push_back('1'); |
1214 | | } |
1215 | | else |
1216 | | { |
1217 | | result.push_back(c); |
1218 | | } |
1219 | | } |
1220 | | } |
1221 | | |
1222 | | // flatten |
1223 | | |
1224 | | template <typename Json> |
1225 | | void flatten_(const std::basic_string<typename Json::char_type>& parent_key, |
1226 | | const Json& parent_value, |
1227 | | Json& result) |
1228 | | { |
1229 | | using char_type = typename Json::char_type; |
1230 | | using string_type = std::basic_string<char_type>; |
1231 | | |
1232 | | switch (parent_value.type()) |
1233 | | { |
1234 | | case json_type::array_value: |
1235 | | { |
1236 | | if (parent_value.empty()) |
1237 | | { |
1238 | | // Flatten empty array to null |
1239 | | //result.try_emplace(parent_key, null_type{}); |
1240 | | //result[parent_key] = parent_value; |
1241 | | result.try_emplace(parent_key, parent_value); |
1242 | | } |
1243 | | else |
1244 | | { |
1245 | | for (std::size_t i = 0; i < parent_value.size(); ++i) |
1246 | | { |
1247 | | string_type key(parent_key); |
1248 | | key.push_back('/'); |
1249 | | jsoncons::utility::from_integer(i,key); |
1250 | | flatten_(key, parent_value.at(i), result); |
1251 | | } |
1252 | | } |
1253 | | break; |
1254 | | } |
1255 | | |
1256 | | case json_type::object_value: |
1257 | | { |
1258 | | if (parent_value.empty()) |
1259 | | { |
1260 | | // Flatten empty object to null |
1261 | | //result.try_emplace(parent_key, null_type{}); |
1262 | | //result[parent_key] = parent_value; |
1263 | | result.try_emplace(parent_key, parent_value); |
1264 | | } |
1265 | | else |
1266 | | { |
1267 | | for (const auto& item : parent_value.object_range()) |
1268 | | { |
1269 | | string_type key(parent_key); |
1270 | | key.push_back('/'); |
1271 | | escape(jsoncons::basic_string_view<char_type>(item.key().data(),item.key().size()), key); |
1272 | | flatten_(key, item.value(), result); |
1273 | | } |
1274 | | } |
1275 | | break; |
1276 | | } |
1277 | | |
1278 | | default: |
1279 | | { |
1280 | | // add primitive parent_value with its reference string |
1281 | | //result[parent_key] = parent_value; |
1282 | | result.try_emplace(parent_key, parent_value); |
1283 | | break; |
1284 | | } |
1285 | | } |
1286 | | } |
1287 | | |
1288 | | template <typename Json> |
1289 | | Json flatten(const Json& value) |
1290 | | { |
1291 | | Json result; |
1292 | | std::basic_string<typename Json::char_type> parent_key; |
1293 | | flatten_(parent_key, value, result); |
1294 | | return result; |
1295 | | } |
1296 | | |
1297 | | |
1298 | | // unflatten |
1299 | | |
1300 | | enum class unflatten_options {none,assume_object = 1 |
1301 | | }; |
1302 | | |
1303 | | template <typename Json> |
1304 | | Json safe_unflatten (Json& value) |
1305 | | { |
1306 | | if (!value.is_object() || value.empty()) |
1307 | | { |
1308 | | return value; |
1309 | | } |
1310 | | bool safe = true; |
1311 | | std::size_t index = 0; |
1312 | | for (const auto& item : value.object_range()) |
1313 | | { |
1314 | | std::size_t n; |
1315 | | auto r = jsoncons::utility::dec_to_integer(item.key().data(),item.key().size(), n); |
1316 | | if (!r || (index++ != n)) |
1317 | | { |
1318 | | safe = false; |
1319 | | break; |
1320 | | } |
1321 | | } |
1322 | | |
1323 | | if (safe) |
1324 | | { |
1325 | | Json j(json_array_arg); |
1326 | | j.reserve(value.size()); |
1327 | | for (auto& item : value.object_range()) |
1328 | | { |
1329 | | j.emplace_back(std::move(item.value())); |
1330 | | } |
1331 | | Json a(json_array_arg); |
1332 | | for (auto& item : j.array_range()) |
1333 | | { |
1334 | | a.emplace_back(safe_unflatten (item)); |
1335 | | } |
1336 | | return a; |
1337 | | } |
1338 | | else |
1339 | | { |
1340 | | Json o(json_object_arg); |
1341 | | for (auto& item : value.object_range()) |
1342 | | { |
1343 | | o.try_emplace(item.key(), safe_unflatten (item.value())); |
1344 | | } |
1345 | | return o; |
1346 | | } |
1347 | | } |
1348 | | |
1349 | | template <typename Json> |
1350 | | jsoncons::optional<Json> try_unflatten_array(const Json& value) |
1351 | | { |
1352 | | using char_type = typename Json::char_type; |
1353 | | |
1354 | | if (JSONCONS_UNLIKELY(!value.is_object())) |
1355 | | { |
1356 | | JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); |
1357 | | } |
1358 | | Json result; |
1359 | | |
1360 | | for (const auto& item: value.object_range()) |
1361 | | { |
1362 | | Json* part = &result; |
1363 | | basic_json_pointer<char_type> ptr(item.key()); |
1364 | | std::size_t index = 0; |
1365 | | for (auto it = ptr.begin(); it != ptr.end(); ) |
1366 | | { |
1367 | | auto s = *it; |
1368 | | std::size_t n{0}; |
1369 | | auto r = jsoncons::utility::dec_to_integer(s.data(), s.size(), n); |
1370 | | if (r.ec == std::errc() && (index++ == n)) |
1371 | | { |
1372 | | if (!part->is_array()) |
1373 | | { |
1374 | | *part = Json(json_array_arg); |
1375 | | } |
1376 | | if (++it != ptr.end()) |
1377 | | { |
1378 | | if (n+1 > part->size()) |
1379 | | { |
1380 | | Json& ref = part->emplace_back(); |
1381 | | part = std::addressof(ref); |
1382 | | } |
1383 | | else |
1384 | | { |
1385 | | part = &part->at(n); |
1386 | | } |
1387 | | } |
1388 | | else |
1389 | | { |
1390 | | Json& ref = part->emplace_back(item.value()); |
1391 | | part = std::addressof(ref); |
1392 | | } |
1393 | | } |
1394 | | else if (part->is_object()) |
1395 | | { |
1396 | | if (++it != ptr.end()) |
1397 | | { |
1398 | | auto res = part->try_emplace(s,Json()); |
1399 | | part = &(res.first->value()); |
1400 | | } |
1401 | | else |
1402 | | { |
1403 | | auto res = part->try_emplace(s, item.value()); |
1404 | | part = &(res.first->value()); |
1405 | | } |
1406 | | } |
1407 | | else |
1408 | | { |
1409 | | return jsoncons::optional<Json>(); |
1410 | | } |
1411 | | } |
1412 | | } |
1413 | | |
1414 | | return result; |
1415 | | } |
1416 | | |
1417 | | template <typename Json> |
1418 | | Json unflatten_to_object(const Json& value, unflatten_options options = unflatten_options::none) |
1419 | | { |
1420 | | using char_type = typename Json::char_type; |
1421 | | |
1422 | | if (JSONCONS_UNLIKELY(!value.is_object())) |
1423 | | { |
1424 | | JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); |
1425 | | } |
1426 | | Json result; |
1427 | | |
1428 | | for (const auto& item: value.object_range()) |
1429 | | { |
1430 | | Json* part = &result; |
1431 | | basic_json_pointer<char_type> ptr(item.key()); |
1432 | | for (auto it = ptr.begin(); it != ptr.end(); ) |
1433 | | { |
1434 | | auto s = *it; |
1435 | | if (++it != ptr.end()) |
1436 | | { |
1437 | | auto res = part->try_emplace(s,Json()); |
1438 | | part = &(res.first->value()); |
1439 | | } |
1440 | | else |
1441 | | { |
1442 | | auto res = part->try_emplace(s, item.value()); |
1443 | | part = &(res.first->value()); |
1444 | | } |
1445 | | } |
1446 | | } |
1447 | | |
1448 | | return options == unflatten_options::none ? safe_unflatten (result) : result; |
1449 | | } |
1450 | | |
1451 | | template <typename Json> |
1452 | | Json unflatten(const Json& value, unflatten_options options = unflatten_options::none) |
1453 | | { |
1454 | | if (options == unflatten_options::none) |
1455 | | { |
1456 | | jsoncons::optional<Json> j = try_unflatten_array(value); |
1457 | | return j ? *j : unflatten_to_object(value,options); |
1458 | | } |
1459 | | else |
1460 | | { |
1461 | | return unflatten_to_object(value,options); |
1462 | | } |
1463 | | } |
1464 | | |
1465 | | } // namespace jsonpointer |
1466 | | } // namespace jsoncons |
1467 | | |
1468 | | namespace std { |
1469 | | template <typename CharT> |
1470 | | struct hash<jsoncons::jsonpointer::basic_json_pointer<CharT>> |
1471 | | { |
1472 | | std::size_t operator()(const jsoncons::jsonpointer::basic_json_pointer<CharT>& ptr) const noexcept |
1473 | | { |
1474 | | constexpr std::uint64_t prime{0x100000001B3}; |
1475 | | std::uint64_t result{0xcbf29ce484222325}; |
1476 | | |
1477 | | for (const auto& str : ptr) |
1478 | | { |
1479 | | for (std::size_t i = 0; i < str.length(); ++i) |
1480 | | { |
1481 | | result = (result * prime) ^ str[i]; |
1482 | | } |
1483 | | } |
1484 | | return result; |
1485 | | } |
1486 | | }; |
1487 | | |
1488 | | } // namespace std |
1489 | | |
1490 | | #endif |