! @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, only `remove`, `add`, and
| 10912 | @since version 2.0.0 |
| 10913 | */ |
| 10914 | static basic_json diff(const basic_json& source, const basic_json& target, |
| 10915 | const std::string& path = "") { |
| 10916 | // the patch |
| 10917 | basic_json result(value_t::array); |
| 10918 | |
| 10919 | // if the values are the same, return empty patch |
| 10920 | if (source == target) { return result; } |
| 10921 | |
| 10922 | if (source.type() != target.type()) { |
| 10923 | // different types: replace value |
| 10924 | result.push_back({{"op", "replace"}, {"path", path}, {"value", target}}); |
| 10925 | } else { |
| 10926 | switch (source.type()) { |
| 10927 | case value_t::array: { |
| 10928 | // first pass: traverse common elements |
| 10929 | size_t i = 0; |
| 10930 | while (i < source.size() and i < target.size()) { |
| 10931 | // recursive call to compare array values at index i |
| 10932 | auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); |
| 10933 | result.insert(result.end(), temp_diff.begin(), temp_diff.end()); |
| 10934 | ++i; |
| 10935 | } |
| 10936 | |
| 10937 | // i now reached the end of at least one array |
| 10938 | // in a second pass, traverse the remaining elements |
| 10939 | |
| 10940 | // remove my remaining elements |
| 10941 | const auto end_index = static_cast<difference_type>(result.size()); |
| 10942 | while (i < source.size()) { |
| 10943 | // add operations in reverse order to avoid invalid |
| 10944 | // indices |
| 10945 | result.insert( |
| 10946 | result.begin() + end_index, |
| 10947 | object({{"op", "remove"}, {"path", path + "/" + std::to_string(i)}})); |
| 10948 | ++i; |
| 10949 | } |
| 10950 | |
| 10951 | // add other remaining elements |
| 10952 | while (i < target.size()) { |
| 10953 | result.push_back({{"op", "add"}, |
| 10954 | {"path", path + "/" + std::to_string(i)}, |
| 10955 | {"value", target[i]}}); |
| 10956 | ++i; |
| 10957 | } |
| 10958 | |
| 10959 | break; |
| 10960 | } |
| 10961 | |
| 10962 | case value_t::object: { |
| 10963 | // first pass: traverse this object's elements |
| 10964 | for (auto it = source.begin(); it != source.end(); ++it) { |
| 10965 | // escape the key name to be used in a JSON patch |
| 10966 | const auto key = json_pointer::escape(it.key()); |
| 10967 | |
| 10968 | if (target.find(it.key()) != target.end()) { |
| 10969 | // recursive call to compare object values at key it |
| 10970 | auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); |
| 10971 | result.insert(result.end(), temp_diff.begin(), temp_diff.end()); |