From f106d6ba3abcee65765dcd5734fb670d539920d6 Mon Sep 17 00:00:00 2001 From: Ivan Le Lann Date: Wed, 29 May 2019 01:08:05 +0200 Subject: [PATCH] allow to use unordered maps to have stable key order --- single_include/nlohmann/json.hpp | 35 +++++++----- test/src/unit-unorderedmap.cpp | 94 ++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 test/src/unit-unorderedmap.cpp diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 1f82da447e..88201f7c62 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12881,6 +12881,27 @@ class serializer namespace nlohmann { +template < + template class ObjectType, + class StringType, + template class AllocatorType, + class BasicJsonType> +struct object_traits +{ +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + using object_t = ObjectType>>; +}; + /*! @brief a class to store JSON values @@ -13186,14 +13207,6 @@ class basic_json /// the template arguments passed to class @ref basic_json. /// @{ -#if defined(JSON_HAS_CPP_14) - // Use transparent comparator if possible, combined with perfect forwarding - // on find() and count() calls prevents unnecessary string construction. - using object_comparator_t = std::less<>; -#else - using object_comparator_t = std::less; -#endif - /*! @brief a type for an object @@ -13277,11 +13290,7 @@ class basic_json 7159](http://rfc7159.net/rfc7159), because any order implements the specified "unordered" nature of JSON objects. */ - using object_t = ObjectType>>; + using object_t = typename object_traits::object_t; /*! @brief a type for an array diff --git a/test/src/unit-unorderedmap.cpp b/test/src/unit-unorderedmap.cpp new file mode 100644 index 0000000000..fb7f97837a --- /dev/null +++ b/test/src/unit-unorderedmap.cpp @@ -0,0 +1,94 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.6.1 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "doctest_compatibility.h" + +#include +#include + +namespace +{ + +// a dumb map preserving insertion order +// written solely for the purpose of this test +// in real life, consider something like https://github.com/Tessil/ordered-map + +template >> +class insertion_ordered_map +{ +private: + using data_t = std::list, Alloc>; + data_t data; + +public: + using key_type = Key; + using value_type = std::pair; + using size_type = typename data_t::size_type; + using iterator = typename data_t::iterator; + using const_iterator = typename data_t::const_iterator; + + size_type size() const { return data.size(); } + size_type max_size() const { return data.max_size(); } + bool empty() const { return data.empty(); } + const_iterator cbegin() const { return data.cbegin(); } + const_iterator cend() const { return data.cend(); } + iterator begin() { return data.begin(); } + iterator end() { return data.end(); } + T& operator[](const key_type & k) { + auto e = end(); + auto pos = std::find_if(begin(), e, [&] (std::pair & p) { return p.first == k; }); + return (pos == e ? data.emplace(e, k, T{}) : pos)->second; + } + iterator erase(const_iterator pos) { return data.erase(pos); } +}; +} + +namespace nlohmann +{ + template class AllocatorType, class BasicJsonType> + struct object_traits + { + using object_t = insertion_ordered_map>>; + }; +} + +TEST_CASE("use of unordered maps") +{ + SECTION("order is stable with insertion_ordered_map") + { + const auto text = "{\"name\":1,\"id\":2}"; + CHECK(text == nlohmann::basic_json::parse(text).dump()); + } + + SECTION("keys are sorted with std::map") + { + const auto text = "{\"name\":1,\"id\":2}"; + CHECK("{\"id\":2,\"name\":1}" == nlohmann::json::parse(text).dump()); + } +}