Skip to content

Commit

Permalink
Implemented a json_replace overload that accepts a function object
Browse files Browse the repository at this point in the history
Now it is possible to use json_replace() to rewrite matched
values with a callback function.
  • Loading branch information
wbangna committed Oct 26, 2020
1 parent 3f791f7 commit 94f7144
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 3 deletions.
91 changes: 89 additions & 2 deletions doc/ref/jsonpath/json_replace.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ template<class Json, class T>
void json_replace(Json& root,
const typename Json::string_view_type& path,
T&& new_value)

template<class Json, class Op>
void json_replace(Json& root,
const typename Json::string_view_type& path,
T Op)
```
Searches for all values that match a JSONPath expression and replaces them with the specified value
(1) Searches for all values that match a JSONPath expression and replaces them with the specified value
(2) Searches for all values that match a JSONPath expression and replaces them with the result of the given function
#### Parameters
Expand All @@ -23,9 +29,13 @@ Searches for all values that match a JSONPath expression and replaces them with
<td>JSONPath expression string</td>
</tr>
<tr>
<td>new_value</td>
<td>(1) new_value</td>
<td>The value to use as replacement</td>
</tr>
<tr>
<td>(2) op</td>
<td>Callback function to rewrite values that matched the expression</td>
</tr>
</table>
#### Exceptions
Expand Down Expand Up @@ -109,3 +119,80 @@ Output:
}
```

#### Change the prices of all books

Input JSON file `booklist.json`:

```json
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
}
]
}
}
```
```c++
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpath/jsonpath.hpp>
#include <cmath>

using namespace jsoncons;
using namespace jsoncons::jsonpath;

int main()
{
std::ifstream is("input/booklist.json");
json booklist;
is >> booklist;

// make a discount on all books
jsonpath::json_replace(booklist, "$.store.book[*].price",
[](const json& price) { return std::round(price.as<double>() - 1.0); });
std::cout << pretty_print(booklist) << std::endl;

}
```
Output:
```json
{
"store": {
"book": [
{
"author": "Nigel Rees",
"category": "reference",
"price": 8.0,
"title": "Sayings of the Century"
},
{
"author": "Evelyn Waugh",
"category": "fiction",
"price": 12.0,
"title": "Sword of Honour"
},
{
"author": "Herman Melville",
"category": "fiction",
"isbn": "0-553-21311-3",
"price": 8.0,
"title": "Moby Dick"
}
]
}
}
```

14 changes: 14 additions & 0 deletions examples/src/jsonpath_examples.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <string>
#include <fstream>
#include <cmath>
#include <cassert>
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpath/jsonpath.hpp>
Expand Down Expand Up @@ -111,6 +112,18 @@ namespace {
std::cout << ("2\n") << pretty_print(j) << "\n";
}

void json_replace_example3()
{
std::ifstream is("./input/booklist.json");
json booklist = json::parse(is);

// make a discount on all books
jsonpath::json_replace(booklist, "$.store.book[*].price",
[](const json& price) { return std::round(price.as<double>() - 1.0); });
std::cout << pretty_print(booklist);
}


void jsonpath_complex_examples()
{
const json j = json::parse(R"(
Expand Down Expand Up @@ -276,6 +289,7 @@ void jsonpath_examples()
std::cout << "\nJsonPath examples\n\n";
json_replace_example1();
json_replace_example2();
json_replace_example3();
jsonpath_complex_examples();
jsonpath_union();
json_query_examples();
Expand Down
11 changes: 11 additions & 0 deletions include/jsoncons/detail/more_type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,17 @@ namespace impl {
template<class T>
using
is_constructible_from_string = is_detected<construct_from_string_t,T>;

// is_function_object

template<class FunctionObject, class Arg>
using
function_object_t = decltype(std::declval<FunctionObject>()(std::declval<Arg>()));

template<class FunctionObject, class Arg>
using
is_function_object = jsoncons::detail::is_detected<function_object_t, FunctionObject, Arg>;

} // detail
} // jsoncons

Expand Down
25 changes: 24 additions & 1 deletion include/jsoncons_ext/jsonpath/json_query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,25 @@ namespace jsoncons { namespace jsonpath {
}

template<class Json, class T>
void json_replace(Json& root, const typename Json::string_view_type& path, T&& new_value)
typename std::enable_if<!jsoncons::detail::is_function_object<T,Json>::value,void>::type
json_replace(Json& root, const typename Json::string_view_type& path, T&& new_value)
{
jsoncons::jsonpath::detail::jsonpath_evaluator<Json,Json&,detail::VoidPathConstructor<Json>> evaluator;
jsoncons::jsonpath::detail::jsonpath_resources<Json> resources;
evaluator.evaluate(resources, root, path);
evaluator.replace(std::forward<T>(new_value));
}

template<class Json, class Op>
typename std::enable_if<jsoncons::detail::is_function_object<Op,Json>::value,void>::type
json_replace(Json& root, const typename Json::string_view_type& path, Op op)
{
jsoncons::jsonpath::detail::jsonpath_evaluator<Json,Json&,detail::VoidPathConstructor<Json>> evaluator;
jsoncons::jsonpath::detail::jsonpath_resources<Json> resources;
evaluator.evaluate(resources, root, path);
evaluator.replace_fn(op);
}

namespace detail {

enum class path_state
Expand Down Expand Up @@ -601,6 +612,18 @@ namespace jsoncons { namespace jsonpath {
return result;
}

template<class Op>
void replace_fn(Op op)
{
if (!stack_.empty())
{
for (std::size_t i = 0; i < stack_.back().size(); ++i)
{
*(stack_.back()[i].val_ptr) = op(*(stack_.back()[i].val_ptr));
}
}
}

template <class T>
void replace(T&& new_value)
{
Expand Down

0 comments on commit 94f7144

Please sign in to comment.