/src/jsoncons/include/jsoncons_ext/jsonpointer/jsonpointer.hpp
Line | Count | Source (jump to first uncovered line) |
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 <iostream> |
12 | | #include <memory> |
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 | | Json* current = std::addressof(root); |
668 | | |
669 | | std::basic_string<typename Json::char_type> buffer; |
670 | | auto it = location.begin(); |
671 | | auto end = location.end(); |
672 | | while (it != end) |
673 | | { |
674 | | buffer = *it; |
675 | | ++it; |
676 | | if (it != end) |
677 | | { |
678 | | current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); |
679 | | if (JSONCONS_UNLIKELY(ec)) |
680 | | return; |
681 | | } |
682 | | } |
683 | | if (current->is_array()) |
684 | | { |
685 | | if (buffer.size() == 1 && buffer[0] == '-') |
686 | | { |
687 | | current->emplace_back(std::forward<T>(value)); |
688 | | current = std::addressof(current->at(current->size()-1)); |
689 | | } |
690 | | else |
691 | | { |
692 | | std::size_t index{0}; |
693 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
694 | | if (!result) |
695 | | { |
696 | | ec = jsonpointer_errc::invalid_index; |
697 | | return; |
698 | | } |
699 | | if (index > current->size()) |
700 | | { |
701 | | ec = jsonpointer_errc::index_exceeds_array_size; |
702 | | return; |
703 | | } |
704 | | if (index == current->size()) |
705 | | { |
706 | | current->emplace_back(std::forward<T>(value)); |
707 | | current = std::addressof(current->at(current->size()-1)); |
708 | | } |
709 | | else |
710 | | { |
711 | | auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value)); |
712 | | current = std::addressof(*it2); |
713 | | } |
714 | | } |
715 | | } |
716 | | else if (current->is_object()) |
717 | | { |
718 | | auto r = current->insert_or_assign(buffer,std::forward<T>(value)); |
719 | | current = std::addressof(r.first->value()); |
720 | | } |
721 | | else |
722 | | { |
723 | | ec = jsonpointer_errc::expected_object_or_array; |
724 | | return; |
725 | | } |
726 | | } |
727 | | |
728 | | // add |
729 | | template <typename Json,typename StringSource,typename T> |
730 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
731 | | add(Json& root, |
732 | | const StringSource& location_str, |
733 | | T&& value, |
734 | | bool create_if_missing, |
735 | | std::error_code& ec) |
736 | | { |
737 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
738 | | if (JSONCONS_UNLIKELY(ec)) |
739 | | { |
740 | | return; |
741 | | } |
742 | | add(root, jsonptr, std::forward<T>(value), create_if_missing, ec); |
743 | | } |
744 | | |
745 | | template <typename Json,typename T> |
746 | | void add(Json& root, |
747 | | const basic_json_pointer<typename Json::char_type>& location, |
748 | | T&& value, |
749 | | std::error_code& ec) |
750 | | { |
751 | | add(root, location, std::forward<T>(value), false, ec); |
752 | | } |
753 | | |
754 | | template <typename Json,typename StringSource,typename T> |
755 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
756 | | add(Json& root, |
757 | | const StringSource& location_str, |
758 | | T&& value, |
759 | | std::error_code& ec) |
760 | | { |
761 | | add(root, location_str, std::forward<T>(value), false, ec); |
762 | | } |
763 | | |
764 | | template <typename Json,typename T> |
765 | | void add(Json& root, |
766 | | const basic_json_pointer<typename Json::char_type>& location, |
767 | | T&& value, |
768 | | bool create_if_missing = false) |
769 | | { |
770 | | std::error_code ec; |
771 | | add(root, location, std::forward<T>(value), create_if_missing, ec); |
772 | | if (JSONCONS_UNLIKELY(ec)) |
773 | | { |
774 | | JSONCONS_THROW(jsonpointer_error(ec)); |
775 | | } |
776 | | } |
777 | | |
778 | | template <typename Json,typename StringSource,typename T> |
779 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
780 | | add(Json& root, |
781 | | const StringSource& location_str, |
782 | | T&& value, |
783 | | bool create_if_missing = false) |
784 | | { |
785 | | std::error_code ec; |
786 | | add(root, location_str, std::forward<T>(value), create_if_missing, ec); |
787 | | if (JSONCONS_UNLIKELY(ec)) |
788 | | { |
789 | | JSONCONS_THROW(jsonpointer_error(ec)); |
790 | | } |
791 | | } |
792 | | |
793 | | // add_if_absent |
794 | | |
795 | | template <typename Json,typename T> |
796 | | void add_if_absent(Json& root, |
797 | | const basic_json_pointer<typename Json::char_type>& location, |
798 | | T&& value, |
799 | | bool create_if_missing, |
800 | | std::error_code& ec) |
801 | | { |
802 | | Json* current = std::addressof(root); |
803 | | |
804 | | std::basic_string<typename Json::char_type> buffer; |
805 | | auto it = location.begin(); |
806 | | auto end = location.end(); |
807 | | |
808 | | while (it != end) |
809 | | { |
810 | | buffer = *it; |
811 | | ++it; |
812 | | if (it != end) |
813 | | { |
814 | | current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); |
815 | | if (JSONCONS_UNLIKELY(ec)) |
816 | | return; |
817 | | } |
818 | | } |
819 | | if (current->is_array()) |
820 | | { |
821 | | if (buffer.size() == 1 && buffer[0] == '-') |
822 | | { |
823 | | current->emplace_back(std::forward<T>(value)); |
824 | | current = std::addressof(current->at(current->size()-1)); |
825 | | } |
826 | | else |
827 | | { |
828 | | std::size_t index{0}; |
829 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
830 | | if (!result) |
831 | | { |
832 | | ec = jsonpointer_errc::invalid_index; |
833 | | return; |
834 | | } |
835 | | if (index > current->size()) |
836 | | { |
837 | | ec = jsonpointer_errc::index_exceeds_array_size; |
838 | | return; |
839 | | } |
840 | | if (index == current->size()) |
841 | | { |
842 | | current->emplace_back(std::forward<T>(value)); |
843 | | current = std::addressof(current->at(current->size()-1)); |
844 | | } |
845 | | else |
846 | | { |
847 | | auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value)); |
848 | | current = std::addressof(*it2); |
849 | | } |
850 | | } |
851 | | } |
852 | | else if (current->is_object()) |
853 | | { |
854 | | if (current->contains(buffer)) |
855 | | { |
856 | | ec = jsonpointer_errc::key_already_exists; |
857 | | return; |
858 | | } |
859 | | else |
860 | | { |
861 | | auto r = current->try_emplace(buffer,std::forward<T>(value)); |
862 | | current = std::addressof(r.first->value()); |
863 | | } |
864 | | } |
865 | | else |
866 | | { |
867 | | ec = jsonpointer_errc::expected_object_or_array; |
868 | | return; |
869 | | } |
870 | | } |
871 | | |
872 | | template <typename Json,typename StringSource,typename T> |
873 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
874 | | add_if_absent(Json& root, |
875 | | const StringSource& location_str, |
876 | | T&& value, |
877 | | bool create_if_missing, |
878 | | std::error_code& ec) |
879 | | { |
880 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
881 | | if (JSONCONS_UNLIKELY(ec)) |
882 | | { |
883 | | return; |
884 | | } |
885 | | add_if_absent(root, jsonptr, std::forward<T>(value), create_if_missing, ec); |
886 | | } |
887 | | |
888 | | template <typename Json,typename StringSource,typename T> |
889 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
890 | | add_if_absent(Json& root, |
891 | | const StringSource& location, |
892 | | T&& value, |
893 | | std::error_code& ec) |
894 | | { |
895 | | add_if_absent(root, location, std::forward<T>(value), false, ec); |
896 | | } |
897 | | |
898 | | template <typename Json,typename StringSource,typename T> |
899 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
900 | | add_if_absent(Json& root, |
901 | | const StringSource& location_str, |
902 | | T&& value, |
903 | | bool create_if_missing = false) |
904 | | { |
905 | | std::error_code ec; |
906 | | add_if_absent(root, location_str, std::forward<T>(value), create_if_missing, ec); |
907 | | if (JSONCONS_UNLIKELY(ec)) |
908 | | { |
909 | | JSONCONS_THROW(jsonpointer_error(ec)); |
910 | | } |
911 | | } |
912 | | |
913 | | template <typename Json,typename T> |
914 | | void add_if_absent(Json& root, |
915 | | const basic_json_pointer<typename Json::char_type>& location, |
916 | | T&& value, |
917 | | std::error_code& ec) |
918 | | { |
919 | | add_if_absent(root, location, std::forward<T>(value), false, ec); |
920 | | } |
921 | | |
922 | | template <typename Json,typename T> |
923 | | void add_if_absent(Json& root, |
924 | | const basic_json_pointer<typename Json::char_type>& location, |
925 | | T&& value, |
926 | | bool create_if_missing = false) |
927 | | { |
928 | | std::error_code ec; |
929 | | add_if_absent(root, location, std::forward<T>(value), create_if_missing, ec); |
930 | | if (JSONCONS_UNLIKELY(ec)) |
931 | | { |
932 | | JSONCONS_THROW(jsonpointer_error(ec)); |
933 | | } |
934 | | } |
935 | | |
936 | | // remove |
937 | | |
938 | | template <typename Json> |
939 | | void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location, std::error_code& ec) |
940 | | { |
941 | | Json* current = std::addressof(root); |
942 | | |
943 | | std::basic_string<typename Json::char_type> buffer; |
944 | | auto it = location.begin(); |
945 | | auto end = location.end(); |
946 | | |
947 | | while (it != end) |
948 | | { |
949 | | buffer = *it; |
950 | | ++it; |
951 | | if (it != end) |
952 | | { |
953 | | current = jsoncons::jsonpointer::detail::resolve(current, buffer, false, ec); |
954 | | if (JSONCONS_UNLIKELY(ec)) |
955 | | return; |
956 | | } |
957 | | } |
958 | | if (current->is_array()) |
959 | | { |
960 | | if (buffer.size() == 1 && buffer[0] == '-') |
961 | | { |
962 | | ec = jsonpointer_errc::index_exceeds_array_size; |
963 | | return; |
964 | | } |
965 | | else |
966 | | { |
967 | | std::size_t index{0}; |
968 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
969 | | if (!result) |
970 | | { |
971 | | ec = jsonpointer_errc::invalid_index; |
972 | | return; |
973 | | } |
974 | | if (index >= current->size()) |
975 | | { |
976 | | ec = jsonpointer_errc::index_exceeds_array_size; |
977 | | return; |
978 | | } |
979 | | current->erase(current->array_range().begin()+index); |
980 | | } |
981 | | } |
982 | | else if (current->is_object()) |
983 | | { |
984 | | if (!current->contains(buffer)) |
985 | | { |
986 | | ec = jsonpointer_errc::key_not_found; |
987 | | return; |
988 | | } |
989 | | else |
990 | | { |
991 | | current->erase(buffer); |
992 | | } |
993 | | } |
994 | | else |
995 | | { |
996 | | ec = jsonpointer_errc::expected_object_or_array; |
997 | | return; |
998 | | } |
999 | | } |
1000 | | |
1001 | | template <typename Json,typename StringSource> |
1002 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1003 | | remove(Json& root, const StringSource& location_str, std::error_code& ec) |
1004 | | { |
1005 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
1006 | | if (JSONCONS_UNLIKELY(ec)) |
1007 | | { |
1008 | | return; |
1009 | | } |
1010 | | remove(root, jsonptr, ec); |
1011 | | } |
1012 | | |
1013 | | template <typename Json,typename StringSource> |
1014 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1015 | | remove(Json& root, const StringSource& location_str) |
1016 | | { |
1017 | | std::error_code ec; |
1018 | | remove(root, location_str, ec); |
1019 | | if (JSONCONS_UNLIKELY(ec)) |
1020 | | { |
1021 | | JSONCONS_THROW(jsonpointer_error(ec)); |
1022 | | } |
1023 | | } |
1024 | | |
1025 | | template <typename Json> |
1026 | | void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location) |
1027 | | { |
1028 | | std::error_code ec; |
1029 | | remove(root, location, ec); |
1030 | | if (JSONCONS_UNLIKELY(ec)) |
1031 | | { |
1032 | | JSONCONS_THROW(jsonpointer_error(ec)); |
1033 | | } |
1034 | | } |
1035 | | |
1036 | | // replace |
1037 | | |
1038 | | template <typename Json,typename T> |
1039 | | void replace(Json& root, |
1040 | | const basic_json_pointer<typename Json::char_type>& location, |
1041 | | T&& value, |
1042 | | bool create_if_missing, |
1043 | | std::error_code& ec) |
1044 | | { |
1045 | | Json* current = std::addressof(root); |
1046 | | |
1047 | | std::basic_string<typename Json::char_type> buffer; |
1048 | | auto it = location.begin(); |
1049 | | auto end = location.end(); |
1050 | | |
1051 | | while (it != end) |
1052 | | { |
1053 | | buffer = *it; |
1054 | | ++it; |
1055 | | if (it != end) |
1056 | | { |
1057 | | current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); |
1058 | | if (JSONCONS_UNLIKELY(ec)) |
1059 | | return; |
1060 | | } |
1061 | | } |
1062 | | if (current->is_array()) |
1063 | | { |
1064 | | if (buffer.size() == 1 && buffer[0] == '-') |
1065 | | { |
1066 | | ec = jsonpointer_errc::index_exceeds_array_size; |
1067 | | return; |
1068 | | } |
1069 | | else |
1070 | | { |
1071 | | std::size_t index{}; |
1072 | | auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index); |
1073 | | if (!result) |
1074 | | { |
1075 | | ec = jsonpointer_errc::invalid_index; |
1076 | | return; |
1077 | | } |
1078 | | if (index >= current->size()) |
1079 | | { |
1080 | | ec = jsonpointer_errc::index_exceeds_array_size; |
1081 | | return; |
1082 | | } |
1083 | | current->at(index) = std::forward<T>(value); |
1084 | | } |
1085 | | } |
1086 | | else if (current->is_object()) |
1087 | | { |
1088 | | if (!current->contains(buffer)) |
1089 | | { |
1090 | | if (create_if_missing) |
1091 | | { |
1092 | | current->try_emplace(buffer,std::forward<T>(value)); |
1093 | | } |
1094 | | else |
1095 | | { |
1096 | | ec = jsonpointer_errc::key_not_found; |
1097 | | return; |
1098 | | } |
1099 | | } |
1100 | | else |
1101 | | { |
1102 | | auto r = current->insert_or_assign(buffer,std::forward<T>(value)); |
1103 | | current = std::addressof(r.first->value()); |
1104 | | } |
1105 | | } |
1106 | | else |
1107 | | { |
1108 | | ec = jsonpointer_errc::expected_object_or_array; |
1109 | | return; |
1110 | | } |
1111 | | } |
1112 | | |
1113 | | template <typename Json,typename StringSource,typename T> |
1114 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1115 | | replace(Json& root, |
1116 | | const StringSource& location_str, |
1117 | | T&& value, |
1118 | | bool create_if_missing, |
1119 | | std::error_code& ec) |
1120 | | { |
1121 | | auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec); |
1122 | | if (JSONCONS_UNLIKELY(ec)) |
1123 | | { |
1124 | | return; |
1125 | | } |
1126 | | replace(root, jsonptr, std::forward<T>(value), create_if_missing, ec); |
1127 | | } |
1128 | | |
1129 | | template <typename Json,typename StringSource,typename T> |
1130 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1131 | | replace(Json& root, |
1132 | | const StringSource& location_str, |
1133 | | T&& value, |
1134 | | std::error_code& ec) |
1135 | | { |
1136 | | replace(root, location_str, std::forward<T>(value), false, ec); |
1137 | | } |
1138 | | |
1139 | | template <typename Json,typename StringSource,typename T> |
1140 | | typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type |
1141 | | replace(Json& root, |
1142 | | const StringSource& location_str, |
1143 | | T&& value, |
1144 | | bool create_if_missing = false) |
1145 | | { |
1146 | | std::error_code ec; |
1147 | | replace(root, location_str, std::forward<T>(value), create_if_missing, ec); |
1148 | | if (JSONCONS_UNLIKELY(ec)) |
1149 | | { |
1150 | | JSONCONS_THROW(jsonpointer_error(ec)); |
1151 | | } |
1152 | | } |
1153 | | |
1154 | | template <typename Json,typename T> |
1155 | | void replace(Json& root, |
1156 | | const basic_json_pointer<typename Json::char_type>& location, |
1157 | | T&& value, |
1158 | | std::error_code& ec) |
1159 | | { |
1160 | | replace(root, location, std::forward<T>(value), false, ec); |
1161 | | } |
1162 | | |
1163 | | template <typename Json,typename T> |
1164 | | void replace(Json& root, |
1165 | | const basic_json_pointer<typename Json::char_type>& location, |
1166 | | T&& value, |
1167 | | bool create_if_missing = false) |
1168 | | { |
1169 | | std::error_code ec; |
1170 | | replace(root, location, std::forward<T>(value), create_if_missing, ec); |
1171 | | if (JSONCONS_UNLIKELY(ec)) |
1172 | | { |
1173 | | JSONCONS_THROW(jsonpointer_error(ec)); |
1174 | | } |
1175 | | } |
1176 | | |
1177 | | template <typename String,typename Result> |
1178 | | typename std::enable_if<std::is_convertible<typename String::value_type,typename Result::value_type>::value>::type |
1179 | | escape(const String& s, Result& result) |
1180 | | { |
1181 | | for (auto c : s) |
1182 | | { |
1183 | | if (c == '~') |
1184 | | { |
1185 | | result.push_back('~'); |
1186 | | result.push_back('0'); |
1187 | | } |
1188 | | else if (c == '/') |
1189 | | { |
1190 | | result.push_back('~'); |
1191 | | result.push_back('1'); |
1192 | | } |
1193 | | else |
1194 | | { |
1195 | | result.push_back(c); |
1196 | | } |
1197 | | } |
1198 | | } |
1199 | | |
1200 | | // flatten |
1201 | | |
1202 | | template <typename Json> |
1203 | | void flatten_(const std::basic_string<typename Json::char_type>& parent_key, |
1204 | | const Json& parent_value, |
1205 | | Json& result) |
1206 | | { |
1207 | | using char_type = typename Json::char_type; |
1208 | | using string_type = std::basic_string<char_type>; |
1209 | | |
1210 | | switch (parent_value.type()) |
1211 | | { |
1212 | | case json_type::array_value: |
1213 | | { |
1214 | | if (parent_value.empty()) |
1215 | | { |
1216 | | // Flatten empty array to null |
1217 | | //result.try_emplace(parent_key, null_type{}); |
1218 | | //result[parent_key] = parent_value; |
1219 | | result.try_emplace(parent_key, parent_value); |
1220 | | } |
1221 | | else |
1222 | | { |
1223 | | for (std::size_t i = 0; i < parent_value.size(); ++i) |
1224 | | { |
1225 | | string_type key(parent_key); |
1226 | | key.push_back('/'); |
1227 | | jsoncons::utility::from_integer(i,key); |
1228 | | flatten_(key, parent_value.at(i), result); |
1229 | | } |
1230 | | } |
1231 | | break; |
1232 | | } |
1233 | | |
1234 | | case json_type::object_value: |
1235 | | { |
1236 | | if (parent_value.empty()) |
1237 | | { |
1238 | | // Flatten empty object 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 (const auto& item : parent_value.object_range()) |
1246 | | { |
1247 | | string_type key(parent_key); |
1248 | | key.push_back('/'); |
1249 | | escape(jsoncons::basic_string_view<char_type>(item.key().data(),item.key().size()), key); |
1250 | | flatten_(key, item.value(), result); |
1251 | | } |
1252 | | } |
1253 | | break; |
1254 | | } |
1255 | | |
1256 | | default: |
1257 | | { |
1258 | | // add primitive parent_value with its reference string |
1259 | | //result[parent_key] = parent_value; |
1260 | | result.try_emplace(parent_key, parent_value); |
1261 | | break; |
1262 | | } |
1263 | | } |
1264 | | } |
1265 | | |
1266 | | template <typename Json> |
1267 | | Json flatten(const Json& value) |
1268 | | { |
1269 | | Json result; |
1270 | | std::basic_string<typename Json::char_type> parent_key; |
1271 | | flatten_(parent_key, value, result); |
1272 | | return result; |
1273 | | } |
1274 | | |
1275 | | |
1276 | | // unflatten |
1277 | | |
1278 | | enum class unflatten_options {none,assume_object = 1 |
1279 | | }; |
1280 | | |
1281 | | template <typename Json> |
1282 | | Json safe_unflatten (Json& value) |
1283 | | { |
1284 | | if (!value.is_object() || value.empty()) |
1285 | | { |
1286 | | return value; |
1287 | | } |
1288 | | bool safe = true; |
1289 | | std::size_t index = 0; |
1290 | | for (const auto& item : value.object_range()) |
1291 | | { |
1292 | | std::size_t n; |
1293 | | auto r = jsoncons::utility::dec_to_integer(item.key().data(),item.key().size(), n); |
1294 | | if (!r || (index++ != n)) |
1295 | | { |
1296 | | safe = false; |
1297 | | break; |
1298 | | } |
1299 | | } |
1300 | | |
1301 | | if (safe) |
1302 | | { |
1303 | | Json j(json_array_arg); |
1304 | | j.reserve(value.size()); |
1305 | | for (auto& item : value.object_range()) |
1306 | | { |
1307 | | j.emplace_back(std::move(item.value())); |
1308 | | } |
1309 | | Json a(json_array_arg); |
1310 | | for (auto& item : j.array_range()) |
1311 | | { |
1312 | | a.emplace_back(safe_unflatten (item)); |
1313 | | } |
1314 | | return a; |
1315 | | } |
1316 | | else |
1317 | | { |
1318 | | Json o(json_object_arg); |
1319 | | for (auto& item : value.object_range()) |
1320 | | { |
1321 | | o.try_emplace(item.key(), safe_unflatten (item.value())); |
1322 | | } |
1323 | | return o; |
1324 | | } |
1325 | | } |
1326 | | |
1327 | | template <typename Json> |
1328 | | jsoncons::optional<Json> try_unflatten_array(const Json& value) |
1329 | | { |
1330 | | using char_type = typename Json::char_type; |
1331 | | |
1332 | | if (JSONCONS_UNLIKELY(!value.is_object())) |
1333 | | { |
1334 | | JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); |
1335 | | } |
1336 | | Json result; |
1337 | | |
1338 | | for (const auto& item: value.object_range()) |
1339 | | { |
1340 | | Json* part = &result; |
1341 | | basic_json_pointer<char_type> ptr(item.key()); |
1342 | | std::size_t index = 0; |
1343 | | for (auto it = ptr.begin(); it != ptr.end(); ) |
1344 | | { |
1345 | | auto s = *it; |
1346 | | std::size_t n{0}; |
1347 | | auto r = jsoncons::utility::dec_to_integer(s.data(), s.size(), n); |
1348 | | if (r.ec == std::errc() && (index++ == n)) |
1349 | | { |
1350 | | if (!part->is_array()) |
1351 | | { |
1352 | | *part = Json(json_array_arg); |
1353 | | } |
1354 | | if (++it != ptr.end()) |
1355 | | { |
1356 | | if (n+1 > part->size()) |
1357 | | { |
1358 | | Json& ref = part->emplace_back(); |
1359 | | part = std::addressof(ref); |
1360 | | } |
1361 | | else |
1362 | | { |
1363 | | part = &part->at(n); |
1364 | | } |
1365 | | } |
1366 | | else |
1367 | | { |
1368 | | Json& ref = part->emplace_back(item.value()); |
1369 | | part = std::addressof(ref); |
1370 | | } |
1371 | | } |
1372 | | else if (part->is_object()) |
1373 | | { |
1374 | | if (++it != ptr.end()) |
1375 | | { |
1376 | | auto res = part->try_emplace(s,Json()); |
1377 | | part = &(res.first->value()); |
1378 | | } |
1379 | | else |
1380 | | { |
1381 | | auto res = part->try_emplace(s, item.value()); |
1382 | | part = &(res.first->value()); |
1383 | | } |
1384 | | } |
1385 | | else |
1386 | | { |
1387 | | return jsoncons::optional<Json>(); |
1388 | | } |
1389 | | } |
1390 | | } |
1391 | | |
1392 | | return result; |
1393 | | } |
1394 | | |
1395 | | template <typename Json> |
1396 | | Json unflatten_to_object(const Json& value, unflatten_options options = unflatten_options::none) |
1397 | | { |
1398 | | using char_type = typename Json::char_type; |
1399 | | |
1400 | | if (JSONCONS_UNLIKELY(!value.is_object())) |
1401 | | { |
1402 | | JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); |
1403 | | } |
1404 | | Json result; |
1405 | | |
1406 | | for (const auto& item: value.object_range()) |
1407 | | { |
1408 | | Json* part = &result; |
1409 | | basic_json_pointer<char_type> ptr(item.key()); |
1410 | | for (auto it = ptr.begin(); it != ptr.end(); ) |
1411 | | { |
1412 | | auto s = *it; |
1413 | | if (++it != ptr.end()) |
1414 | | { |
1415 | | auto res = part->try_emplace(s,Json()); |
1416 | | part = &(res.first->value()); |
1417 | | } |
1418 | | else |
1419 | | { |
1420 | | auto res = part->try_emplace(s, item.value()); |
1421 | | part = &(res.first->value()); |
1422 | | } |
1423 | | } |
1424 | | } |
1425 | | |
1426 | | return options == unflatten_options::none ? safe_unflatten (result) : result; |
1427 | | } |
1428 | | |
1429 | | template <typename Json> |
1430 | | Json unflatten(const Json& value, unflatten_options options = unflatten_options::none) |
1431 | | { |
1432 | | if (options == unflatten_options::none) |
1433 | | { |
1434 | | jsoncons::optional<Json> j = try_unflatten_array(value); |
1435 | | return j ? *j : unflatten_to_object(value,options); |
1436 | | } |
1437 | | else |
1438 | | { |
1439 | | return unflatten_to_object(value,options); |
1440 | | } |
1441 | | } |
1442 | | |
1443 | | } // namespace jsonpointer |
1444 | | } // namespace jsoncons |
1445 | | |
1446 | | namespace std { |
1447 | | template <typename CharT> |
1448 | | struct hash<jsoncons::jsonpointer::basic_json_pointer<CharT>> |
1449 | | { |
1450 | | std::size_t operator()(const jsoncons::jsonpointer::basic_json_pointer<CharT>& ptr) const noexcept |
1451 | | { |
1452 | | constexpr std::uint64_t prime{0x100000001B3}; |
1453 | | std::uint64_t result{0xcbf29ce484222325}; |
1454 | | |
1455 | | for (const auto& str : ptr) |
1456 | | { |
1457 | | for (std::size_t i = 0; i < str.length(); ++i) |
1458 | | { |
1459 | | result = (result * prime) ^ str[i]; |
1460 | | } |
1461 | | } |
1462 | | return result; |
1463 | | } |
1464 | | }; |
1465 | | |
1466 | | } // namespace std |
1467 | | |
1468 | | #endif |