! @brief creates a diff as a JSON patch Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can be changed into the value @a target by calling @ref patch function. @invariant For two JSON values @a source and @a target, the following code yields always `true`: @code {.cpp} source.patch(diff(source, target)) == target; @endcode @note Currently
| 20514 | @since version 2.0.0 |
| 20515 | */ |
| 20516 | JSON_NODISCARD |
| 20517 | static basic_json diff(const basic_json& source, const basic_json& target, |
| 20518 | const std::string& path = "") |
| 20519 | { |
| 20520 | // the patch |
| 20521 | basic_json result(value_t::array); |
| 20522 | |
| 20523 | // if the values are the same, return empty patch |
| 20524 | if (source == target) |
| 20525 | { |
| 20526 | return result; |
| 20527 | } |
| 20528 | |
| 20529 | if (source.type() != target.type()) |
| 20530 | { |
| 20531 | // different types: replace value |
| 20532 | result.push_back( |
| 20533 | { |
| 20534 | {"op", "replace"}, {"path", path}, {"value", target} |
| 20535 | }); |
| 20536 | return result; |
| 20537 | } |
| 20538 | |
| 20539 | switch (source.type()) |
| 20540 | { |
| 20541 | case value_t::array: |
| 20542 | { |
| 20543 | // first pass: traverse common elements |
| 20544 | std::size_t i = 0; |
| 20545 | while (i < source.size() and i < target.size()) |
| 20546 | { |
| 20547 | // recursive call to compare array values at index i |
| 20548 | auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); |
| 20549 | result.insert(result.end(), temp_diff.begin(), temp_diff.end()); |
| 20550 | ++i; |
| 20551 | } |
| 20552 | |
| 20553 | // i now reached the end of at least one array |
| 20554 | // in a second pass, traverse the remaining elements |
| 20555 | |
| 20556 | // remove my remaining elements |
| 20557 | const auto end_index = static_cast<difference_type>(result.size()); |
| 20558 | while (i < source.size()) |
| 20559 | { |
| 20560 | // add operations in reverse order to avoid invalid |
| 20561 | // indices |
| 20562 | result.insert(result.begin() + end_index, object( |
| 20563 | { |
| 20564 | {"op", "remove"}, |
| 20565 | {"path", path + "/" + std::to_string(i)} |
| 20566 | })); |
| 20567 | ++i; |
| 20568 | } |
| 20569 | |
| 20570 | // add other remaining elements |
| 20571 | while (i < target.size()) |
| 20572 | { |
| 20573 | result.push_back( |