Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion data/include/join/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ namespace join
* @return sorted object members.
*/
template <typename Type, JsonWriteMode Mode = WriteMode>
std::vector<std::reference_wrapper<const Type>> members (const View<Type>& object) const
std::vector<std::reference_wrapper<const Type>> members (const View<Type>& object)
requires (Mode == JsonWriteMode::Canonical)
{
std::vector<std::reference_wrapper<const Type>> sorted (object.begin (), object.end ());
Expand All @@ -517,6 +517,13 @@ namespace join
return utf8::compareUtf16Order (sa.data (), sa.data () + sa.size (), sb.data (),
sb.data () + sb.size ()) < 0;
});
for (size_t i = 1; i < sorted.size (); ++i)
{
if (sorted[i].get ().first.isString () && (sorted[i].get ().first == sorted[i - 1].get ().first))
{
_duplicate = true;
}
}
return sorted;
}

Expand Down Expand Up @@ -603,6 +610,15 @@ namespace join
reset ();
return -1;
}
if constexpr (WriteMode == JsonWriteMode::Canonical)
{
if (JOIN_UNLIKELY (_duplicate))
{
join::lastError = make_error_code (SaxErrc::DuplicateKey);
reset ();
return -1;
}
}
--_depth;
if (!_first)
{
Expand Down Expand Up @@ -633,6 +649,7 @@ namespace join
_size = 0;
_depth = 0;
_first = true;
_duplicate = false;
}

protected:
Expand Down Expand Up @@ -992,6 +1009,9 @@ namespace join

/// is first element.
bool _first = true;

/// duplicate key found in a canonical object.
bool _duplicate = false;
};

/// JSON default writer.
Expand Down
1 change: 1 addition & 0 deletions data/include/join/sax_error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace join
StackOverflow = 1, /**< Stack overflow. */
InvalidParent, /**< Parent not an array nor an object. */
InvalidKey, /**< Key is invalid. */
DuplicateKey, /**< Key is duplicated. */
InvalidValue, /**< Value is invalid. */
InvalidEncoding, /**< Character encoding is invalid. */
ExtraData, /**< Extra data detected. */
Expand Down
2 changes: 2 additions & 0 deletions data/src/sax_error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ std::string SaxCategory::message (int code) const
return "parent not an array nor an object";
case SaxErrc::InvalidKey:
return "key is invalid";
case SaxErrc::DuplicateKey:
return "key is duplicated";
case SaxErrc::InvalidValue:
return "value is invalid";
case SaxErrc::InvalidEncoding:
Expand Down
19 changes: 19 additions & 0 deletions data/tests/json_canonicalizer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ using join::Value;
using join::Array;
using join::Object;
using join::JsonCanonicalizer;
using join::JsonReader;
using join::JsonErrc;
using join::SaxErrc;

Expand Down Expand Up @@ -63,6 +64,24 @@ TEST (JsonCanonicalizer, members)
EXPECT_EQ (out.str (), "{\"a\":2,\"b\":1}");
EXPECT_EQ (value.getObject ()[0].first, "b");
EXPECT_EQ (value.getObject ()[1].first, "a");

Value duplicate;
ASSERT_EQ (duplicate.deserialize<JsonReader> (std::string ("{\"a\":1,\"b\":2,\"a\":3}")), 0)
<< join::lastError.message ();
out.str ("");
ASSERT_EQ (duplicate.serialize (writer), -1);
EXPECT_EQ (join::lastError, SaxErrc::DuplicateKey);
EXPECT_TRUE (out.str ().empty ());

Value nested;
ASSERT_EQ (nested.deserialize<JsonReader> (std::string ("{\"x\":{\"a\":1,\"a\":2}}")), 0)
<< join::lastError.message ();
ASSERT_EQ (nested.serialize (writer), -1);
EXPECT_EQ (join::lastError, SaxErrc::DuplicateKey);
EXPECT_TRUE (out.str ().empty ());

ASSERT_EQ (value.serialize (writer), 0) << join::lastError.message ();
EXPECT_EQ (out.str (), "{\"a\":2,\"b\":1}");
}

/**
Expand Down
8 changes: 5 additions & 3 deletions data/tests/sax_error_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ TEST (SaxCategory, message)
EXPECT_STREQ (SaxCategory ().message (static_cast<int> (SaxErrc::InvalidParent)).c_str (),
"parent not an array nor an object");
EXPECT_STREQ (SaxCategory ().message (static_cast<int> (SaxErrc::InvalidKey)).c_str (), "key is invalid");
EXPECT_STREQ (SaxCategory ().message (static_cast<int> (SaxErrc::DuplicateKey)).c_str (), "key is duplicated");
EXPECT_STREQ (SaxCategory ().message (static_cast<int> (SaxErrc::InvalidValue)).c_str (), "value is invalid");
EXPECT_STREQ (SaxCategory ().message (static_cast<int> (SaxErrc::InvalidEncoding)).c_str (),
"character encoding is invalid");
Expand All @@ -65,9 +66,10 @@ TEST (SaxCategory, default_error_condition)
EXPECT_EQ (SaxCategory ().default_error_condition (1).message (), "stack overflow");
EXPECT_EQ (SaxCategory ().default_error_condition (2).message (), "parent not an array nor an object");
EXPECT_EQ (SaxCategory ().default_error_condition (3).message (), "key is invalid");
EXPECT_EQ (SaxCategory ().default_error_condition (4).message (), "value is invalid");
EXPECT_EQ (SaxCategory ().default_error_condition (5).message (), "character encoding is invalid");
EXPECT_EQ (SaxCategory ().default_error_condition (6).message (), "extra data detected");
EXPECT_EQ (SaxCategory ().default_error_condition (4).message (), "key is duplicated");
EXPECT_EQ (SaxCategory ().default_error_condition (5).message (), "value is invalid");
EXPECT_EQ (SaxCategory ().default_error_condition (6).message (), "character encoding is invalid");
EXPECT_EQ (SaxCategory ().default_error_condition (7).message (), "extra data detected");
}

/**
Expand Down
Loading