diff --git a/examples/src/jmespath_custom_function_examples.cpp b/examples/src/jmespath_custom_function_examples.cpp new file mode 100644 index 000000000..96aaa680b --- /dev/null +++ b/examples/src/jmespath_custom_function_examples.cpp @@ -0,0 +1,330 @@ +// Copyright 2013-2024 Daniel Parker +// Distributed under Boost license + +#include +#include + +#include +#include +#include +#include +#include + +namespace myspace { + using Json = jsoncons::json; + using JsonReference = const Json&; + using jmespath_errc = jsoncons::jmespath::jmespath_errc; +#define json_object_arg jsoncons::json_object_arg +#define json_array_arg jsoncons::json_array_arg +#define json_const_pointer_arg jsoncons::json_const_pointer_arg + + using function_base = jsoncons::jmespath::function_base; + using dynamic_resources = jsoncons::jmespath::dynamic_resources; + using static_resources = jsoncons::jmespath::detail::jmespath_evaluator::static_resources; + using parameter = jsoncons::jmespath::parameter; + using string_type = jsoncons::jmespath::detail::jmespath_evaluator::string_type; + using customer_get_function = jsoncons::jmespath::detail::jmespath_evaluator::static_resources::customer_get_function; + + bool is_integer(JsonReference value) + { + if (value.is() || value.is() || value.is() || value.is()) + { + return true; + } + else + { + return false; + } + } + + JsonReference get_value(JsonReference context, dynamic_resources& resources, const parameter& p) + { + if (p.is_expression()) + { + const auto& expr = p.expression(); + std::error_code ec2; + JsonReference value = expr.evaluate(context, resources, ec2); + // if (value.is_object() || value.is_array()) + // { + // return *resources.create_json(deep_copy(value)); + // } + // else + // { + // return value; + // } + return value; + } + else + { + JsonReference value = p.value(); + return value; + } + } + + template + class my_custom_functions : public jsoncons::jmespath::custom_functions + { + public: + my_custom_functions() + { + this->register_function("current_date_time", // function name + 0, // number of arguments + [](const jsoncons::span> params, + jsoncons::jmespath::dynamic_resources& resources, + std::error_code& ec) -> JsonReference + { + auto now = std::chrono::system_clock::now(); + auto milliseconds = std::chrono::duration_cast(now.time_since_epoch()); + return *resources.create_json(milliseconds.count()); + } + ); + this->register_function("add", // function name + 2, // number of arguments + [](jsoncons::span> params, + jsoncons::jmespath::dynamic_resources& resources, + std::error_code& ec) -> JsonReference + { + JSONCONS_ASSERT(2 == params.size()); + + if (!(params[0].is_value() && params[1].is_value())) + { + ec = jmespath_errc::invalid_argument; + return resources.null_value(); + } + + const auto arg0 = params[0].value(); + const auto arg1 = params[1].value(); + if (!(arg0.is_number() && arg1.is_number())) + { + ec = jmespath_errc::invalid_argument; + return resources.null_value(); + } + + if (is_integer(arg0) && is_integer(arg1)) + { + int64_t v = arg0.template as() + arg1.template as(); + return *resources.create_json(v); + } + else + { + double v = arg0.template as() + arg1.template as(); + return *resources.create_json(v); + } + } + ); + } + }; + + // When adding custom functions, they are generally placed in their own project's source code and namespace. + + class current_date_time_function : public function_base + { + public: + current_date_time_function() : function_base(0) {} + JsonReference evaluate(const std::vector& params, dynamic_resources& resources, std::error_code& ec) const override + { + auto now = std::chrono::system_clock::now(); + auto milliseconds = std::chrono::duration_cast(now.time_since_epoch()); + return *resources.create_json(milliseconds.count()); + } + std::string to_string(std::size_t = 0) const override + { + return std::string("current_date_time_function\n"); + } + }; + + class current_index_function : public function_base + { + public: + current_index_function() : function_base(0) {} + JsonReference evaluate(const std::vector& params, dynamic_resources& resources, std::error_code& ec) const override + { + size_t index = current_index_function::index; + JsonReference result = *resources.create_json(index); + return result; + } + std::string to_string(std::size_t = 0) const override + { + return std::string("current_index_function\n"); + } + + static thread_local size_t index; + }; + + thread_local size_t current_index_function::index = 0; + + /// @brief generate array,include 4 params:context value,array size (or &expression),&generate expression,default value (or &expression) + class generate_array_function : public function_base + { + public: + generate_array_function() : function_base(4) {} // context, size (or &expression), &expression, default (or &expression) + JsonReference evaluate(const std::vector& params, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(params.size() == *this->arity()); + + if (!(params[0].is_value() && params[2].is_expression())) + { + ec = jmespath_errc::invalid_argument; + return resources.null_value(); + } + + const auto context = params[0].value(); + const auto countValue = get_value(context, resources, params[1]); + const auto& expr = params[2].expression(); + const auto& argDefault = params[3]; + + if (!countValue.is_number()) + { + ec = jmespath_errc::invalid_argument; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + int count = countValue.template as(); + for (size_t i = 0; i < count; i++) + { + current_index_function::index = i; + std::error_code ec2; + + auto ele = expr.evaluate(context, resources, ec2); + + if (ele.is_null()) + { + auto defaultVal = get_value(context, resources, argDefault); + result->emplace_back(defaultVal); + } + else + { + // result->emplace_back(ele); // ?: It may lead to an abnormal exit. + result->emplace_back(*resources.create_json(deep_copy(ele))); + } + } + current_index_function::index = 0; + + return *result; + } + std::string to_string(std::size_t = 0) const override + { + return std::string("generate_array_function\n"); + } + }; + + class add_function : public function_base + { + public: + add_function() : function_base(2) {} + JsonReference evaluate(const std::vector& params, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(params.size() == *this->arity()); + + if (!(params[0].is_value() && params[1].is_value())) + { + ec = jmespath_errc::invalid_argument; + return resources.null_value(); + } + + const auto arg0 = params[0].value(); + const auto arg1 = params[1].value(); + if (!(arg0.is_number() && arg1.is_number())) + { + ec = jmespath_errc::invalid_argument; + return resources.null_value(); + } + + if (is_integer(arg0) && is_integer(arg1)) + { + int64_t v = arg0.template as() + arg1.template as(); + return *resources.create_json(v); + } + else + { + double v = arg0.template as() + arg1.template as(); + return *resources.create_json(v); + } + } + std::string to_string(std::size_t = 0) const override + { + return std::string("add_function\n"); + } + }; + + void init_customer_jmespath_functions() + { + customer_get_function cgf = [](const string_type& name) -> const function_base* + { + static current_date_time_function current_date_time_func; + static current_index_function current_index_func; + static generate_array_function generate_array_func; + static add_function add_func; + + static std::map functions = { + {"current_date_time", ¤t_date_time_func}, + {"current_index", ¤t_index_func}, + {"generate_array", &generate_array_func} /*, + {"add", &add_func}*/ }; + + auto it = functions.find(name); + + if (it == functions.end()) + { + return nullptr; + } + else + { + return it->second; + } + }; + + static_resources::get_or_set_customer_get_function(cgf, true); + } + + } + + +// for brevity +using jsoncons::json; +namespace jmespath = jsoncons::jmespath; + +void jmespath_customer_functions_example() +{ + std::string jtext = R"( + { + "devices": [ + { + "position": 1, + "id": "id-xxx", + "state": 1 + }, + { + "position": 5, + "id": "id-yyy", + "state": 1 + }, + { + "position": 9, + "id": "id-mmm", + "state": 2 + } + ] + } + )"; + + auto expr = jmespath::jmespath_expression::compile("generate_array(devices, `16`, &[?position==add(current_index(), `1`)] | [0], &{id: '', state: `0`, position: add(current_index(), `1`)})", + myspace::my_custom_functions{}); + + json doc = json::parse(jtext); + + json result = expr.evaluate(doc); + + std::cout << pretty_print(result) << "\n\n"; +} + +int main() +{ + std::cout << "\nJMESPath customer functions examples\n\n"; + myspace::init_customer_jmespath_functions(); + + jmespath_customer_functions_example(); + + std::cout << "\n"; +} diff --git a/examples/src/jmespath_customer_functions_examples.cpp b/examples/src/jmespath_customer_functions_examples.cpp index 505004b31..3e74527c9 100644 --- a/examples/src/jmespath_customer_functions_examples.cpp +++ b/examples/src/jmespath_customer_functions_examples.cpp @@ -20,12 +20,12 @@ namespace myspace #define json_array_arg jsoncons::json_array_arg #define json_const_pointer_arg jsoncons::json_const_pointer_arg - using function_base = jsoncons::jmespath::detail::jmespath_evaluator::function_base; + using function_base = jsoncons::jmespath::function_base; using dynamic_resources = jsoncons::jmespath::dynamic_resources; - using static_resources = jsoncons::jmespath::detail::jmespath_evaluator::static_resources; + using static_resources = jsoncons::jmespath::detail::jmespath_evaluator::static_resources; using parameter = jsoncons::jmespath::parameter; - using string_type = jsoncons::jmespath::detail::jmespath_evaluator::string_type; - using customer_get_function = jsoncons::jmespath::detail::jmespath_evaluator::static_resources::customer_get_function; + using string_type = jsoncons::jmespath::detail::jmespath_evaluator::string_type; + using customer_get_function = jsoncons::jmespath::detail::jmespath_evaluator::static_resources::customer_get_function; bool is_integer(JsonReference value) { @@ -67,7 +67,7 @@ namespace myspace { public: current_date_time_function() : function_base(0) {} - JsonReference evaluate(std::vector &args, dynamic_resources &resources, std::error_code &ec) const override + JsonReference evaluate(const std::vector &args, dynamic_resources &resources, std::error_code &ec) const override { auto now = std::chrono::system_clock::now(); auto milliseconds = std::chrono::duration_cast(now.time_since_epoch()); @@ -83,7 +83,7 @@ namespace myspace { public: current_index_function() : function_base(0) {} - JsonReference evaluate(std::vector &args, dynamic_resources &resources, std::error_code &ec) const override + JsonReference evaluate(const std::vector &args, dynamic_resources &resources, std::error_code &ec) const override { size_t index = current_index_function::index; JsonReference result = *resources.create_json(index); @@ -104,7 +104,7 @@ namespace myspace { public: generate_array_function() : function_base(4) {} // context, size (or &expression), &expression, default (or &expression) - JsonReference evaluate(std::vector &args, dynamic_resources &resources, std::error_code &ec) const override + JsonReference evaluate(const std::vector &args, dynamic_resources &resources, std::error_code &ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -159,7 +159,7 @@ namespace myspace { public: add_function() : function_base(2) {} - JsonReference evaluate(std::vector &args, dynamic_resources &resources, std::error_code &ec) const override + JsonReference evaluate(const std::vector &args, dynamic_resources &resources, std::error_code &ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); diff --git a/include/jsoncons_ext/jmespath/jmespath.hpp b/include/jsoncons_ext/jmespath/jmespath.hpp index 7ae31044f..72c8bf9da 100644 --- a/include/jsoncons_ext/jmespath/jmespath.hpp +++ b/include/jsoncons_ext/jmespath/jmespath.hpp @@ -184,11 +184,11 @@ namespace jmespath { }; // expression_base - template + template class expression_base { public: - using reference = JsonReference; + using reference = const Json&; private: std::size_t precedence_level_; bool is_right_associative_; @@ -237,7 +237,7 @@ namespace jmespath { { public: using reference = const Json&; - using expression_type = expression_base; + using expression_type = expression_base; private: parameter_kind type_; @@ -321,9 +321,11 @@ namespace jmespath { { public: using value_type = Json; + using reference = const Json&; using char_type = typename Json::char_type; using parameter_type = parameter; - using function_type = std::function, std::error_code& ec)>; + using function_type = std::function, + dynamic_resources&, std::error_code& ec)>; using string_type = typename Json::string_type; string_type function_name_; @@ -368,14 +370,72 @@ namespace jmespath { } }; + // function_base + + template + class function_base + { + public: + using reference = const Json&; + using parameter_type = parameter; + private: + jsoncons::optional arg_count_; + public: + function_base(jsoncons::optional arg_count) + : arg_count_(arg_count) + { + } + + jsoncons::optional arity() const + { + return arg_count_; + } + + virtual ~function_base() = default; + + virtual reference evaluate(const std::vector& params, dynamic_resources& resources, + std::error_code& ec) const = 0; + + virtual std::string to_string(std::size_t = 0) const + { + return std::string("to_string not implemented"); + } + }; + + template + class function_wrapper : public function_base + { + public: + using value_type = Json; + using reference = const Json&; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + using function_type = std::function, + dynamic_resources&, std::error_code& ec)>; + private: + function_type f_; + public: + function_wrapper(jsoncons::optional arity, const function_type& f) + : function_base(arity), f_(f) + { + } + + reference evaluate(const std::vector& params, dynamic_resources& resources, + std::error_code& ec) const override + { + return f_(params, resources, ec); + } + }; + template class custom_functions { using char_type = typename Json::char_type; using string_type = typename Json::string_type; using value_type = Json; + using reference = const Json&; using parameter_type = parameter; - using function_type = std::function, dynamic_resources& resources, + using function_type = std::function, dynamic_resources& resources, std::error_code& ec)>; using const_iterator = typename std::vector>::const_iterator; @@ -675,7 +735,7 @@ namespace jmespath { expect_and }; - template + template class jmespath_evaluator { public: @@ -683,11 +743,12 @@ namespace jmespath { typedef typename Json::char_traits_type char_traits_type; typedef std::basic_string string_type; typedef typename Json::string_view_type string_view_type; - typedef JsonReference reference; - using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; - typedef typename Json::const_pointer const_pointer; + using reference = const Json&; + using pointer = typename Json::const_pointer; + using const_pointer = typename Json::const_pointer; using parameter_type = parameter; - using expression_type = expression_base; + using expression_type = expression_base; + using function_type = function_base; static bool is_false(reference ref) { @@ -779,40 +840,15 @@ namespace jmespath { } }; - // function_base - class function_base - { - jsoncons::optional arg_count_; - public: - function_base(jsoncons::optional arg_count) - : arg_count_(arg_count) - { - } - - jsoncons::optional arity() const - { - return arg_count_; - } - - virtual ~function_base() = default; - - virtual reference evaluate(std::vector& args, dynamic_resources&, std::error_code& ec) const = 0; - - virtual std::string to_string(std::size_t = 0) const - { - return std::string("to_string not implemented"); - } - }; - - class abs_function : public function_base + class abs_function : public function_type { public: abs_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -844,15 +880,15 @@ namespace jmespath { } }; - class avg_function : public function_base + class avg_function : public function_type { public: avg_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -888,15 +924,15 @@ namespace jmespath { } }; - class ceil_function : public function_base + class ceil_function : public function_type { public: ceil_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -925,15 +961,15 @@ namespace jmespath { } }; - class contains_function : public function_base + class contains_function : public function_type { public: contains_function() - : function_base(2) + : function_type(2) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -978,15 +1014,15 @@ namespace jmespath { } }; - class ends_with_function : public function_base + class ends_with_function : public function_type { public: ends_with_function() - : function_base(2) + : function_type(2) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1024,15 +1060,15 @@ namespace jmespath { } }; - class floor_function : public function_base + class floor_function : public function_type { public: floor_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1061,15 +1097,15 @@ namespace jmespath { } }; - class join_function : public function_base + class join_function : public function_type { public: join_function() - : function_base(2) + : function_type(2) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1120,15 +1156,15 @@ namespace jmespath { } }; - class length_function : public function_base + class length_function : public function_type { public: length_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1160,15 +1196,15 @@ namespace jmespath { } }; - class max_function : public function_base + class max_function : public function_type { public: max_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1215,15 +1251,15 @@ namespace jmespath { } }; - class max_by_function : public function_base + class max_by_function : public function_type { public: max_by_function() - : function_base(2) + : function_type(2) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1277,15 +1313,15 @@ namespace jmespath { } }; - class map_function : public function_base + class map_function : public function_type { public: map_function() - : function_base(2) + : function_type(2) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1325,15 +1361,15 @@ namespace jmespath { } }; - class min_function : public function_base + class min_function : public function_type { public: min_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1380,15 +1416,15 @@ namespace jmespath { } }; - class min_by_function : public function_base + class min_by_function : public function_type { public: min_by_function() - : function_base(2) + : function_type(2) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1442,15 +1478,15 @@ namespace jmespath { } }; - class merge_function : public function_base + class merge_function : public function_type { public: merge_function() - : function_base(jsoncons::optional()) + : function_type(jsoncons::optional()) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { if (args.empty()) { @@ -1497,15 +1533,15 @@ namespace jmespath { } }; - class type_function : public function_base + class type_function : public function_type { public: type_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1539,15 +1575,15 @@ namespace jmespath { } }; - class sort_function : public function_base + class sort_function : public function_type { public: sort_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1591,15 +1627,15 @@ namespace jmespath { } }; - class sort_by_function : public function_base + class sort_by_function : public function_type { public: sort_by_function() - : function_base(2) + : function_type(2) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1652,15 +1688,15 @@ namespace jmespath { } }; - class keys_function final : public function_base + class keys_function final : public function_type { public: keys_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1688,15 +1724,15 @@ namespace jmespath { } }; - class values_function final : public function_base + class values_function final : public function_type { public: values_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1724,15 +1760,15 @@ namespace jmespath { } }; - class reverse_function final : public function_base + class reverse_function final : public function_type { public: reverse_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1768,15 +1804,15 @@ namespace jmespath { } }; - class starts_with_function : public function_base + class starts_with_function : public function_type { public: starts_with_function() - : function_base(2) + : function_type(2) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1814,15 +1850,15 @@ namespace jmespath { } }; - class sum_function : public function_base + class sum_function : public function_type { public: sum_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1853,15 +1889,15 @@ namespace jmespath { } }; - class to_array_function final : public function_base + class to_array_function final : public function_type { public: to_array_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1890,15 +1926,15 @@ namespace jmespath { } }; - class to_number_function final : public function_base + class to_number_function final : public function_type { public: to_number_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1953,15 +1989,15 @@ namespace jmespath { } }; - class to_string_function final : public function_base + class to_string_function final : public function_type { public: to_string_function() - : function_base(1) + : function_type(1) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code& ec) const override { JSONCONS_ASSERT(args.size() == *this->arity()); @@ -1981,15 +2017,15 @@ namespace jmespath { } }; - class not_null_function final : public function_base + class not_null_function final : public function_type { public: not_null_function() - : function_base(jsoncons::optional()) + : function_type(jsoncons::optional()) { } - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code&) const override + reference evaluate(const std::vector& args, dynamic_resources& resources, std::error_code&) const override { for (auto& param : args) { @@ -2019,7 +2055,7 @@ namespace jmespath { std::unique_ptr expression_; const unary_operator* unary_operator_; const binary_operator* binary_operator_; - const function_base* function_; + const function_type* function_; Json value_; string_type key_; }; @@ -2114,7 +2150,7 @@ namespace jmespath { { } - token(const function_base* function) noexcept + token(const function_type* function) noexcept : type_(token_kind::function), function_(function) { @@ -3296,8 +3332,25 @@ namespace jmespath { class static_resources { - std::vector> temp_storage_; + struct MyHash + { + std::uintmax_t operator()(string_type const& s) const noexcept + { + const int p = 31; + const int m = static_cast(1e9) + 9; + std::uintmax_t hash_value = 0; + std::uintmax_t p_pow = 1; + for (char_type c : s) { + hash_value = (hash_value + (c - 'a' + 1) * p_pow) % m; + p_pow = (p_pow * p) % m; + } + return hash_value; + } + }; + std::vector> temp_storage_; + std::unordered_map,MyHash> custom_functions_; + public: static_resources() = default; @@ -3306,7 +3359,17 @@ namespace jmespath { static_resources(static_resources&& expr) = default; static_resources& operator=(static_resources&& expr) = default; - typedef std::function customer_get_function; + static_resources(const custom_functions& functions) + : static_resources{} + { + for (const auto& item : functions) + { + custom_functions_.emplace(item.name(), + jsoncons::make_unique>(item.arity(),item.function())); + } + } + + typedef std::function customer_get_function; static const customer_get_function get_or_set_customer_get_function(customer_get_function cgf = nullptr, bool set = false) { static customer_get_function customer = nullptr; @@ -3317,7 +3380,7 @@ namespace jmespath { return customer; } - const function_base* get_function(const string_type& name, std::error_code& ec) const + const function_type* get_function(const string_type& name, std::error_code& ec) const { static abs_function abs_func; static avg_function avg_func; @@ -3346,7 +3409,7 @@ namespace jmespath { static to_string_function to_string_func; static not_null_function not_null_func; - using function_dictionary = std::unordered_map; + using function_dictionary = std::unordered_map; static const function_dictionary functions_ = { {string_type{'a','b','s'}, &abs_func}, @@ -3379,19 +3442,24 @@ namespace jmespath { const customer_get_function cgf = get_or_set_customer_get_function(); if(cgf){ - const function_base *func = cgf(name); + const function_type *func = cgf(name); if(func){ return func; } } auto it = functions_.find(name); - if (it == functions_.end()) + if (it != functions_.end()) + { + return it->second; + } + auto it2 = custom_functions_.find(name); + if (it2 == custom_functions_.end()) { ec = jmespath_errc::unknown_function; return nullptr; } - return it->second; + return it2->second.get(); } const unary_operator* get_not_operator() const @@ -3457,7 +3525,8 @@ namespace jmespath { static_resources resources_; std::vector output_stack_; public: - jmespath_expression() + jmespath_expression(const jsoncons::jmespath::custom_functions& funcs = jsoncons::jmespath::custom_functions()) + : resources_{funcs} { } @@ -3501,9 +3570,10 @@ namespace jmespath { return deep_copy(*evaluate_tokens(doc, output_stack_, dynamic_storage, ec)); } - static jmespath_expression compile(const string_view_type& expr) + static jmespath_expression compile(const string_view_type& expr, + const jsoncons::jmespath::custom_functions& funcs = jsoncons::jmespath::custom_functions()) { - jsoncons::jmespath::detail::jmespath_evaluator evaluator; + jsoncons::jmespath::detail::jmespath_evaluator evaluator{funcs}; std::error_code ec; jmespath_expression result = evaluator.compile(expr.data(), expr.size(), ec); if (ec) @@ -3514,9 +3584,17 @@ namespace jmespath { } static jmespath_expression compile(const string_view_type& expr, - std::error_code& ec) + std::error_code& ec) { - jsoncons::jmespath::detail::jmespath_evaluator evaluator; + jsoncons::jmespath::detail::jmespath_evaluator evaluator{}; + return evaluator.compile(expr.data(), expr.size(), ec); + } + + static jmespath_expression compile(const string_view_type& expr, + const jsoncons::jmespath::custom_functions& funcs, + std::error_code& ec) + { + jsoncons::jmespath::detail::jmespath_evaluator evaluator{funcs}; return evaluator.compile(expr.data(), expr.size(), ec); } }; @@ -3534,10 +3612,11 @@ namespace jmespath { std::vector operator_stack_; public: - jmespath_evaluator() + jmespath_evaluator(const jsoncons::jmespath::custom_functions& funcs = jsoncons::jmespath::custom_functions()) : line_(1), column_(1), begin_input_(nullptr), end_input_(nullptr), - p_(nullptr) + p_(nullptr), + resources_{funcs} { } @@ -3551,9 +3630,8 @@ namespace jmespath { return column_; } - jmespath_expression compile(const char_type* path, - std::size_t length, - std::error_code& ec) + jmespath_expression compile(const char_type* path, std::size_t length, + std::error_code& ec) { push_token(current_node_arg, ec); if (ec) {return jmespath_expression();} @@ -5307,12 +5385,12 @@ namespace jmespath { } // detail template - using jmespath_expression = typename jsoncons::jmespath::detail::jmespath_evaluator::jmespath_expression; + using jmespath_expression = typename jsoncons::jmespath::detail::jmespath_evaluator::jmespath_expression; template Json search(const Json& doc, const typename Json::string_view_type& path) { - jsoncons::jmespath::detail::jmespath_evaluator evaluator; + jsoncons::jmespath::detail::jmespath_evaluator evaluator; std::error_code ec; auto expr = evaluator.compile(path.data(), path.size(), ec); if (ec) @@ -5330,7 +5408,7 @@ namespace jmespath { template Json search(const Json& doc, const typename Json::string_view_type& path, std::error_code& ec) { - jsoncons::jmespath::detail::jmespath_evaluator evaluator; + jsoncons::jmespath::detail::jmespath_evaluator evaluator; auto expr = evaluator.compile(path.data(), path.size(), ec); if (ec) { @@ -5345,18 +5423,27 @@ namespace jmespath { } template - jmespath_expression make_expression(const typename json::string_view_type& expr) + jmespath_expression make_expression(const typename json::string_view_type& expr, + const jsoncons::jmespath::custom_functions& funcs = jsoncons::jmespath::custom_functions()) { - return jmespath_expression::compile(expr); + return jmespath_expression::compile(expr, funcs); } template jmespath_expression make_expression(const typename json::string_view_type& expr, - std::error_code& ec) + std::error_code& ec) { return jmespath_expression::compile(expr, ec); } + template + jmespath_expression make_expression(const typename json::string_view_type& expr, + const jsoncons::jmespath::custom_functions& funcs, + std::error_code& ec) + { + return jmespath_expression::compile(expr, funcs, ec); + } + } // namespace jmespath } // namespace jsoncons diff --git a/include/jsoncons_ext/jsonpath/expression.hpp b/include/jsoncons_ext/jsonpath/expression.hpp index 1b31ee26b..be3457de3 100644 --- a/include/jsoncons_ext/jsonpath/expression.hpp +++ b/include/jsoncons_ext/jsonpath/expression.hpp @@ -397,8 +397,7 @@ namespace detail { return is_right_associative_; } - virtual Json evaluate(JsonReference, - std::error_code&) const = 0; + virtual Json evaluate(JsonReference, std::error_code&) const = 0; }; template @@ -425,8 +424,7 @@ namespace detail { : unary_operator(1, true) {} - Json evaluate(JsonReference val, - std::error_code&) const override + Json evaluate(JsonReference val, std::error_code&) const override { return is_false(val) ? Json(true, semantic_tag::none) : Json(false, semantic_tag::none); } @@ -440,8 +438,7 @@ namespace detail { : unary_operator(1, true) {} - Json evaluate(JsonReference val, - std::error_code&) const override + Json evaluate(JsonReference val, std::error_code&) const override { if (val.is_int64()) { @@ -474,8 +471,7 @@ namespace detail { regex_operator(regex_operator&&) = default; regex_operator& operator=(regex_operator&&) = default; - Json evaluate(JsonReference val, - std::error_code&) const override + Json evaluate(JsonReference val, std::error_code&) const override { if (!val.is_string()) { @@ -507,10 +503,7 @@ namespace detail { return is_right_associative_; } - virtual Json evaluate(JsonReference, - JsonReference, - - std::error_code&) const = 0; + virtual Json evaluate(JsonReference, JsonReference, std::error_code&) const = 0; virtual std::string to_string(int = 0) const { @@ -1026,7 +1019,7 @@ namespace detail { } virtual value_type evaluate(const std::vector& args, - std::error_code& ec) const = 0; + std::error_code& ec) const = 0; virtual std::string to_string(int level = 0) const { @@ -1042,7 +1035,7 @@ namespace detail { }; template - class decorator_function : public function_base + class function_wrapper : public function_base { public: using value_type = Json; @@ -1052,7 +1045,7 @@ namespace detail { private: function_type f_; public: - decorator_function(jsoncons::optional arity, + function_wrapper(jsoncons::optional arity, const function_type& f) : function_base(arity), f_(f) { @@ -1079,7 +1072,7 @@ namespace detail { } value_type evaluate(const std::vector& args, - std::error_code& ec) const override + std::error_code& ec) const override { if (args.size() != *this->arity()) { @@ -1147,7 +1140,7 @@ namespace detail { } value_type evaluate(const std::vector& args, - std::error_code& ec) const override + std::error_code& ec) const override { if (args.size() != *this->arity()) { @@ -2364,7 +2357,7 @@ namespace detail { using string_type = typename Json::string_type; using value_type = Json; using reference = JsonReference; - using function_base_type = function_base; + using function_type = function_base; using selector_type = jsonpath_selector; struct MyHash @@ -2388,8 +2381,8 @@ namespace detail { std::vector> temp_json_values_; std::vector>> unary_operators_; - std::unordered_map,MyHash> functions_; - std::unordered_map,MyHash> custom_functions_; + std::unordered_map,MyHash> functions_; + std::unordered_map,MyHash> custom_functions_; static_resources(const allocator_type& alloc = allocator_type()) : alloc_(alloc) @@ -2437,7 +2430,7 @@ namespace detail { for (const auto& item : functions) { custom_functions_.emplace(item.name(), - jsoncons::make_unique>(item.arity(),item.function())); + jsoncons::make_unique>(item.arity(),item.function())); } } @@ -2459,7 +2452,7 @@ namespace detail { { } - const function_base_type* get_function(const string_type& name, std::error_code& ec) const + const function_type* get_function(const string_type& name, std::error_code& ec) const { auto it = functions_.find(name); if (it == functions_.end()) @@ -3246,10 +3239,10 @@ namespace detail { expression& operator=(expression&& expr) = default; value_type evaluate(dynamic_resources& resources, - reference root, - reference current, - result_options options, - std::error_code& ec) const override + reference root, + reference current, + result_options options, + std::error_code& ec) const override { std::vector stack; std::vector arg_stack; diff --git a/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp index 7b2910afc..97e974c12 100644 --- a/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp +++ b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp @@ -78,7 +78,7 @@ namespace jsonpath { jsonpath_expression& operator=(const jsonpath_expression&) = delete; jsonpath_expression& operator=(jsonpath_expression&&) = default; - template + template typename std::enable_if::value,void>::type evaluate(const_reference root, BinaryCallback callback, result_options options = result_options()) const {