mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2024-12-14 11:58:02 +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: |
|
||||
(?x)(
|
||||
src/redis/.* |
|
||||
contrib/charts/dragonfly/ci/.*
|
||||
contrib/charts/dragonfly/ci/.* |
|
||||
patches/.*
|
||||
)
|
||||
repos:
|
||||
- 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(
|
||||
jsoncons
|
||||
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"
|
||||
LIB "none"
|
||||
)
|
||||
|
|
|
@ -390,7 +390,8 @@ void SendJsonValue(ConnectionContext* cntx, const JsonType& j) {
|
|||
}
|
||||
|
||||
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);
|
||||
if (!result) {
|
||||
return result.status();
|
||||
|
@ -402,8 +403,39 @@ OpResult<string> OpGet(const OpArgs& op_args, string_view key,
|
|||
// means we just brings all values
|
||||
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) {
|
||||
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>();
|
||||
}
|
||||
|
||||
|
@ -413,6 +445,13 @@ OpResult<string> OpGet(const OpArgs& op_args, string_view key,
|
|||
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>();
|
||||
}
|
||||
|
||||
|
@ -1748,22 +1787,52 @@ void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) {
|
|||
DCHECK_GE(args.size(), 1U);
|
||||
string_view key = ArgS(args, 0);
|
||||
|
||||
OptString indent;
|
||||
OptString new_line;
|
||||
OptString space;
|
||||
vector<pair<string_view, JsonExpression>> expressions;
|
||||
|
||||
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;
|
||||
JsonExpression expr = ParseJsonPath(path, &ec);
|
||||
JsonExpression expr = ParseJsonPath(param, &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);
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
|
|
|
@ -120,6 +120,28 @@ TEST_F(JsonFamilyTest, SetGetFromPhonebook) {
|
|||
|
||||
resp = Run({"JSON.GET", "json", "$..phoneNumbers[1].*"});
|
||||
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) {
|
||||
|
|
Loading…
Reference in a new issue