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