The jmespath extension implements JMESPath. JMESPath is a query language for transforming JSON documents into other JSON documents. It's supported in both the AWS and Azure CLI and has libraries available in a number of languages.
Fully compliant. The jsoncons implementation passes all compliance tests.
jmespath_expression | Represents the compiled form of a JMESPath string. |
search | Searches for all values that match a JMESPath expression |
make_expression | Returns a compiled JMESPath expression for later evaluation. (since 0.159.0) |
search function
jmespath_expression
custom_functions (since 1.0.0)
jsoncons::jmespath::search takes two arguments, a basic_json
and a JMESPath expression string, and returns a basic_json
result. This is the simplest way to
compile and evaluate a JMESPath expression.
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jmespath/jmespath.hpp>
// for brevity
using jsoncons::json;
namespace jmespath = jsoncons::jmespath;
int main()
{
// This examples is from the JMESPath front page
std::string jtext = R"(
{
"locations": [
{"name": "Seattle", "state": "WA"},
{"name": "New York", "state": "NY"},
{"name": "Bellevue", "state": "WA"},
{"name": "Olympia", "state": "WA"}
]
}
)";
std::string expr = "locations[?state == 'WA'].name | sort(@) | {WashingtonCities: join(', ', @)}";
json doc = json::parse(jtext);
json result = jmespath::search(doc, expr);
std::cout << pretty_print(result) << "\n\n";
}
Output:
{
"WashingtonCities": "Bellevue, Olympia, Seattle"
}
Credit to JMESPath for this example
A jsoncons::jmespath::jmespath_expression
represents the compiled form of a JMESPath string. It allows you to
evaluate a single compiled expression on multiple JSON documents.
A jmespath_expression
is immutable and thread-safe.
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jmespath/jmespath.hpp>
// for brevity
using jsoncons::json;
namespace jmespath = jsoncons::jmespath;
int main()
{
std::string jtext = R"(
{
"people": [
{
"age": 20,
"other": "foo",
"name": "Bob"
},
{
"age": 25,
"other": "bar",
"name": "Fred"
},
{
"age": 30,
"other": "baz",
"name": "George"
}
]
}
)";
// auto expr = jmespath::jmespath_expression<json>::compile("people[?age > `20`].[name, age]"); // until 0.159.0
auto expr = jmespath::make_expression<json>("people[?age > `20`].[name, age]"); // since 0.159.0
json doc = json::parse(jtext);
json result = expr.evaluate(doc);
std::cout << pretty_print(result) << "\n\n";
}
Output:
[
["Fred", 25],
["George", 30]
]
Credit to JMESPath Tutorial for this Example
#include <chrono>
#include <thread>
#include <string>
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jmespath/jmespath.hpp>
// for brevity
namespace jmespath = jsoncons::jmespath;
// When adding custom functions, they are generally placed in their own project's source code and namespace.
namespace myspace {
template <typename Json>
class my_custom_functions : public jmespath::custom_functions<Json>
{
using reference = const Json&;
using pointer = const Json*;
static thread_local size_t current_index;
public:
my_custom_functions()
{
this->register_function("current_index", // function name
0, // number of arguments
[](const jsoncons::span<const jmespath::parameter<Json>> params,
jmespath::dynamic_resources<Json>& resources,
std::error_code& ec) -> reference
{
auto result = resources.create_json(current_index);
return *result;
}
);
this->register_function("generate_array", // function name
4, // number of arguments
[](const jsoncons::span<const jmespath::parameter<Json>> params,
jmespath::dynamic_resources<Json>& resources,
std::error_code& ec) -> reference
{
JSONCONS_ASSERT(4 == params.size());
if (!(params[0].is_value() && params[2].is_expression()))
{
ec = jmespath::jmespath_errc::invalid_argument;
return resources.null_value();
}
reference context = params[0].value();
reference countValue = get_value(context, resources, params[1]);
const auto& expr = params[2].expression();
auto argDefault = params[3];
if (!countValue.is_number())
{
ec = jmespath::jmespath_errc::invalid_argument;
return resources.null_value();
}
auto result = resources.create_json(jsoncons::json_array_arg);
int count = countValue.template as<int>();
for (size_t i = 0; i < count; i++)
{
current_index = i;
std::error_code ec2;
reference 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);
}
}
current_index = 0;
return *result;
}
);
this->register_function("add", // function name
2, // number of arguments
[](jsoncons::span<const jmespath::parameter<Json>> params,
jmespath::dynamic_resources<Json>& resources,
std::error_code& ec) -> reference
{
JSONCONS_ASSERT(2 == params.size());
if (!(params[0].is_value() && params[1].is_value()))
{
ec = jmespath::jmespath_errc::invalid_argument;
return resources.null_value();
}
reference arg0 = params[0].value();
reference arg1 = params[1].value();
if (!(arg0.is_number() && arg1.is_number()))
{
ec = jmespath::jmespath_errc::invalid_argument;
return resources.null_value();
}
if (arg0.is<int64_t>() && arg1.is<int64_t>())
{
int64_t v = arg0.template as<int64_t>() + arg1.template as<int64_t>();
return *resources.create_json(v);
}
else
{
double v = arg0.template as<double>() + arg1.template as<double>();
return *resources.create_json(v);
}
}
);
}
static reference get_value(reference context, jmespath::dynamic_resources<Json>& resources,
const jmespath::parameter<Json>& param)
{
if (param.is_expression())
{
const auto& expr = param.expression();
std::error_code ec;
reference value = expr.evaluate(context, resources, ec);
return value;
}
else
{
reference value = param.value();
return value;
}
}
};
template <typename Json>
thread_local size_t my_custom_functions<Json>::current_index = 0;
} // namespace myspace
// for brevity
using json = jsoncons::json;
int main()
{
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::make_expression<json>("generate_array(devices, `16`, &[?position==add(current_index(), `1`)] | [0], &{id: '', state: `0`, position: add(current_index(), `1`)})",
myspace::my_custom_functions<json>{});
auto doc = json::parse(jtext);
auto result = expr.evaluate(doc);
std::cout << pretty_print(result) << "\n\n";
}
Output:
[
{
"id": "id-xxx",
"position": 1,
"state": 1
},
{
"id": "",
"position": 2,
"state": 0
},
{
"id": "",
"position": 3,
"state": 0
},
{
"id": "",
"position": 4,
"state": 0
},
{
"id": "id-yyy",
"position": 5,
"state": 1
},
{
"id": "",
"position": 6,
"state": 0
},
{
"id": "",
"position": 7,
"state": 0
},
{
"id": "",
"position": 8,
"state": 0
},
{
"id": "id-mmm",
"position": 9,
"state": 2
},
{
"id": "",
"position": 10,
"state": 0
},
{
"id": "",
"position": 11,
"state": 0
},
{
"id": "",
"position": 12,
"state": 0
},
{
"id": "",
"position": 13,
"state": 0
},
{
"id": "",
"position": 14,
"state": 0
},
{
"id": "",
"position": 15,
"state": 0
},
{
"id": "",
"position": 16,
"state": 0
}
]
Credit to PR #560 for this example