Skip to content

Commit

Permalink
Support value setting for nonexistent paths in JSON.SET (#2178)
Browse files Browse the repository at this point in the history
  • Loading branch information
PragmaTwice authored Mar 18, 2024
1 parent 20e01c4 commit 9d83653
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 3 deletions.
32 changes: 29 additions & 3 deletions src/types/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,34 @@ struct JsonValue {

Status Set(std::string_view path, JsonValue &&new_value) {
try {
jsoncons::jsonpath::json_replace(value, path, [&new_value](const std::string & /*path*/, jsoncons::json &origin) {
origin = new_value.value;
});
bool is_set = false;
jsoncons::jsonpath::json_replace(value, path,
[&new_value, &is_set](const std::string & /*path*/, jsoncons::json &origin) {
origin = new_value.value;
is_set = true;
});

if (!is_set) {
// NOTE: this is a workaround since jsonpath doesn't support replace for nonexistent paths in jsoncons
// and in this workaround we can only accept normalized path
// refer to https://github.com/danielaparker/jsoncons/issues/496
jsoncons::jsonpath::json_location location = jsoncons::jsonpath::json_location::parse(path);
jsoncons::jsonpointer::json_pointer ptr{};

for (const auto &element : location) {
if (element.has_name())
ptr /= element.name();
else {
ptr /= element.index();
}
}

jsoncons::jsonpointer::replace(value, ptr, new_value.value, true);
}
} catch (const jsoncons::jsonpath::jsonpath_error &e) {
return {Status::NotOK, e.what()};
} catch (const jsoncons::jsonpointer::jsonpointer_error &e) {
return {Status::NotOK, e.what()};
}

return Status::OK();
Expand Down Expand Up @@ -413,6 +436,9 @@ struct JsonValue {
bool not_exists = jsoncons::jsonpath::json_query(value, path).empty();

if (not_exists) {
// NOTE: this is a workaround since jsonpath doesn't support replace for nonexistent paths in jsoncons
// and in this workaround we can only accept normalized path
// refer to https://github.com/danielaparker/jsoncons/issues/496
jsoncons::jsonpath::json_location location = jsoncons::jsonpath::json_location::parse(path);
jsoncons::jsonpointer::json_pointer ptr{};

Expand Down
8 changes: 8 additions & 0 deletions tests/cppunit/types/json_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ TEST_F(RedisJsonTest, Set) {
ASSERT_EQ(json_val_.Dump().GetValue(), "[{},[]]");
ASSERT_THAT(json_->Set(key_, "$[1]", "invalid").ToString(), MatchesRegex(".*syntax_error.*"));
ASSERT_TRUE(json_->Del(key_, "$", &result).ok());

ASSERT_TRUE(json_->Set(key_, "$", R"({"a":1})").ok());
ASSERT_TRUE(json_->Set(key_, "$.b", "2").ok());
ASSERT_TRUE(json_->Set(key_, "$.c", R"({"x":3})").ok());
ASSERT_TRUE(json_->Set(key_, "$.c.y", "4").ok());

ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
ASSERT_EQ(json_val_.value, jsoncons::json::parse(R"({"a":1,"b":2,"c":{"x":3,"y":4}})"));
}

TEST_F(RedisJsonTest, Get) {
Expand Down

0 comments on commit 9d83653

Please sign in to comment.