TerraForge3D  2.3.1
3D Terrain And Landscape Generator

◆ patch()

template<template< typename U, typename V, typename... Args > class ObjectType = std::map, template< typename U, typename... Args > class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, class NumberIntegerType = std::int64_t, class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, template< typename U > class AllocatorType = std::allocator, template< typename T, typename SFINAE=void > class JSONSerializer = adl_serializer, class BinaryType = std::vector<std::uint8_t>>
basic_json nlohmann::basic_json< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType >::patch ( const basic_json< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType > &  json_patch) const
inline

applies a JSON patch

JSON Patch defines a JSON document structure for expressing a sequence of operations to apply to a JSON) document. With this function, a JSON Patch is applied to the current JSON value by executing all operations from the patch.

Parameters
[in]json_patchJSON patch document
Returns
patched document
Note
The application of a patch is atomic: Either all operations succeed and the patched document is returned or an exception is thrown. In any case, the original value is not changed: the patch is applied to a copy of the value.
Exceptions
parse_error.104if the JSON patch does not consist of an array of objects
parse_error.105if the JSON patch is malformed (e.g., mandatory attributes are missing); example: "operation add must have member path"
out_of_range.401if an array index is out of range.
out_of_range.403if a JSON pointer inside the patch could not be resolved successfully in the current JSON value; example: "key baz not found"
out_of_range.405if JSON pointer has no parent ("add", "remove", "move")
other_error.501if "test" operation was unsuccessful

@complexity Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is affected by the patch, the complexity can usually be neglected.

@liveexample{The following code shows how a JSON patch is applied to a value.,patch}

See also
see diffcreate a JSON patch by comparing two JSON values
RFC 6902 (JSON Patch)
RFC 6901 (JSON Pointer)
Since
version 2.0.0

Definition at line 26156 of file json.hpp.

26157 {
26158 // make a working copy to apply the patch to
26159 basic_json result = *this;
26160 // the valid JSON Patch operations
26161 enum class patch_operations {add, remove, replace, move, copy, test, invalid};
26162 const auto get_op = [](const std::string & op)
26163 {
26164 if (op == "add")
26165 {
26166 return patch_operations::add;
26167 }
26168
26169 if (op == "remove")
26170 {
26171 return patch_operations::remove;
26172 }
26173
26174 if (op == "replace")
26175 {
26176 return patch_operations::replace;
26177 }
26178
26179 if (op == "move")
26180 {
26181 return patch_operations::move;
26182 }
26183
26184 if (op == "copy")
26185 {
26186 return patch_operations::copy;
26187 }
26188
26189 if (op == "test")
26190 {
26191 return patch_operations::test;
26192 }
26193
26194 return patch_operations::invalid;
26195 };
26196 // wrapper for "add" operation; add value at ptr
26197 const auto operation_add = [&result](json_pointer & ptr, basic_json val)
26198 {
26199 // adding to the root of the target document means replacing it
26200 if (ptr.empty())
26201 {
26202 result = val;
26203 return;
26204 }
26205
26206 // make sure the top element of the pointer exists
26207 json_pointer top_pointer = ptr.top();
26208
26209 if (top_pointer != ptr)
26210 {
26211 result.at(top_pointer);
26212 }
26213
26214 // get reference to parent of JSON pointer ptr
26215 const auto last_path = ptr.back();
26216 ptr.pop_back();
26217 basic_json &parent = result[ptr];
26218
26219 switch (parent.m_type)
26220 {
26221 case value_t::null:
26222 case value_t::object:
26223 {
26224 // use operator[] to add value
26225 parent[last_path] = val;
26226 break;
26227 }
26228
26229 case value_t::array:
26230 {
26231 if (last_path == "-")
26232 {
26233 // special case: append to back
26234 parent.push_back(val);
26235 }
26236
26237 else
26238 {
26239 const auto idx = json_pointer::array_index(last_path);
26240
26241 if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
26242 {
26243 // avoid undefined behavior
26244 JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent));
26245 }
26246
26247 // default case: insert add offset
26248 parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
26249 }
26250
26251 break;
26252 }
26253
26254 // if there exists a parent it cannot be primitive
26255 case value_t::string: // LCOV_EXCL_LINE
26256 case value_t::boolean: // LCOV_EXCL_LINE
26257 case value_t::number_integer: // LCOV_EXCL_LINE
26258 case value_t::number_unsigned: // LCOV_EXCL_LINE
26259 case value_t::number_float: // LCOV_EXCL_LINE
26260 case value_t::binary: // LCOV_EXCL_LINE
26261 case value_t::discarded: // LCOV_EXCL_LINE
26262 default: // LCOV_EXCL_LINE
26263 JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
26264 }
26265 };
26266 // wrapper for "remove" operation; remove value at ptr
26267 const auto operation_remove = [this, &result](json_pointer & ptr)
26268 {
26269 // get reference to parent of JSON pointer ptr
26270 const auto last_path = ptr.back();
26271 ptr.pop_back();
26272 basic_json &parent = result.at(ptr);
26273
26274 // remove child
26275 if (parent.is_object())
26276 {
26277 // perform range check
26278 auto it = parent.find(last_path);
26279
26280 if (JSON_HEDLEY_LIKELY(it != parent.end()))
26281 {
26282 parent.erase(it);
26283 }
26284
26285 else
26286 {
26287 JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this));
26288 }
26289 }
26290
26291 else if (parent.is_array())
26292 {
26293 // note erase performs range check
26294 parent.erase(json_pointer::array_index(last_path));
26295 }
26296 };
26297
26298 // type check: top level value must be an array
26299 if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
26300 {
26301 JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch));
26302 }
26303
26304 // iterate and apply the operations
26305 for (const auto &val : json_patch)
26306 {
26307 // wrapper to get a value for an operation
26308 const auto get_value = [&val](const std::string & op,
26309 const std::string & member,
26310 bool string_type) -> basic_json &
26311 {
26312 // find value
26313 auto it = val.m_value.object->find(member);
26314
26315 // context-sensitive error message
26316 const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
26317
26318 // check if desired value is present
26319 if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))
26320 {
26321 // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
26322 JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val));
26323 }
26324
26325 // check if result is of type string
26326 if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
26327 {
26328 // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
26329 JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val));
26330 }
26331
26332 // no error: return value
26333 return it->second;
26334 };
26335
26336 // type check: every element of the array must be an object
26337 if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
26338 {
26339 JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val));
26340 }
26341
26342 // collect mandatory members
26343 const auto op = get_value("op", "op", true).template get<std::string>();
26344 const auto path = get_value(op, "path", true).template get<std::string>();
26345 json_pointer ptr(path);
26346
26347 switch (get_op(op))
26348 {
26349 case patch_operations::add:
26350 {
26351 operation_add(ptr, get_value("add", "value", false));
26352 break;
26353 }
26354
26355 case patch_operations::remove:
26356 {
26357 operation_remove(ptr);
26358 break;
26359 }
26360
26361 case patch_operations::replace:
26362 {
26363 // the "path" location must exist - use at()
26364 result.at(ptr) = get_value("replace", "value", false);
26365 break;
26366 }
26367
26368 case patch_operations::move:
26369 {
26370 const auto from_path = get_value("move", "from", true).template get<std::string>();
26371 json_pointer from_ptr(from_path);
26372 // the "from" location must exist - use at()
26373 basic_json v = result.at(from_ptr);
26374 // The move operation is functionally identical to a
26375 // "remove" operation on the "from" location, followed
26376 // immediately by an "add" operation at the target
26377 // location with the value that was just removed.
26378 operation_remove(from_ptr);
26379 operation_add(ptr, v);
26380 break;
26381 }
26382
26383 case patch_operations::copy:
26384 {
26385 const auto from_path = get_value("copy", "from", true).template get<std::string>();
26386 const json_pointer from_ptr(from_path);
26387 // the "from" location must exist - use at()
26388 basic_json v = result.at(from_ptr);
26389 // The copy is functionally identical to an "add"
26390 // operation at the target location using the value
26391 // specified in the "from" member.
26392 operation_add(ptr, v);
26393 break;
26394 }
26395
26396 case patch_operations::test:
26397 {
26398 bool success = false;
26399 JSON_TRY
26400 {
26401 // check if "value" matches the one at "path"
26402 // the "path" location must exist - use at()
26403 success = (result.at(ptr) == get_value("test", "value", false));
26404 }
26405 JSON_INTERNAL_CATCH (out_of_range &)
26406 {
26407 // ignore out of range errors: success remains false
26408 }
26409
26410 // throw an exception if test fails
26411 if (JSON_HEDLEY_UNLIKELY(!success))
26412 {
26413 JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val));
26414 }
26415
26416 break;
26417 }
26418
26419 case patch_operations::invalid:
26420 default:
26421 {
26422 // op must be "add", "remove", "replace", "move", "copy", or
26423 // "test"
26424 JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val));
26425 }
26426 }
26427 }
26428
26429 return result;
26430 }
basic_json(const value_t v)
create an empty value with a given type
Definition: json.hpp:19164
detail::out_of_range out_of_range
exception indicating access out of the defined range
Definition: json.hpp:17953
std::ptrdiff_t difference_type
a type to represent differences between iterators
Definition: json.hpp:17978
::nlohmann::json_pointer< basic_json > json_pointer
JSON Pointer, see nlohmann::json_pointer.
Definition: json.hpp:17922
static parse_error create(int id_, const position_t &pos, const std::string &what_arg, const BasicJsonType &context)
create a parse error exception
Definition: json.hpp:2800
static BasicJsonType::size_type array_index(const std::string &s)
Definition: json.hpp:12878
@ number_integer
number value (signed integer)
@ discarded
discarded by the parser callback function
@ binary
binary array (ordered collection of bytes)
@ object
object (unordered set of name/value pairs)
@ number_float
number value (floating-point)
@ number_unsigned
number value (unsigned integer)
@ array
array (ordered collection of values)