mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2024-12-15 17:51:06 +00:00
feat(json): support JSON.GET pretty print options (#1832)
* parse json.get pretty print options Signed-off-by: iko1 <me@remotecpp.dev> * finalize json.get sub args * set indent size to 1 & add unit tests cases --------- Signed-off-by: iko1 <me@remotecpp.dev>
This commit is contained in:
parent
bb77de7551
commit
19783face5
5 changed files with 211 additions and 8 deletions
|
@ -2,7 +2,8 @@ default_stages: [commit]
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)(
|
(?x)(
|
||||||
src/redis/.* |
|
src/redis/.* |
|
||||||
contrib/charts/dragonfly/ci/.*
|
contrib/charts/dragonfly/ci/.* |
|
||||||
|
patches/.*
|
||||||
)
|
)
|
||||||
repos:
|
repos:
|
||||||
- repo: local
|
- repo: local
|
||||||
|
|
110
patches/jsoncons-v0.171.0.patch
Normal file
110
patches/jsoncons-v0.171.0.patch
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
diff --git a/include/jsoncons/json_encoder.hpp b/include/jsoncons/json_encoder.hpp
|
||||||
|
index 6a1daba63..d20673171 100644
|
||||||
|
--- a/include/jsoncons/json_encoder.hpp
|
||||||
|
+++ b/include/jsoncons/json_encoder.hpp
|
||||||
|
@@ -355,6 +355,7 @@ namespace detail {
|
||||||
|
colon_str_.push_back(':');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
+ colon_str_.append(options.after_key_chars());
|
||||||
|
switch (options.spaces_around_comma())
|
||||||
|
{
|
||||||
|
case spaces_option::space_after:
|
||||||
|
@@ -1021,9 +1022,9 @@ namespace detail {
|
||||||
|
sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
|
||||||
|
for (int i = 0; i < indent_amount_; ++i)
|
||||||
|
{
|
||||||
|
- sink_.push_back(' ');
|
||||||
|
+ sink_.append(options_.indent_chars().data(), options_.indent_chars().length());
|
||||||
|
}
|
||||||
|
- column_ = indent_amount_;
|
||||||
|
+ column_ = indent_amount_ * options_.new_line_chars().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
void new_line(std::size_t len)
|
||||||
|
@@ -1031,7 +1032,7 @@ namespace detail {
|
||||||
|
sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
|
||||||
|
for (std::size_t i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
- sink_.push_back(' ');
|
||||||
|
+ sink_.append(options_.indent_chars().data(), options_.indent_chars().length());
|
||||||
|
}
|
||||||
|
column_ = len;
|
||||||
|
}
|
||||||
|
diff --git a/include/jsoncons/json_options.hpp b/include/jsoncons/json_options.hpp
|
||||||
|
index 58dcf3ba3..74d5ab217 100644
|
||||||
|
--- a/include/jsoncons/json_options.hpp
|
||||||
|
+++ b/include/jsoncons/json_options.hpp
|
||||||
|
@@ -425,6 +425,8 @@ private:
|
||||||
|
uint8_t indent_size_;
|
||||||
|
std::size_t line_length_limit_;
|
||||||
|
string_type new_line_chars_;
|
||||||
|
+ string_type after_key_chars_;
|
||||||
|
+ string_type indent_chars_;
|
||||||
|
public:
|
||||||
|
basic_json_encode_options()
|
||||||
|
: escape_all_non_ascii_(false),
|
||||||
|
@@ -445,6 +447,7 @@ public:
|
||||||
|
line_length_limit_(line_length_limit_default)
|
||||||
|
{
|
||||||
|
new_line_chars_.push_back('\n');
|
||||||
|
+ indent_chars_.push_back('\t');
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_json_encode_options(const basic_json_encode_options&) = default;
|
||||||
|
@@ -467,7 +470,9 @@ public:
|
||||||
|
precision_(other.precision_),
|
||||||
|
indent_size_(other.indent_size_),
|
||||||
|
line_length_limit_(other.line_length_limit_),
|
||||||
|
- new_line_chars_(std::move(other.new_line_chars_))
|
||||||
|
+ new_line_chars_(std::move(other.new_line_chars_)),
|
||||||
|
+ after_key_chars_(std::move(other.after_key_chars_)),
|
||||||
|
+ indent_chars_(std::move(other.indent_chars))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -515,6 +520,16 @@ public:
|
||||||
|
return new_line_chars_;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ string_type after_key_chars() const
|
||||||
|
+ {
|
||||||
|
+ return after_key_chars_;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ string_type indent_chars() const
|
||||||
|
+ {
|
||||||
|
+ return indent_chars_;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
std::size_t line_length_limit() const
|
||||||
|
{
|
||||||
|
return line_length_limit_;
|
||||||
|
@@ -599,6 +614,8 @@ public:
|
||||||
|
using basic_json_encode_options<CharT>::pad_inside_object_braces;
|
||||||
|
using basic_json_encode_options<CharT>::pad_inside_array_brackets;
|
||||||
|
using basic_json_encode_options<CharT>::new_line_chars;
|
||||||
|
+ using basic_json_encode_options<CharT>::after_key_chars;
|
||||||
|
+ using basic_json_encode_options<CharT>::indent_chars;
|
||||||
|
using basic_json_encode_options<CharT>::line_length_limit;
|
||||||
|
using basic_json_encode_options<CharT>::float_format;
|
||||||
|
using basic_json_encode_options<CharT>::precision;
|
||||||
|
@@ -761,6 +778,18 @@ public:
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ basic_json_options& after_key_chars(const string_type& value)
|
||||||
|
+ {
|
||||||
|
+ this->after_key_chars_ = value;
|
||||||
|
+ return *this;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ basic_json_options& indent_chars(const string_type& value)
|
||||||
|
+ {
|
||||||
|
+ this->indent_chars_ = value;
|
||||||
|
+ return *this;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
basic_json_options& lossless_number(bool value)
|
||||||
|
{
|
||||||
|
this->lossless_number_ = value;
|
|
@ -73,6 +73,7 @@ set(REFLEX "${THIRD_PARTY_LIB_DIR}/reflex/bin/reflex")
|
||||||
add_third_party(
|
add_third_party(
|
||||||
jsoncons
|
jsoncons
|
||||||
URL https://github.com/danielaparker/jsoncons/archive/refs/tags/v0.171.0.tar.gz
|
URL https://github.com/danielaparker/jsoncons/archive/refs/tags/v0.171.0.tar.gz
|
||||||
|
PATCH_COMMAND patch -p1 -i "${CMAKE_SOURCE_DIR}/patches/jsoncons-v0.171.0.patch"
|
||||||
CMAKE_PASS_FLAGS "-DJSONCONS_BUILD_TESTS=OFF -DJSONCONS_HAS_POLYMORPHIC_ALLOCATOR=ON"
|
CMAKE_PASS_FLAGS "-DJSONCONS_BUILD_TESTS=OFF -DJSONCONS_HAS_POLYMORPHIC_ALLOCATOR=ON"
|
||||||
LIB "none"
|
LIB "none"
|
||||||
)
|
)
|
||||||
|
|
|
@ -390,7 +390,8 @@ void SendJsonValue(ConnectionContext* cntx, const JsonType& j) {
|
||||||
}
|
}
|
||||||
|
|
||||||
OpResult<string> OpGet(const OpArgs& op_args, string_view key,
|
OpResult<string> OpGet(const OpArgs& op_args, string_view key,
|
||||||
vector<pair<string_view, JsonExpression>> expressions) {
|
vector<pair<string_view, JsonExpression>> expressions, bool should_format,
|
||||||
|
const OptString& indent, const OptString& new_line, const OptString& space) {
|
||||||
OpResult<JsonType*> result = GetJson(op_args, key);
|
OpResult<JsonType*> result = GetJson(op_args, key);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return result.status();
|
return result.status();
|
||||||
|
@ -402,8 +403,39 @@ OpResult<string> OpGet(const OpArgs& op_args, string_view key,
|
||||||
// means we just brings all values
|
// means we just brings all values
|
||||||
return json_entry.to_string();
|
return json_entry.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json_options options;
|
||||||
|
if (should_format) {
|
||||||
|
options.spaces_around_comma(spaces_option::no_spaces)
|
||||||
|
.spaces_around_colon(spaces_option::no_spaces)
|
||||||
|
.object_array_line_splits(line_split_kind::multi_line)
|
||||||
|
.indent_size(1)
|
||||||
|
.new_line_chars("");
|
||||||
|
|
||||||
|
if (indent) {
|
||||||
|
options.indent_chars(*indent);
|
||||||
|
} else {
|
||||||
|
options.indent_size(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_line) {
|
||||||
|
options.new_line_chars(*new_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (space) {
|
||||||
|
options.after_key_chars(*space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (expressions.size() == 1) {
|
if (expressions.size() == 1) {
|
||||||
json out = expressions[0].second.evaluate(json_entry);
|
json out = expressions[0].second.evaluate(json_entry);
|
||||||
|
if (should_format) {
|
||||||
|
json_printable jp(out, options, indenting::indent);
|
||||||
|
std::stringstream ss;
|
||||||
|
jp.dump(ss);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
return out.as<string>();
|
return out.as<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +445,13 @@ OpResult<string> OpGet(const OpArgs& op_args, string_view key,
|
||||||
out[expr.first] = eval;
|
out[expr.first] = eval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (should_format) {
|
||||||
|
json_printable jp(out, options, indenting::indent);
|
||||||
|
std::stringstream ss;
|
||||||
|
jp.dump(ss);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
return out.as<string>();
|
return out.as<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1748,22 +1787,52 @@ void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) {
|
||||||
DCHECK_GE(args.size(), 1U);
|
DCHECK_GE(args.size(), 1U);
|
||||||
string_view key = ArgS(args, 0);
|
string_view key = ArgS(args, 0);
|
||||||
|
|
||||||
|
OptString indent;
|
||||||
|
OptString new_line;
|
||||||
|
OptString space;
|
||||||
vector<pair<string_view, JsonExpression>> expressions;
|
vector<pair<string_view, JsonExpression>> expressions;
|
||||||
|
|
||||||
for (size_t i = 1; i < args.size(); ++i) {
|
for (size_t i = 1; i < args.size(); ++i) {
|
||||||
string_view path = ArgS(args, i);
|
string_view param = ArgS(args, i);
|
||||||
|
if (absl::EqualsIgnoreCase(param, "space")) {
|
||||||
|
if (++i >= args.size()) {
|
||||||
|
return (*cntx)->SendError(facade::WrongNumArgsError(cntx->cid->name()),
|
||||||
|
facade::kSyntaxErrType);
|
||||||
|
} else {
|
||||||
|
space = ArgS(args, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (absl::EqualsIgnoreCase(param, "newline")) {
|
||||||
|
if (++i >= args.size()) {
|
||||||
|
return (*cntx)->SendError(facade::WrongNumArgsError(cntx->cid->name()),
|
||||||
|
facade::kSyntaxErrType);
|
||||||
|
} else {
|
||||||
|
new_line = ArgS(args, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (absl::EqualsIgnoreCase(param, "indent")) {
|
||||||
|
if (++i >= args.size()) {
|
||||||
|
return (*cntx)->SendError(facade::WrongNumArgsError(cntx->cid->name()),
|
||||||
|
facade::kSyntaxErrType);
|
||||||
|
} else {
|
||||||
|
indent = ArgS(args, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
error_code ec;
|
error_code ec;
|
||||||
JsonExpression expr = ParseJsonPath(path, &ec);
|
JsonExpression expr = ParseJsonPath(param, &ec);
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
LOG(WARNING) << "path '" << path << "': Invalid JSONPath syntax: " << ec.message();
|
LOG(WARNING) << "path '" << param << "': Invalid JSONPath syntax: " << ec.message();
|
||||||
return (*cntx)->SendError(kSyntaxErr);
|
return (*cntx)->SendError(kSyntaxErr);
|
||||||
}
|
}
|
||||||
expressions.emplace_back(path, move(expr));
|
expressions.emplace_back(param, move(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool should_format = (indent || new_line || space);
|
||||||
auto cb = [&](Transaction* t, EngineShard* shard) {
|
auto cb = [&](Transaction* t, EngineShard* shard) {
|
||||||
return OpGet(t->GetOpArgs(shard), key, move(expressions));
|
return OpGet(t->GetOpArgs(shard), key, move(expressions), should_format, indent, new_line,
|
||||||
|
space);
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction* trans = cntx->transaction;
|
Transaction* trans = cntx->transaction;
|
||||||
|
|
|
@ -120,6 +120,28 @@ TEST_F(JsonFamilyTest, SetGetFromPhonebook) {
|
||||||
|
|
||||||
resp = Run({"JSON.GET", "json", "$..phoneNumbers[1].*"});
|
resp = Run({"JSON.GET", "json", "$..phoneNumbers[1].*"});
|
||||||
EXPECT_EQ(resp, R"(["646 555-4567","office"])");
|
EXPECT_EQ(resp, R"(["646 555-4567","office"])");
|
||||||
|
|
||||||
|
resp = Run({"JSON.GET", "json", "$.address.*", "INDENT", "indent", "NEWLINE", "newline"});
|
||||||
|
EXPECT_EQ(
|
||||||
|
resp,
|
||||||
|
R"([newlineindent"New York",newlineindent"NY",newlineindent"21 2nd Street",newlineindent"10021-3100"newline])");
|
||||||
|
|
||||||
|
resp = Run({"JSON.GET", "json", "$.address", "SPACE", "space"});
|
||||||
|
EXPECT_EQ(
|
||||||
|
resp,
|
||||||
|
R"([{"city":space"New York","state":space"NY","street":space"21 2nd Street","zipcode":space"10021-3100"}])");
|
||||||
|
|
||||||
|
resp = Run({"JSON.GET", "json", "$.firstName", "$.age", "$.lastName", "INDENT", "indent",
|
||||||
|
"NEWLINE", "newline", "SPACE", "space"});
|
||||||
|
EXPECT_EQ(
|
||||||
|
resp,
|
||||||
|
R"({newlineindent"$.age":space[newlineindentindent27newlineindent],newlineindent"$.firstName":space[newlineindentindent"John"newlineindent],newlineindent"$.lastName":space[newlineindentindent"Smith"newlineindent]newline})");
|
||||||
|
|
||||||
|
resp =
|
||||||
|
Run({"JSON.GET", "json", "$..phoneNumbers.*", "INDENT", "t", "NEWLINE", "s", "SPACE", "s"});
|
||||||
|
EXPECT_EQ(
|
||||||
|
resp,
|
||||||
|
R"([st{stt"number":s"212 555-1234",stt"type":s"home"st},st{stt"number":s"646 555-4567",stt"type":s"office"st}s])");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(JsonFamilyTest, Type) {
|
TEST_F(JsonFamilyTest, Type) {
|
||||||
|
|
Loading…
Reference in a new issue