Coverage Report

Created: 2025-10-10 07:03

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