1
0
Fork 0
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:
iko1 2023-09-24 16:40:13 +02:00 committed by GitHub
parent bb77de7551
commit 19783face5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 211 additions and 8 deletions

View file

@ -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

View 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;

View file

@ -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"
) )

View file

@ -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;

View file

@ -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) {