@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}
26157 {
26158
26160
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
26198 {
26199
26200 if (ptr.empty())
26201 {
26202 result = val;
26203 return;
26204 }
26205
26206
26208
26209 if (top_pointer != ptr)
26210 {
26211 result.at(top_pointer);
26212 }
26213
26214
26215 const auto last_path = ptr.back();
26216 ptr.pop_back();
26218
26219 switch (parent.m_type)
26220 {
26223 {
26224
26225 parent[last_path] = val;
26226 break;
26227 }
26228
26230 {
26231 if (last_path == "-")
26232 {
26233
26234 parent.push_back(val);
26235 }
26236
26237 else
26238 {
26240
26241 if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
26242 {
26243
26244 JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent));
26245 }
26246
26247
26248 parent.insert(parent.begin() +
static_cast<difference_type>(idx), val);
26249 }
26250
26251 break;
26252 }
26253
26254
26262 default:
26263 JSON_ASSERT(false);
26264 }
26265 };
26266
26267 const auto operation_remove = [
this, &result](
json_pointer & ptr)
26268 {
26269
26270 const auto last_path = ptr.back();
26271 ptr.pop_back();
26273
26274
26275 if (parent.is_object())
26276 {
26277
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
26295 }
26296 };
26297
26298
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
26305 for (const auto &val : json_patch)
26306 {
26307
26308 const auto get_value = [&val](const std::string & op,
26309 const std::string & member,
26311 {
26312
26313 auto it = val.m_value.object->find(member);
26314
26315
26316 const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
26317
26318
26319 if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))
26320 {
26321
26322 JSON_THROW(
parse_error::create(105, 0, error_msg +
" must have member '" + member +
"'", val));
26323 }
26324
26325
26326 if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
26327 {
26328
26329 JSON_THROW(
parse_error::create(105, 0, error_msg +
" must have string member '" + member +
"'", val));
26330 }
26331
26332
26333 return it->second;
26334 };
26335
26336
26337 if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
26338 {
26340 }
26341
26342
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>();
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
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>();
26372
26374
26375
26376
26377
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>();
26387
26389
26390
26391
26392 operation_add(ptr, v);
26393 break;
26394 }
26395
26396 case patch_operations::test:
26397 {
26398 bool success = false;
26399 JSON_TRY
26400 {
26401
26402
26403 success = (result.at(ptr) == get_value("test", "value", false));
26404 }
26406 {
26407
26408 }
26409
26410
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
26423
26425 }
26426 }
26427 }
26428
26429 return result;
26430 }
basic_json(const value_t v)
create an empty value with a given type
detail::out_of_range out_of_range
exception indicating access out of the defined range
std::ptrdiff_t difference_type
a type to represent differences between iterators
::nlohmann::json_pointer< basic_json > json_pointer
JSON Pointer, see nlohmann::json_pointer.
static parse_error create(int id_, const position_t &pos, const std::string &what_arg, const BasicJsonType &context)
create a parse error exception
static BasicJsonType::size_type array_index(const std::string &s)
@ 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)