1//     __ _____ _____ _____
2//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
3// |  |  |__   |  |  | | | |  version 3.11.2
4// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
5//
6// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
7// SPDX-License-Identifier: MIT
8
9// cmake/test.cmake selects the C++ standard versions with which to build a
10// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
11// When using macros that are only defined for particular versions of the standard
12// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
13// version macro in a comment close by, like this:
14// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
15
16#include "doctest_compatibility.h"
17
18// for some reason including this after the json header leads to linker errors with VS 2017...
19#include <locale>
20
21#define JSON_TESTS_PRIVATE
22#include <nlohmann/json.hpp>
23using json = nlohmann::json;
24using ordered_json = nlohmann::ordered_json;
25#ifdef JSON_TEST_NO_GLOBAL_UDLS
26    using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
27#endif
28
29#include <cstdio>
30#include <list>
31#include <type_traits>
32#include <utility>
33
34#ifdef JSON_HAS_CPP_17
35    #include <any>
36    #include <variant>
37#endif
38
39#ifdef JSON_HAS_CPP_20
40    #include <span>
41#endif
42
43// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
44DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
45DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
46
47/////////////////////////////////////////////////////////////////////
48// for #1021
49/////////////////////////////////////////////////////////////////////
50
51using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
52
53/////////////////////////////////////////////////////////////////////
54// for #1647
55/////////////////////////////////////////////////////////////////////
56namespace
57{
58struct NonDefaultFromJsonStruct
59{};
60
61inline bool operator==(NonDefaultFromJsonStruct const& /*unused*/, NonDefaultFromJsonStruct const& /*unused*/)
62{
63    return true;
64}
65
66enum class for_1647
67{
68    one,
69    two
70};
71
72// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): this is a false positive
73NLOHMANN_JSON_SERIALIZE_ENUM(for_1647,
74{
75    {for_1647::one, "one"},
76    {for_1647::two, "two"},
77})
78}  // namespace
79
80/////////////////////////////////////////////////////////////////////
81// for #1299
82/////////////////////////////////////////////////////////////////////
83
84struct Data
85{
86    Data() = default;
87    Data(std::string a_, std::string b_)
88        : a(std::move(a_))
89        , b(std::move(b_))
90    {}
91    std::string a{};
92    std::string b{};
93};
94
95void from_json(const json& j, Data& data);
96void from_json(const json& j, Data& data)
97{
98    j["a"].get_to(data.a);
99    j["b"].get_to(data.b);
100}
101
102bool operator==(Data const& lhs, Data const& rhs);
103bool operator==(Data const& lhs, Data const& rhs)
104{
105    return lhs.a == rhs.a && lhs.b == rhs.b;
106}
107
108//bool operator!=(Data const& lhs, Data const& rhs)
109//{
110//    return !(lhs == rhs);
111//}
112
113namespace nlohmann
114{
115template<>
116struct adl_serializer<NonDefaultFromJsonStruct>
117{
118    static NonDefaultFromJsonStruct from_json(json const& /*unused*/) noexcept
119    {
120        return {};
121    }
122};
123}  // namespace nlohmann
124
125/////////////////////////////////////////////////////////////////////
126// for #1805
127/////////////////////////////////////////////////////////////////////
128
129struct NotSerializableData
130{
131    int mydata;
132    float myfloat;
133};
134
135/////////////////////////////////////////////////////////////////////
136// for #2574
137/////////////////////////////////////////////////////////////////////
138
139struct NonDefaultConstructible
140{
141    explicit NonDefaultConstructible(int a)
142        : x(a)
143    {}
144    int x;
145};
146
147namespace nlohmann
148{
149template<>
150struct adl_serializer<NonDefaultConstructible>
151{
152    static NonDefaultConstructible from_json(json const& j)
153    {
154        return NonDefaultConstructible(j.get<int>());
155    }
156};
157}  // namespace nlohmann
158
159/////////////////////////////////////////////////////////////////////
160// for #2824
161/////////////////////////////////////////////////////////////////////
162
163class sax_no_exception : public nlohmann::detail::json_sax_dom_parser<json>
164{
165  public:
166    explicit sax_no_exception(json& j)
167        : nlohmann::detail::json_sax_dom_parser<json>(j, false)
168    {}
169
170    static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex)
171    {
172        error_string = new std::string(ex.what());  // NOLINT(cppcoreguidelines-owning-memory)
173        return false;
174    }
175
176    static std::string* error_string;
177};
178
179std::string* sax_no_exception::error_string = nullptr;
180
181/////////////////////////////////////////////////////////////////////
182// for #2982
183/////////////////////////////////////////////////////////////////////
184
185template<class T>
186class my_allocator : public std::allocator<T>
187{
188  public:
189    using std::allocator<T>::allocator;
190};
191
192/////////////////////////////////////////////////////////////////////
193// for #3077
194/////////////////////////////////////////////////////////////////////
195
196class FooAlloc
197{};
198
199class Foo
200{
201  public:
202    explicit Foo(const FooAlloc& /* unused */ = FooAlloc()) {}
203
204    bool value = false;
205};
206
207class FooBar
208{
209  public:
210    Foo foo{};
211};
212
213inline void from_json(const nlohmann::json& j, FooBar& fb)
214{
215    j.at("value").get_to(fb.foo.value);
216}
217
218/////////////////////////////////////////////////////////////////////
219// for #3171
220/////////////////////////////////////////////////////////////////////
221
222struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions)
223{
224    for_3171_base(const std::string& /*unused*/ = {}) {}
225    virtual ~for_3171_base() = default;
226
227    virtual void _from_json(const json& j)
228    {
229        j.at("str").get_to(str);
230    }
231
232    std::string str{};
233};
234
235struct for_3171_derived : public for_3171_base
236{
237    for_3171_derived() = default;
238    explicit for_3171_derived(const std::string& /*unused*/) { }
239};
240
241inline void from_json(const json& j, for_3171_base& tb)
242{
243    tb._from_json(j);
244}
245
246/////////////////////////////////////////////////////////////////////
247// for #3312
248/////////////////////////////////////////////////////////////////////
249
250#ifdef JSON_HAS_CPP_20
251struct for_3312
252{
253    std::string name;
254};
255
256inline void from_json(const json& j, for_3312& obj)
257{
258    j.at("name").get_to(obj.name);
259}
260#endif
261
262/////////////////////////////////////////////////////////////////////
263// for #3204
264/////////////////////////////////////////////////////////////////////
265
266struct for_3204_foo
267{
268    for_3204_foo() = default;
269    explicit for_3204_foo(std::string /*unused*/) {} // NOLINT(performance-unnecessary-value-param)
270};
271
272struct for_3204_bar
273{
274    enum constructed_from_t
275    {
276        constructed_from_none = 0,
277        constructed_from_foo = 1,
278        constructed_from_json = 2
279    };
280
281    explicit for_3204_bar(std::function<void(for_3204_foo)> /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param)
282        : constructed_from(constructed_from_foo) {}
283    explicit for_3204_bar(std::function<void(json)> /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param)
284        : constructed_from(constructed_from_json) {}
285
286    constructed_from_t constructed_from = constructed_from_none;
287};
288
289/////////////////////////////////////////////////////////////////////
290// for #3333
291/////////////////////////////////////////////////////////////////////
292
293struct for_3333 final
294{
295    for_3333(int x_ = 0, int y_ = 0) : x(x_), y(y_) {}
296
297    template <class T>
298    for_3333(const T& /*unused*/)
299    {
300        CHECK(false);
301    }
302
303    int x = 0;
304    int y = 0;
305};
306
307template <>
308inline for_3333::for_3333(const json& j)
309    : for_3333(j.value("x", 0), j.value("y", 0))
310{}
311
312TEST_CASE("regression tests 2")
313{
314    SECTION("issue #1001 - Fix memory leak during parser callback")
315    {
316        const auto* geojsonExample = R"(
317          { "type": "FeatureCollection",
318            "features": [
319              { "type": "Feature",
320                "geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
321                "properties": {"prop0": "value0"}
322                },
323              { "type": "Feature",
324                "geometry": {
325                  "type": "LineString",
326                  "coordinates": [
327                    [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
328                    ]
329                  },
330                "properties": {
331                  "prop0": "value0",
332                  "prop1": 0.0
333                  }
334                },
335              { "type": "Feature",
336                 "geometry": {
337                   "type": "Polygon",
338                   "coordinates": [
339                     [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
340                       [100.0, 1.0], [100.0, 0.0] ]
341                     ]
342                 },
343                 "properties": {
344                   "prop0": "value0",
345                   "prop1": {"this": "that"}
346                   }
347                 }
348               ]
349             })";
350
351        json::parser_callback_t cb = [&](int /*level*/, json::parse_event_t event, json & parsed) noexcept
352        {
353            // skip uninteresting events
354            if (event == json::parse_event_t::value && !parsed.is_primitive())
355            {
356                return false;
357            }
358
359            switch (event)
360            {
361                case json::parse_event_t::key:
362                {
363                    return true;
364                }
365                case json::parse_event_t::value:
366                {
367                    return false;
368                }
369                case json::parse_event_t::object_start:
370                {
371                    return true;
372                }
373                case json::parse_event_t::object_end:
374                {
375                    return false;
376                }
377                case json::parse_event_t::array_start:
378                {
379                    return true;
380                }
381                case json::parse_event_t::array_end:
382                {
383                    return false;
384                }
385
386                default:
387                {
388                    return true;
389                }
390            }
391        };
392
393        auto j = json::parse(geojsonExample, cb, true);
394        CHECK(j == json());
395    }
396
397    SECTION("issue #1021 - to/from_msgpack only works with standard typization")
398    {
399        float_json j = 1000.0;
400        CHECK(float_json::from_cbor(float_json::to_cbor(j)) == j);
401        CHECK(float_json::from_msgpack(float_json::to_msgpack(j)) == j);
402        CHECK(float_json::from_ubjson(float_json::to_ubjson(j)) == j);
403
404        float_json j2 = {1000.0, 2000.0, 3000.0};
405        CHECK(float_json::from_ubjson(float_json::to_ubjson(j2, true, true)) == j2);
406    }
407
408    SECTION("issue #1045 - Using STL algorithms with JSON containers with expected results?")
409    {
410        json diffs = nlohmann::json::array();
411        json m1{{"key1", 42}};
412        json m2{{"key2", 42}};
413        auto p1 = m1.items();
414        auto p2 = m2.items();
415
416        using it_type = decltype(p1.begin());
417
418        std::set_difference(
419            p1.begin(),
420            p1.end(),
421            p2.begin(),
422            p2.end(),
423            std::inserter(diffs, diffs.end()),
424            [&](const it_type & e1, const it_type & e2) -> bool
425        {
426            using comper_pair = std::pair<std::string, decltype(e1.value())>;              // Trying to avoid unneeded copy
427            return comper_pair(e1.key(), e1.value()) < comper_pair(e2.key(), e2.value());  // Using pair comper
428        });
429
430        CHECK(diffs.size() == 1);  // Note the change here, was 2
431    }
432
433#ifdef JSON_HAS_CPP_17
434    SECTION("issue #1292 - Serializing std::variant causes stack overflow")
435    {
436        static_assert(!std::is_constructible<json, std::variant<int, float>>::value, "unexpected value");
437    }
438#endif
439
440    SECTION("issue #1299 - compile error in from_json converting to container "
441            "with std::pair")
442    {
443        json j =
444        {
445            {"1", {{"a", "testa_1"}, {"b", "testb_1"}}},
446            {"2", {{"a", "testa_2"}, {"b", "testb_2"}}},
447            {"3", {{"a", "testa_3"}, {"b", "testb_3"}}},
448        };
449
450        std::map<std::string, Data> expected
451        {
452            {"1", {"testa_1", "testb_1"}},
453            {"2", {"testa_2", "testb_2"}},
454            {"3", {"testa_3", "testb_3"}},
455        };
456        const auto data = j.get<decltype(expected)>();
457        CHECK(expected == data);
458    }
459
460    SECTION("issue #1445 - buffer overflow in dumping invalid utf-8 strings")
461    {
462        SECTION("a bunch of -1, ensure_ascii=true")
463        {
464            const auto length = 300;
465
466            json dump_test;
467            dump_test["1"] = std::string(length, -1);
468
469            std::string expected = R"({"1":")";
470            for (int i = 0; i < length; ++i)
471            {
472                expected += "\\ufffd";
473            }
474            expected += "\"}";
475
476            auto s = dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
477            CHECK(s == expected);
478        }
479        SECTION("a bunch of -2, ensure_ascii=false")
480        {
481            const auto length = 500;
482
483            json dump_test;
484            dump_test["1"] = std::string(length, -2);
485
486            std::string expected = R"({"1":")";
487            for (int i = 0; i < length; ++i)
488            {
489                expected += "\xEF\xBF\xBD";
490            }
491            expected += "\"}";
492
493            auto s = dump_test.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
494            CHECK(s == expected);
495        }
496        SECTION("test case in issue #1445")
497        {
498            nlohmann::json dump_test;
499            const std::array<int, 108> data =
500            {
501                {109, 108, 103, 125, -122, -53, 115, 18, 3, 0, 102, 19, 1, 15, -110, 13, -3, -1, -81, 32, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -80, 2, 0, 0, 96, -118, 46, -116, 46, 109, -84, -87, 108, 14, 109, -24, -83, 13, -18, -51, -83, -52, -115, 14, 6, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 3, 0, 0, 0, 35, -74, -73, 55, 57, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, -96, -54, -28, -26}
502            };
503            std::string s;
504            for (int i : data)
505            {
506                s += static_cast<char>(i);
507            }
508            dump_test["1"] = s;
509            dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
510        }
511    }
512
513    SECTION("issue #1447 - Integer Overflow (OSS-Fuzz 12506)")
514    {
515        json j = json::parse("[-9223372036854775808]");
516        CHECK(j.dump() == "[-9223372036854775808]");
517    }
518
519    SECTION("issue #1708 - minimum value of int64_t can be outputted")
520    {
521        constexpr auto smallest = (std::numeric_limits<int64_t>::min)();
522        json j = smallest;
523        CHECK(j.dump() == std::to_string(smallest));
524    }
525
526    SECTION("issue #1727 - Contains with non-const lvalue json_pointer picks the wrong overload")
527    {
528        json j = {{"root", {{"settings", {{"logging", true}}}}}};
529
530        auto jptr1 = "/root/settings/logging"_json_pointer;
531        auto jptr2 = json::json_pointer{"/root/settings/logging"};
532
533        CHECK(j.contains(jptr1));
534        CHECK(j.contains(jptr2));
535    }
536
537    SECTION("issue #1647 - compile error when deserializing enum if both non-default from_json and non-member operator== exists for other type")
538    {
539        // does not compile on ICPC when targeting C++20
540#if !(defined(__INTEL_COMPILER) && __cplusplus >= 202000)
541        {
542            json j;
543            NonDefaultFromJsonStruct x(j);
544            NonDefaultFromJsonStruct y;
545            CHECK(x == y);
546        }
547#endif
548
549        auto val = nlohmann::json("one").get<for_1647>();
550        CHECK(val == for_1647::one);
551        json j = val;
552    }
553
554    SECTION("issue #1715 - json::from_cbor does not respect allow_exceptions = false when input is string literal")
555    {
556        SECTION("string literal")
557        {
558            json cbor = json::from_cbor("B", true, false);
559            CHECK(cbor.is_discarded());
560        }
561
562        SECTION("string array")
563        {
564            const std::array<char, 2> input = {{'B', 0x00}};
565            json cbor = json::from_cbor(input, true, false);
566            CHECK(cbor.is_discarded());
567        }
568
569        SECTION("std::string")
570        {
571            json cbor = json::from_cbor(std::string("B"), true, false);
572            CHECK(cbor.is_discarded());
573        }
574    }
575
576    SECTION("issue #1805 - A pair<T1, T2> is json constructible only if T1 and T2 are json constructible")
577    {
578        static_assert(!std::is_constructible<json, std::pair<std::string, NotSerializableData>>::value, "unexpected result");
579        static_assert(!std::is_constructible<json, std::pair<NotSerializableData, std::string>>::value, "unexpected result");
580        static_assert(std::is_constructible<json, std::pair<int, std::string>>::value, "unexpected result");
581    }
582    SECTION("issue #1825 - A tuple<Args..> is json constructible only if all T in Args are json constructible")
583    {
584        static_assert(!std::is_constructible<json, std::tuple<std::string, NotSerializableData>>::value, "unexpected result");
585        static_assert(!std::is_constructible<json, std::tuple<NotSerializableData, std::string>>::value, "unexpected result");
586        static_assert(std::is_constructible<json, std::tuple<int, std::string>>::value, "unexpected result");
587    }
588
589    SECTION("issue #1983 - JSON patch diff for op=add formation is not as per standard (RFC 6902)")
590    {
591        const auto source = R"({ "foo": [ "1", "2" ] })"_json;
592        const auto target = R"({"foo": [ "1", "2", "3" ]})"_json;
593        const auto result = json::diff(source, target);
594        CHECK(result.dump() == R"([{"op":"add","path":"/foo/-","value":"3"}])");
595    }
596
597    SECTION("issue #2067 - cannot serialize binary data to text JSON")
598    {
599        const std::array<unsigned char, 23> data = {{0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30}};
600        json j = json::from_msgpack(data.data(), data.size());
601        CHECK_NOTHROW(
602            j.dump(4,                             // Indent
603                   ' ',                           // Indent char
604                   false,                         // Ensure ascii
605                   json::error_handler_t::strict  // Error
606                  ));
607    }
608
609    SECTION("PR #2181 - regression bug with lvalue")
610    {
611        // see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060
612        json j{{"x", "test"}};
613        std::string defval = "default value";
614        auto val = j.value("x", defval);
615        auto val2 = j.value("y", defval);
616    }
617
618    SECTION("issue #2293 - eof doesn't cause parsing to stop")
619    {
620        std::vector<uint8_t> data =
621        {
622            0x7B,
623            0x6F,
624            0x62,
625            0x6A,
626            0x65,
627            0x63,
628            0x74,
629            0x20,
630            0x4F,
631            0x42
632        };
633        json result = json::from_cbor(data, true, false);
634        CHECK(result.is_discarded());
635    }
636
637    SECTION("issue #2315 - json.update and vector<pair>does not work with ordered_json")
638    {
639        nlohmann::ordered_json jsonAnimals = {{"animal", "dog"}};
640        nlohmann::ordered_json jsonCat = {{"animal", "cat"}};
641        jsonAnimals.update(jsonCat);
642        CHECK(jsonAnimals["animal"] == "cat");
643
644        auto jsonAnimals_parsed = nlohmann::ordered_json::parse(jsonAnimals.dump());
645        CHECK(jsonAnimals == jsonAnimals_parsed);
646
647        std::vector<std::pair<std::string, int64_t>> intData = {std::make_pair("aaaa", 11),
648                                                                std::make_pair("bbb", 222)
649                                                               };
650        nlohmann::ordered_json jsonObj;
651        for (const auto& data : intData)
652        {
653            jsonObj[data.first] = data.second;
654        }
655        CHECK(jsonObj["aaaa"] == 11);
656        CHECK(jsonObj["bbb"] == 222);
657    }
658
659    SECTION("issue #2330 - ignore_comment=true fails on multiple consecutive lines starting with comments")
660    {
661        std::string ss = "//\n//\n{\n}\n";
662        json j = json::parse(ss, nullptr, true, true);
663        CHECK(j.dump() == "{}");
664    }
665
666#ifdef JSON_HAS_CPP_20
667    SECTION("issue #2546 - parsing containers of std::byte")
668    {
669        const char DATA[] = R"("Hello, world!")"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
670        const auto s = std::as_bytes(std::span(DATA));
671        json j = json::parse(s);
672        CHECK(j.dump() == "\"Hello, world!\"");
673    }
674#endif
675
676    SECTION("issue #2574 - Deserialization to std::array, std::pair, and std::tuple with non-default constructable types fails")
677    {
678        SECTION("std::array")
679        {
680            {
681                json j = {7, 4};
682                auto arr = j.get<std::array<NonDefaultConstructible, 2>>();
683                CHECK(arr[0].x == 7);
684                CHECK(arr[1].x == 4);
685            }
686
687            {
688                json j = 7;
689                CHECK_THROWS_AS((j.get<std::array<NonDefaultConstructible, 1>>()), json::type_error);
690            }
691        }
692
693        SECTION("std::pair")
694        {
695            {
696                json j = {3, 8};
697                auto p = j.get<std::pair<NonDefaultConstructible, NonDefaultConstructible>>();
698                CHECK(p.first.x == 3);
699                CHECK(p.second.x == 8);
700            }
701
702            {
703                json j = {4, 1};
704                auto p = j.get<std::pair<int, NonDefaultConstructible>>();
705                CHECK(p.first == 4);
706                CHECK(p.second.x == 1);
707            }
708
709            {
710                json j = {6, 7};
711                auto p = j.get<std::pair<NonDefaultConstructible, int>>();
712                CHECK(p.first.x == 6);
713                CHECK(p.second == 7);
714            }
715
716            {
717                json j = 7;
718                CHECK_THROWS_AS((j.get<std::pair<NonDefaultConstructible, int>>()), json::type_error);
719            }
720        }
721
722        SECTION("std::tuple")
723        {
724            {
725                json j = {9};
726                auto t = j.get<std::tuple<NonDefaultConstructible>>();
727                CHECK(std::get<0>(t).x == 9);
728            }
729
730            {
731                json j = {9, 8, 7};
732                auto t = j.get<std::tuple<NonDefaultConstructible, int, NonDefaultConstructible>>();
733                CHECK(std::get<0>(t).x == 9);
734                CHECK(std::get<1>(t) == 8);
735                CHECK(std::get<2>(t).x == 7);
736            }
737
738            {
739                json j = 7;
740                CHECK_THROWS_AS((j.get<std::tuple<NonDefaultConstructible>>()), json::type_error);
741            }
742        }
743    }
744
745    SECTION("issue #2865 - ASAN detects memory leaks")
746    {
747        // the code below is expected to not leak memory
748        {
749            nlohmann::json o;
750            std::string s = "bar";
751
752            nlohmann::to_json(o["foo"], s);
753
754            nlohmann::json p = o;
755
756            // call to_json with a non-null JSON value
757            nlohmann::to_json(p["foo"], s);
758        }
759
760        {
761            nlohmann::json o;
762            std::string s = "bar";
763
764            nlohmann::to_json(o["foo"], s);
765
766            // call to_json with a non-null JSON value
767            nlohmann::to_json(o["foo"], s);
768        }
769    }
770
771    SECTION("issue #2824 - encoding of json::exception::what()")
772    {
773        json j;
774        sax_no_exception sax(j);
775
776        CHECK(!json::sax_parse("xyz", &sax));
777        CHECK(*sax_no_exception::error_string == "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'");
778        delete sax_no_exception::error_string;  // NOLINT(cppcoreguidelines-owning-memory)
779    }
780
781    SECTION("issue #2825 - Properly constrain the basic_json conversion operator")
782    {
783        static_assert(std::is_copy_assignable<nlohmann::ordered_json>::value, "ordered_json must be copy assignable");
784    }
785
786    SECTION("issue #2958 - Inserting in unordered json using a pointer retains the leading slash")
787    {
788        std::string p = "/root";
789
790        json test1;
791        test1[json::json_pointer(p)] = json::object();
792        CHECK(test1.dump() == "{\"root\":{}}");
793
794        ordered_json test2;
795        test2[ordered_json::json_pointer(p)] = json::object();
796        CHECK(test2.dump() == "{\"root\":{}}");
797
798        // json::json_pointer and ordered_json::json_pointer are the same type; behave as above
799        ordered_json test3;
800        test3[json::json_pointer(p)] = json::object();
801        CHECK(std::is_same<json::json_pointer::string_t, ordered_json::json_pointer::string_t>::value);
802        CHECK(test3.dump() == "{\"root\":{}}");
803    }
804
805    SECTION("issue #2982 - to_{binary format} does not provide a mechanism for specifying a custom allocator for the returned type")
806    {
807        std::vector<std::uint8_t, my_allocator<std::uint8_t>> my_vector;
808        json j = {1, 2, 3, 4};
809        json::to_cbor(j, my_vector);
810        json k = json::from_cbor(my_vector);
811        CHECK(j == k);
812    }
813
814#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
815    // JSON_HAS_CPP_17 (do not remove; see note at top of file)
816    SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ")
817    {
818        nlohmann::detail::std_fs::path text_path("/tmp/text.txt");
819        json j(text_path);
820
821        const auto j_path = j.get<nlohmann::detail::std_fs::path>();
822        CHECK(j_path == text_path);
823
824#if DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(8, 4, 0)
825        // only known to work on Clang and GCC >=8.4
826        CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
827#endif
828    }
829#endif
830
831    SECTION("issue #3077 - explicit constructor with default does not compile")
832    {
833        json j;
834        j[0]["value"] = true;
835        std::vector<FooBar> foo;
836        j.get_to(foo);
837    }
838
839    SECTION("issue #3108 - ordered_json doesn't support range based erase")
840    {
841        ordered_json j = {1, 2, 2, 4};
842
843        auto last = std::unique(j.begin(), j.end());
844        j.erase(last, j.end());
845
846        CHECK(j.dump() == "[1,2,4]");
847
848        j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val)
849        {
850            return val == 2;
851        }), j.end());
852
853        CHECK(j.dump() == "[1,4]");
854    }
855
856    SECTION("issue #3343 - json and ordered_json are not interchangable")
857    {
858        json::object_t jobj({ { "product", "one" } });
859        ordered_json::object_t ojobj({{"product", "one"}});
860
861        auto jit = jobj.begin();
862        auto ojit = ojobj.begin();
863
864        CHECK(jit->first == ojit->first);
865        CHECK(jit->second.get<std::string>() == ojit->second.get<std::string>());
866    }
867
868    SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed")
869    {
870        json j{{ "str", "value"}};
871
872        // failed with: error: no match for ‘operator=’ (operand types are ‘for_3171_derived’ and ‘const nlohmann::basic_json<>::string_t’
873        //                                               {aka ‘const std::__cxx11::basic_string<char>’})
874        //                  s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
875        auto td = j.get<for_3171_derived>();
876
877        CHECK(td.str == "value");
878    }
879
880#ifdef JSON_HAS_CPP_20
881    SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20")
882    {
883        // see test for #3171
884        ordered_json j = {{"name", "class"}};
885        for_3312 obj{};
886
887        j.get_to(obj);
888
889        CHECK(obj.name == "class");
890    }
891#endif
892
893#if defined(JSON_HAS_CPP_17) && JSON_USE_IMPLICIT_CONVERSIONS
894    SECTION("issue #3428 - Error occurred when converting nlohmann::json to std::any")
895    {
896        json j;
897        std::any a1 = j;
898        std::any&& a2 = j;
899
900        CHECK(a1.type() == typeid(j));
901        CHECK(a2.type() == typeid(j));
902    }
903#endif
904
905    SECTION("issue #3204 - ambiguous regression")
906    {
907        for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param)
908        for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param)
909
910        CHECK(bar_from_foo.constructed_from == for_3204_bar::constructed_from_foo);
911        CHECK(bar_from_json.constructed_from == for_3204_bar::constructed_from_json);
912    }
913
914    SECTION("issue #3333 - Ambiguous conversion from nlohmann::basic_json<> to custom class")
915    {
916        const json j
917        {
918            {"x", 1},
919            {"y", 2}
920        };
921        for_3333 p = j;
922
923        CHECK(p.x == 1);
924        CHECK(p.y == 2);
925    }
926}
927
928DOCTEST_CLANG_SUPPRESS_WARNING_POP
929