Skip to content

Commit 66bc02f

Browse files
authored
Guarantee set ordering in to_json (#1784)
Fixes: #1783 Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 732e6c1 commit 66bc02f

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

src/core/json/include/sourcemeta/core/json_auto.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <sourcemeta/core/json_value.h>
55

6+
#include <algorithm> // std::sort
67
#include <concepts> // std::same_as, std::constructible_from
78
#include <functional> // std::function
89
#include <optional> // std::optional
@@ -75,6 +76,16 @@ concept json_auto_map_like =
7576
!json_auto_has_method<T> &&
7677
std::is_same_v<typename T::key_type, JSON::String>;
7778

79+
/// @ingroup json
80+
template <typename, typename = void>
81+
struct json_auto_has_reverse_iterator : std::false_type {};
82+
83+
/// @ingroup json
84+
template <typename T>
85+
struct json_auto_has_reverse_iterator<T,
86+
std::void_t<typename T::reverse_iterator>>
87+
: std::true_type {};
88+
7889
/// @ingroup json
7990
template <typename T>
8091
concept json_auto_tuple_mono = requires {
@@ -151,6 +162,11 @@ auto to_json(typename T::const_iterator begin, typename T::const_iterator end)
151162
result.push_back(to_json(*iterator));
152163
}
153164

165+
// To guarantee ordering across implementations
166+
if constexpr (!json_auto_has_reverse_iterator<T>::value) {
167+
std::sort(result.as_array().begin(), result.as_array().end());
168+
}
169+
154170
return result;
155171
}
156172

@@ -166,6 +182,11 @@ auto to_json(
166182
result.push_back(callback(*iterator));
167183
}
168184

185+
// To guarantee ordering across implementations
186+
if constexpr (!json_auto_has_reverse_iterator<T>::value) {
187+
std::sort(result.as_array().begin(), result.as_array().end());
188+
}
189+
169190
return result;
170191
}
171192

test/json/json_to_json_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
#include <map>
44
#include <optional>
5+
#include <set>
56
#include <string>
67
#include <tuple>
8+
#include <unordered_set>
79
#include <utility>
810
#include <vector>
911

@@ -176,6 +178,28 @@ TEST(JSON_to_json, map_without_iterators_and_transform) {
176178
EXPECT_EQ(result, expected);
177179
}
178180

181+
TEST(JSON_to_json, set_without_iterators) {
182+
const std::set<std::string> value{"foo", "bar", "baz"};
183+
const auto result{sourcemeta::core::to_json(value)};
184+
185+
const auto expected{sourcemeta::core::parse_json(R"JSON([
186+
"bar", "baz", "foo"
187+
])JSON")};
188+
189+
EXPECT_EQ(result, expected);
190+
}
191+
192+
TEST(JSON_to_json, unordered_set_without_iterators) {
193+
const std::unordered_set<std::string> value{"foo", "bar", "baz"};
194+
const auto result{sourcemeta::core::to_json(value)};
195+
196+
const auto expected{sourcemeta::core::parse_json(R"JSON([
197+
"bar", "baz", "foo"
198+
])JSON")};
199+
200+
EXPECT_EQ(result, expected);
201+
}
202+
179203
TEST(JSON_to_json, pair) {
180204
const std::pair<std::string, std::size_t> value{"foo", 1};
181205
const auto result{sourcemeta::core::to_json(value)};

0 commit comments

Comments
 (0)