blob: f1cfb14ca23d5d5f5c673011ec97f6572bb02edf [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
2/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7/*
8 __ _____ _____ _____
9 __| | __| | | | JSON for Modern C++
10| | |__ | | | | | | version 3.1.2
11|_____|_____|_____|_|___| https://github.com/nlohmann/json
12
13Licensed under the MIT License <http://opensource.org/licenses/MIT>.
14Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
15
16Permission is hereby granted, free of charge, to any person obtaining a copy
17of this software and associated documentation files (the "Software"), to deal
18in the Software without restriction, including without limitation the rights
19to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20copies of the Software, and to permit persons to whom the Software is
21furnished to do so, subject to the following conditions:
22
23The above copyright notice and this permission notice shall be included in all
24copies or substantial portions of the Software.
25
26THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32SOFTWARE.
33*/
34#define WPI_JSON_IMPLEMENTATION
35#include "wpi/json.h"
36
Austin Schuh812d0d12021-11-04 20:16:48 -070037#include "fmt/format.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080038#include "wpi/raw_ostream.h"
39
40namespace wpi {
41namespace detail {
42
Austin Schuh812d0d12021-11-04 20:16:48 -070043exception::exception(int id_, std::string_view what_arg)
44 : id(id_), m(std::string{what_arg}) {}
Brian Silverman8fce7482020-01-05 13:18:21 -080045
Austin Schuh812d0d12021-11-04 20:16:48 -070046parse_error parse_error::create(int id_, std::size_t byte_, std::string_view what_arg)
Brian Silverman8fce7482020-01-05 13:18:21 -080047{
Austin Schuh812d0d12021-11-04 20:16:48 -070048 if (byte_ != 0)
49 return parse_error(id_, byte_, fmt::format("[json.exception.parse_error.{}] parse error at {}: {}", id_, byte_, what_arg));
50 else
51 return parse_error(id_, byte_, fmt::format("[json.exception.parse_error.{}] parse error: {}", id_, what_arg));
Brian Silverman8fce7482020-01-05 13:18:21 -080052}
53
Austin Schuh812d0d12021-11-04 20:16:48 -070054invalid_iterator invalid_iterator::create(int id_, std::string_view what_arg)
Brian Silverman8fce7482020-01-05 13:18:21 -080055{
Austin Schuh812d0d12021-11-04 20:16:48 -070056 return invalid_iterator(id_, fmt::format("[json.exception.invalid_iterator.{}] {}", id_, what_arg));
Brian Silverman8fce7482020-01-05 13:18:21 -080057}
58
Austin Schuh812d0d12021-11-04 20:16:48 -070059invalid_iterator invalid_iterator::create(int id_, std::string_view what_arg, std::string_view type_info)
Brian Silverman8fce7482020-01-05 13:18:21 -080060{
Austin Schuh812d0d12021-11-04 20:16:48 -070061 return invalid_iterator(id_, fmt::format("[json.exception.invalid_iterator.{}] {} {}", id_, what_arg, type_info));
Brian Silverman8fce7482020-01-05 13:18:21 -080062}
63
Austin Schuh812d0d12021-11-04 20:16:48 -070064type_error type_error::create(int id_, std::string_view what_arg)
Brian Silverman8fce7482020-01-05 13:18:21 -080065{
Austin Schuh812d0d12021-11-04 20:16:48 -070066 return type_error(id_, fmt::format("[json.exception.type_error.{}] {}", id_, what_arg));
Brian Silverman8fce7482020-01-05 13:18:21 -080067}
68
Austin Schuh812d0d12021-11-04 20:16:48 -070069type_error type_error::create(int id_, std::string_view what_arg, std::string_view type_info)
Brian Silverman8fce7482020-01-05 13:18:21 -080070{
Austin Schuh812d0d12021-11-04 20:16:48 -070071 return type_error(id_, fmt::format("[json.exception.type_error.{}] {} {}", id_, what_arg, type_info));
72}
73
74out_of_range out_of_range::create(int id_, std::string_view what_arg)
75{
76 return out_of_range(id_, fmt::format("[json.exception.out_of_range.{}] {}", id_, what_arg));
77}
78
79other_error other_error::create(int id_, std::string_view what_arg)
80{
81 return other_error(id_, fmt::format("[json.exception.other_error.{}] {}", id_, what_arg));
Brian Silverman8fce7482020-01-05 13:18:21 -080082}
83
84} // namespace detail
85
86json::json_value::json_value(value_t t)
87{
88 switch (t)
89 {
90 case value_t::object:
91 {
92 object = create<object_t>();
93 break;
94 }
95
96 case value_t::array:
97 {
98 array = create<array_t>();
99 break;
100 }
101
102 case value_t::string:
103 {
104 string = create<std::string>("");
105 break;
106 }
107
108 case value_t::boolean:
109 {
110 boolean = false;
111 break;
112 }
113
114 case value_t::number_integer:
115 {
116 number_integer = 0;
117 break;
118 }
119
120 case value_t::number_unsigned:
121 {
122 number_unsigned = 0u;
123 break;
124 }
125
126 case value_t::number_float:
127 {
128 number_float = 0.0;
129 break;
130 }
131
132 case value_t::null:
133 {
134 object = nullptr; // silence warning, see #821
135 break;
136 }
137
138 default:
139 {
140 object = nullptr; // silence warning, see #821
141 if (JSON_UNLIKELY(t == value_t::null))
142 {
143 JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.1.2")); // LCOV_EXCL_LINE
144 }
145 break;
146 }
147 }
148}
149
150void json::json_value::destroy(value_t t) noexcept
151{
152 switch (t)
153 {
154 case value_t::object:
155 {
156 std::allocator<object_t> alloc;
157 std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
158 std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
159 break;
160 }
161
162 case value_t::array:
163 {
164 std::allocator<array_t> alloc;
165 std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
166 std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
167 break;
168 }
169
170 case value_t::string:
171 {
172 std::allocator<std::string> alloc;
173 std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
174 std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
175 break;
176 }
177
178 default:
179 {
180 break;
181 }
182 }
183}
184
185json::json(initializer_list_t init,
186 bool type_deduction,
187 value_t manual_type)
188{
189 // check if each element is an array with two elements whose first
190 // element is a string
191 bool is_an_object = std::all_of(init.begin(), init.end(),
192 [](const detail::json_ref<json>& element_ref)
193 {
194 return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string());
195 });
196
197 // adjust type if type deduction is not wanted
198 if (not type_deduction)
199 {
200 // if array is wanted, do not create an object though possible
201 if (manual_type == value_t::array)
202 {
203 is_an_object = false;
204 }
205
206 // if object is wanted but impossible, throw an exception
207 if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object))
208 {
209 JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
210 }
211 }
212
213 if (is_an_object)
214 {
215 // the initializer list is a list of pairs -> create object
216 m_type = value_t::object;
217 m_value = value_t::object;
218
219 std::for_each(init.begin(), init.end(), [this](const detail::json_ref<json>& element_ref)
220 {
221 auto element = element_ref.moved_or_copied();
222 m_value.object->try_emplace(
223 *((*element.m_value.array)[0].m_value.string),
224 std::move((*element.m_value.array)[1]));
225 });
226 }
227 else
228 {
229 // the initializer list describes an array -> create array
230 m_type = value_t::array;
231 m_value.array = create<array_t>(init.begin(), init.end());
232 }
233
234 assert_invariant();
235}
236
237json::json(size_type cnt, const json& val)
238 : m_type(value_t::array)
239{
240 m_value.array = create<array_t>(cnt, val);
241 assert_invariant();
242}
243
244json::json(const json& other)
245 : m_type(other.m_type)
246{
247 // check of passed value is valid
248 other.assert_invariant();
249
250 switch (m_type)
251 {
252 case value_t::object:
253 {
254 m_value = *other.m_value.object;
255 break;
256 }
257
258 case value_t::array:
259 {
260 m_value = *other.m_value.array;
261 break;
262 }
263
264 case value_t::string:
265 {
266 m_value = *other.m_value.string;
267 break;
268 }
269
270 case value_t::boolean:
271 {
272 m_value = other.m_value.boolean;
273 break;
274 }
275
276 case value_t::number_integer:
277 {
278 m_value = other.m_value.number_integer;
279 break;
280 }
281
282 case value_t::number_unsigned:
283 {
284 m_value = other.m_value.number_unsigned;
285 break;
286 }
287
288 case value_t::number_float:
289 {
290 m_value = other.m_value.number_float;
291 break;
292 }
293
294 default:
295 break;
296 }
297
298 assert_invariant();
299}
300
301json json::meta()
302{
303 json result;
304
305 result["copyright"] = "(C) 2013-2017 Niels Lohmann, (C) 2017-2018 FIRST";
306 result["name"] = "WPI version of JSON for Modern C++";
307 result["url"] = "https://github.com/wpilibsuite/allwpilib";
308 result["version"]["string"] =
309 std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." +
310 std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." +
311 std::to_string(NLOHMANN_JSON_VERSION_PATCH);
312 result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
313 result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
314 result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
315
316#ifdef _WIN32
317 result["platform"] = "win32";
318#elif defined __linux__
319 result["platform"] = "linux";
320#elif defined __APPLE__
321 result["platform"] = "apple";
322#elif defined __unix__
323 result["platform"] = "unix";
324#else
325 result["platform"] = "unknown";
326#endif
327
328#if defined(__ICC) || defined(__INTEL_COMPILER)
329 result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
330#elif defined(__clang__)
331 result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
332#elif defined(__GNUC__) || defined(__GNUG__)
333 result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
334#elif defined(__HP_cc) || defined(__HP_aCC)
335 result["compiler"] = "hp"
336#elif defined(__IBMCPP__)
337 result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
338#elif defined(_MSC_VER)
339 result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
340#elif defined(__PGI)
341 result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
342#elif defined(__SUNPRO_CC)
343 result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
344#else
345 result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
346#endif
347
348#ifdef __cplusplus
349 result["compiler"]["c++"] = std::to_string(__cplusplus);
350#else
351 result["compiler"]["c++"] = "unknown";
352#endif
353 return result;
354}
355
356json::reference json::at(size_type idx)
357{
358 // at only works for arrays
359 if (JSON_LIKELY(is_array()))
360 {
361 JSON_TRY
362 {
363 return m_value.array->at(idx);
364 }
365 JSON_CATCH (std::out_of_range&)
366 {
367 // create better exception explanation
Austin Schuh812d0d12021-11-04 20:16:48 -0700368 JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800369 }
370 }
371 else
372 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700373 JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800374 }
375}
376
377json::const_reference json::at(size_type idx) const
378{
379 // at only works for arrays
380 if (JSON_LIKELY(is_array()))
381 {
382 JSON_TRY
383 {
384 return m_value.array->at(idx);
385 }
386 JSON_CATCH (std::out_of_range&)
387 {
388 // create better exception explanation
Austin Schuh812d0d12021-11-04 20:16:48 -0700389 JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800390 }
391 }
392 else
393 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700394 JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800395 }
396}
397
Austin Schuh812d0d12021-11-04 20:16:48 -0700398json::reference json::at(std::string_view key)
Brian Silverman8fce7482020-01-05 13:18:21 -0800399{
400 // at only works for objects
401 if (JSON_LIKELY(is_object()))
402 {
403 auto it = m_value.object->find(key);
404 if (it == m_value.object->end())
405 {
406 // create better exception explanation
Austin Schuh812d0d12021-11-04 20:16:48 -0700407 JSON_THROW(out_of_range::create(403, fmt::format("key '{}' not found", key)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800408 }
409 return it->second;
410 }
411 else
412 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700413 JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800414 }
415}
416
Austin Schuh812d0d12021-11-04 20:16:48 -0700417json::const_reference json::at(std::string_view key) const
Brian Silverman8fce7482020-01-05 13:18:21 -0800418{
419 // at only works for objects
420 if (JSON_LIKELY(is_object()))
421 {
422 auto it = m_value.object->find(key);
423 if (it == m_value.object->end())
424 {
425 // create better exception explanation
Austin Schuh812d0d12021-11-04 20:16:48 -0700426 JSON_THROW(out_of_range::create(403, fmt::format("key '{}' not found", key)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800427 }
428 return it->second;
429 }
430 else
431 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700432 JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800433 }
434}
435
436json::reference json::operator[](size_type idx)
437{
438 // implicitly convert null value to an empty array
439 if (is_null())
440 {
441 m_type = value_t::array;
442 m_value.array = create<array_t>();
443 assert_invariant();
444 }
445
446 // operator[] only works for arrays
447 if (JSON_LIKELY(is_array()))
448 {
449 // fill up array with null values if given idx is outside range
450 if (idx >= m_value.array->size())
451 {
452 m_value.array->insert(m_value.array->end(),
453 idx - m_value.array->size() + 1,
454 json());
455 }
456
457 return m_value.array->operator[](idx);
458 }
459
Austin Schuh812d0d12021-11-04 20:16:48 -0700460 JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800461}
462
463json::const_reference json::operator[](size_type idx) const
464{
465 // const operator[] only works for arrays
466 if (JSON_LIKELY(is_array()))
467 {
468 return m_value.array->operator[](idx);
469 }
470
Austin Schuh812d0d12021-11-04 20:16:48 -0700471 JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800472}
473
Austin Schuh812d0d12021-11-04 20:16:48 -0700474json::reference json::operator[](std::string_view key)
Brian Silverman8fce7482020-01-05 13:18:21 -0800475{
476 // implicitly convert null value to an empty object
477 if (is_null())
478 {
479 m_type = value_t::object;
480 m_value.object = create<object_t>();
481 assert_invariant();
482 }
483
484 // operator[] only works for objects
485 if (JSON_LIKELY(is_object()))
486 {
487 return m_value.object->operator[](key);
488 }
489
Austin Schuh812d0d12021-11-04 20:16:48 -0700490 JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800491}
492
Austin Schuh812d0d12021-11-04 20:16:48 -0700493json::const_reference json::operator[](std::string_view key) const
Brian Silverman8fce7482020-01-05 13:18:21 -0800494{
495 // const operator[] only works for objects
496 if (JSON_LIKELY(is_object()))
497 {
498 assert(m_value.object->find(key) != m_value.object->end());
499 return m_value.object->find(key)->second;
500 }
501
Austin Schuh812d0d12021-11-04 20:16:48 -0700502 JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800503}
504
Austin Schuh812d0d12021-11-04 20:16:48 -0700505json::size_type json::erase(std::string_view key)
Brian Silverman8fce7482020-01-05 13:18:21 -0800506{
507 // this erase only works for objects
508 if (JSON_LIKELY(is_object()))
509 {
510 return m_value.object->erase(key);
511 }
512
Austin Schuh812d0d12021-11-04 20:16:48 -0700513 JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800514}
515
516void json::erase(const size_type idx)
517{
518 // this erase only works for arrays
519 if (JSON_LIKELY(is_array()))
520 {
521 if (JSON_UNLIKELY(idx >= size()))
522 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700523 JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800524 }
525
526 m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
527 }
528 else
529 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700530 JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800531 }
532}
533
Austin Schuh812d0d12021-11-04 20:16:48 -0700534json::iterator json::find(std::string_view key)
Brian Silverman8fce7482020-01-05 13:18:21 -0800535{
536 auto result = end();
537
538 if (is_object())
539 {
540 result.m_it.object_iterator = m_value.object->find(key);
541 }
542
543 return result;
544}
545
Austin Schuh812d0d12021-11-04 20:16:48 -0700546json::const_iterator json::find(std::string_view key) const
Brian Silverman8fce7482020-01-05 13:18:21 -0800547{
548 auto result = cend();
549
550 if (is_object())
551 {
552 result.m_it.object_iterator = m_value.object->find(key);
553 }
554
555 return result;
556}
557
Austin Schuh812d0d12021-11-04 20:16:48 -0700558json::size_type json::count(std::string_view key) const
Brian Silverman8fce7482020-01-05 13:18:21 -0800559{
560 // return 0 for all nonobject types
561 return is_object() ? m_value.object->count(key) : 0;
562}
563
564bool json::empty() const noexcept
565{
566 switch (m_type)
567 {
568 case value_t::null:
569 {
570 // null values are empty
571 return true;
572 }
573
574 case value_t::array:
575 {
576 // delegate call to array_t::empty()
577 return m_value.array->empty();
578 }
579
580 case value_t::object:
581 {
582 // delegate call to object_t::empty()
583 return m_value.object->empty();
584 }
585
586 default:
587 {
588 // all other types are nonempty
589 return false;
590 }
591 }
592}
593
594json::size_type json::size() const noexcept
595{
596 switch (m_type)
597 {
598 case value_t::null:
599 {
600 // null values are empty
601 return 0;
602 }
603
604 case value_t::array:
605 {
606 // delegate call to array_t::size()
607 return m_value.array->size();
608 }
609
610 case value_t::object:
611 {
612 // delegate call to object_t::size()
613 return m_value.object->size();
614 }
615
616 default:
617 {
618 // all other types have size 1
619 return 1;
620 }
621 }
622}
623
624json::size_type json::max_size() const noexcept
625{
626 switch (m_type)
627 {
628 case value_t::array:
629 {
630 // delegate call to array_t::max_size()
631 return m_value.array->max_size();
632 }
633
634 case value_t::object:
635 {
636 // delegate call to std::allocator<object_t>::max_size()
637 return std::allocator_traits<object_t>::max_size(*m_value.object);
638 }
639
640 default:
641 {
642 // all other types have max_size() == size()
643 return size();
644 }
645 }
646}
647
648void json::clear() noexcept
649{
650 switch (m_type)
651 {
652 case value_t::number_integer:
653 {
654 m_value.number_integer = 0;
655 break;
656 }
657
658 case value_t::number_unsigned:
659 {
660 m_value.number_unsigned = 0;
661 break;
662 }
663
664 case value_t::number_float:
665 {
666 m_value.number_float = 0.0;
667 break;
668 }
669
670 case value_t::boolean:
671 {
672 m_value.boolean = false;
673 break;
674 }
675
676 case value_t::string:
677 {
678 m_value.string->clear();
679 break;
680 }
681
682 case value_t::array:
683 {
684 m_value.array->clear();
685 break;
686 }
687
688 case value_t::object:
689 {
690 m_value.object->clear();
691 break;
692 }
693
694 default:
695 break;
696 }
697}
698
699void json::push_back(json&& val)
700{
701 // push_back only works for null objects or arrays
702 if (JSON_UNLIKELY(not(is_null() or is_array())))
703 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700704 JSON_THROW(type_error::create(308, "cannot use push_back() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800705 }
706
707 // transform null object into an array
708 if (is_null())
709 {
710 m_type = value_t::array;
711 m_value = value_t::array;
712 assert_invariant();
713 }
714
715 // add element to array (move semantics)
716 m_value.array->push_back(std::move(val));
717 // invalidate object
718 val.m_type = value_t::null;
719}
720
721void json::push_back(const json& val)
722{
723 // push_back only works for null objects or arrays
724 if (JSON_UNLIKELY(not(is_null() or is_array())))
725 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700726 JSON_THROW(type_error::create(308, "cannot use push_back() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800727 }
728
729 // transform null object into an array
730 if (is_null())
731 {
732 m_type = value_t::array;
733 m_value = value_t::array;
734 assert_invariant();
735 }
736
737 // add element to array
738 m_value.array->push_back(val);
739}
740
741void json::push_back(initializer_list_t init)
742{
743 if (is_object() and init.size() == 2 and (*init.begin())->is_string())
744 {
745 std::string key = init.begin()->moved_or_copied();
Austin Schuh812d0d12021-11-04 20:16:48 -0700746 push_back(std::pair<std::string_view, json>(key, (init.begin() + 1)->moved_or_copied()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800747 }
748 else
749 {
750 push_back(json(init));
751 }
752}
753
754json::iterator json::insert(const_iterator pos, const json& val)
755{
756 // insert only works for arrays
757 if (JSON_LIKELY(is_array()))
758 {
759 // check if iterator pos fits to this JSON value
760 if (JSON_UNLIKELY(pos.m_object != this))
761 {
762 JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
763 }
764
765 // insert to array and return iterator
766 iterator result(this);
767 result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
768 return result;
769 }
770
Austin Schuh812d0d12021-11-04 20:16:48 -0700771 JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800772}
773
774json::iterator json::insert(const_iterator pos, size_type cnt, const json& val)
775{
776 // insert only works for arrays
777 if (JSON_LIKELY(is_array()))
778 {
779 // check if iterator pos fits to this JSON value
780 if (JSON_UNLIKELY(pos.m_object != this))
781 {
782 JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
783 }
784
785 // insert to array and return iterator
786 iterator result(this);
787 result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
788 return result;
789 }
790
Austin Schuh812d0d12021-11-04 20:16:48 -0700791 JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800792}
793
794json::iterator json::insert(const_iterator pos, const_iterator first, const_iterator last)
795{
796 // insert only works for arrays
797 if (JSON_UNLIKELY(not is_array()))
798 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700799 JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800800 }
801
802 // check if iterator pos fits to this JSON value
803 if (JSON_UNLIKELY(pos.m_object != this))
804 {
805 JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
806 }
807
808 // check if range iterators belong to the same JSON object
809 if (JSON_UNLIKELY(first.m_object != last.m_object))
810 {
811 JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
812 }
813
814 if (JSON_UNLIKELY(first.m_object == this))
815 {
816 JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
817 }
818
819 // insert to array and return iterator
820 iterator result(this);
821 result.m_it.array_iterator = m_value.array->insert(
822 pos.m_it.array_iterator,
823 first.m_it.array_iterator,
824 last.m_it.array_iterator);
825 return result;
826}
827
828json::iterator json::insert(const_iterator pos, initializer_list_t ilist)
829{
830 // insert only works for arrays
831 if (JSON_UNLIKELY(not is_array()))
832 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700833 JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800834 }
835
836 // check if iterator pos fits to this JSON value
837 if (JSON_UNLIKELY(pos.m_object != this))
838 {
839 JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
840 }
841
842 // insert to array and return iterator
843 iterator result(this);
844 result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end());
845 return result;
846}
847
848void json::insert(const_iterator first, const_iterator last)
849{
850 // insert only works for objects
851 if (JSON_UNLIKELY(not is_object()))
852 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700853 JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800854 }
855
856 // check if range iterators belong to the same JSON object
857 if (JSON_UNLIKELY(first.m_object != last.m_object))
858 {
859 JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
860 }
861
862 // passed iterators must belong to objects
863 if (JSON_UNLIKELY(not first.m_object->is_object()))
864 {
865 JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
866 }
867
868 for (auto it = first.m_it.object_iterator, end = last.m_it.object_iterator; it != end; ++it)
869 {
870 m_value.object->insert(std::make_pair(it->first(), it->second));
871 }
872}
873
874void json::update(const_reference j)
875{
876 // implicitly convert null value to an empty object
877 if (is_null())
878 {
879 m_type = value_t::object;
880 m_value.object = create<object_t>();
881 assert_invariant();
882 }
883
884 if (JSON_UNLIKELY(not is_object()))
885 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700886 JSON_THROW(type_error::create(312, "cannot use update() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800887 }
888 if (JSON_UNLIKELY(not j.is_object()))
889 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700890 JSON_THROW(type_error::create(312, "cannot use update() with", j.type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800891 }
892
893 for (auto it = j.cbegin(); it != j.cend(); ++it)
894 {
895 m_value.object->operator[](it.key()) = it.value();
896 }
897}
898
899void json::update(const_iterator first, const_iterator last)
900{
901 // implicitly convert null value to an empty object
902 if (is_null())
903 {
904 m_type = value_t::object;
905 m_value.object = create<object_t>();
906 assert_invariant();
907 }
908
909 if (JSON_UNLIKELY(not is_object()))
910 {
Austin Schuh812d0d12021-11-04 20:16:48 -0700911 JSON_THROW(type_error::create(312, "cannot use update() with", type_name()));
Brian Silverman8fce7482020-01-05 13:18:21 -0800912 }
913
914 // check if range iterators belong to the same JSON object
915 if (JSON_UNLIKELY(first.m_object != last.m_object))
916 {
917 JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
918 }
919
920 // passed iterators must belong to objects
921 if (JSON_UNLIKELY(not first.m_object->is_object()
922 or not last.m_object->is_object()))
923 {
924 JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
925 }
926
927 for (auto it = first; it != last; ++it)
928 {
929 m_value.object->operator[](it.key()) = it.value();
930 }
931}
932
933bool operator==(json::const_reference lhs, json::const_reference rhs) noexcept
934{
935 const auto lhs_type = lhs.type();
936 const auto rhs_type = rhs.type();
937
938 if (lhs_type == rhs_type)
939 {
940 switch (lhs_type)
941 {
942 case json::value_t::array:
943 return (*lhs.m_value.array == *rhs.m_value.array);
944
945 case json::value_t::object:
946 return (*lhs.m_value.object == *rhs.m_value.object);
947
948 case json::value_t::null:
949 return true;
950
951 case json::value_t::string:
952 return (*lhs.m_value.string == *rhs.m_value.string);
953
954 case json::value_t::boolean:
955 return (lhs.m_value.boolean == rhs.m_value.boolean);
956
957 case json::value_t::number_integer:
958 return (lhs.m_value.number_integer == rhs.m_value.number_integer);
959
960 case json::value_t::number_unsigned:
961 return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned);
962
963 case json::value_t::number_float:
964 return (lhs.m_value.number_float == rhs.m_value.number_float);
965
966 default:
967 return false;
968 }
969 }
970 else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_float)
971 {
972 return (static_cast<double>(lhs.m_value.number_integer) == rhs.m_value.number_float);
973 }
974 else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_integer)
975 {
976 return (lhs.m_value.number_float == static_cast<double>(rhs.m_value.number_integer));
977 }
978 else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_float)
979 {
980 return (static_cast<double>(lhs.m_value.number_unsigned) == rhs.m_value.number_float);
981 }
982 else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_unsigned)
983 {
984 return (lhs.m_value.number_float == static_cast<double>(rhs.m_value.number_unsigned));
985 }
986 else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_integer)
987 {
988 return (static_cast<int64_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer);
989 }
990 else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_unsigned)
991 {
992 return (lhs.m_value.number_integer == static_cast<int64_t>(rhs.m_value.number_unsigned));
993 }
994
995 return false;
996}
997
998bool operator<(json::const_reference lhs, json::const_reference rhs) noexcept
999{
1000 const auto lhs_type = lhs.type();
1001 const auto rhs_type = rhs.type();
1002
1003 if (lhs_type == rhs_type)
1004 {
1005 switch (lhs_type)
1006 {
1007 case json::value_t::array:
1008 return (*lhs.m_value.array) < (*rhs.m_value.array);
1009
1010 case json::value_t::object:
1011 return *lhs.m_value.object < *rhs.m_value.object;
1012
1013 case json::value_t::null:
1014 return false;
1015
1016 case json::value_t::string:
1017 return *lhs.m_value.string < *rhs.m_value.string;
1018
1019 case json::value_t::boolean:
1020 return lhs.m_value.boolean < rhs.m_value.boolean;
1021
1022 case json::value_t::number_integer:
1023 return lhs.m_value.number_integer < rhs.m_value.number_integer;
1024
1025 case json::value_t::number_unsigned:
1026 return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned;
1027
1028 case json::value_t::number_float:
1029 return lhs.m_value.number_float < rhs.m_value.number_float;
1030
1031 default:
1032 return false;
1033 }
1034 }
1035 else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_float)
1036 {
1037 return static_cast<double>(lhs.m_value.number_integer) < rhs.m_value.number_float;
1038 }
1039 else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_integer)
1040 {
1041 return lhs.m_value.number_float < static_cast<double>(rhs.m_value.number_integer);
1042 }
1043 else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_float)
1044 {
1045 return static_cast<double>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
1046 }
1047 else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_unsigned)
1048 {
1049 return lhs.m_value.number_float < static_cast<double>(rhs.m_value.number_unsigned);
1050 }
1051 else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_unsigned)
1052 {
1053 return lhs.m_value.number_integer < static_cast<int64_t>(rhs.m_value.number_unsigned);
1054 }
1055 else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_integer)
1056 {
1057 return static_cast<int64_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
1058 }
1059
1060 // We only reach this line if we cannot compare values. In that case,
1061 // we compare types. Note we have to call the operator explicitly,
1062 // because MSVC has problems otherwise.
1063 return operator<(lhs_type, rhs_type);
1064}
1065
1066const char* json::type_name() const noexcept
1067{
1068 {
1069 switch (m_type)
1070 {
1071 case value_t::null:
1072 return "null";
1073 case value_t::object:
1074 return "object";
1075 case value_t::array:
1076 return "array";
1077 case value_t::string:
1078 return "string";
1079 case value_t::boolean:
1080 return "boolean";
1081 case value_t::discarded:
1082 return "discarded";
1083 default:
1084 return "number";
1085 }
1086 }
1087}
1088
1089json json::patch(const json& json_patch) const
1090{
1091 // make a working copy to apply the patch to
1092 json result = *this;
1093
1094 // the valid JSON Patch operations
1095 enum class patch_operations {add, remove, replace, move, copy, test, invalid};
1096
1097 const auto get_op = [](const std::string & op)
1098 {
1099 if (op == "add")
1100 {
1101 return patch_operations::add;
1102 }
1103 if (op == "remove")
1104 {
1105 return patch_operations::remove;
1106 }
1107 if (op == "replace")
1108 {
1109 return patch_operations::replace;
1110 }
1111 if (op == "move")
1112 {
1113 return patch_operations::move;
1114 }
1115 if (op == "copy")
1116 {
1117 return patch_operations::copy;
1118 }
1119 if (op == "test")
1120 {
1121 return patch_operations::test;
1122 }
1123
1124 return patch_operations::invalid;
1125 };
1126
1127 // wrapper for "add" operation; add value at ptr
1128 const auto operation_add = [&result](json_pointer & ptr, json val)
1129 {
1130 // adding to the root of the target document means replacing it
1131 if (ptr.is_root())
1132 {
1133 result = val;
1134 }
1135 else
1136 {
1137 // make sure the top element of the pointer exists
1138 json_pointer top_pointer = ptr.top();
1139 if (top_pointer != ptr)
1140 {
1141 result.at(top_pointer);
1142 }
1143
1144 // get reference to parent of JSON pointer ptr
1145 const auto last_path = ptr.pop_back();
1146 json& parent = result[ptr];
1147
1148 switch (parent.m_type)
1149 {
1150 case value_t::null:
1151 case value_t::object:
1152 {
1153 // use operator[] to add value
1154 parent[last_path] = val;
1155 break;
1156 }
1157
1158 case value_t::array:
1159 {
1160 if (last_path == "-")
1161 {
1162 // special case: append to back
1163 parent.push_back(val);
1164 }
1165 else
1166 {
1167 const auto idx = json_pointer::array_index(last_path);
1168 if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
1169 {
1170 // avoid undefined behavior
Austin Schuh812d0d12021-11-04 20:16:48 -07001171 JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
Brian Silverman8fce7482020-01-05 13:18:21 -08001172 }
1173 else
1174 {
1175 // default case: insert add offset
1176 parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
1177 }
1178 }
1179 break;
1180 }
1181
1182 default:
1183 {
1184 // if there exists a parent it cannot be primitive
1185 assert(false); // LCOV_EXCL_LINE
1186 }
1187 }
1188 }
1189 };
1190
1191 // wrapper for "remove" operation; remove value at ptr
1192 const auto operation_remove = [&result](json_pointer & ptr)
1193 {
1194 // get reference to parent of JSON pointer ptr
1195 const auto last_path = ptr.pop_back();
1196 json& parent = result.at(ptr);
1197
1198 // remove child
1199 if (parent.is_object())
1200 {
1201 // perform range check
1202 auto it = parent.find(last_path);
1203 if (JSON_LIKELY(it != parent.end()))
1204 {
1205 parent.erase(it);
1206 }
1207 else
1208 {
Austin Schuh812d0d12021-11-04 20:16:48 -07001209 JSON_THROW(out_of_range::create(403, fmt::format("key '{}' not found", last_path)));
Brian Silverman8fce7482020-01-05 13:18:21 -08001210 }
1211 }
1212 else if (parent.is_array())
1213 {
1214 // note erase performs range check
1215 parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
1216 }
1217 };
1218
1219 // type check: top level value must be an array
1220 if (JSON_UNLIKELY(not json_patch.is_array()))
1221 {
1222 JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
1223 }
1224
1225 // iterate and apply the operations
1226 for (const auto& val : json_patch)
1227 {
1228 // wrapper to get a value for an operation
1229 const auto get_value = [&val](const std::string & op,
1230 const std::string & member,
1231 bool string_type) -> json &
1232 {
1233 // find value
1234 auto it = val.m_value.object->find(member);
1235
1236 // context-sensitive error message
1237 const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
1238
1239 // check if desired value is present
1240 if (JSON_UNLIKELY(it == val.m_value.object->end()))
1241 {
Austin Schuh812d0d12021-11-04 20:16:48 -07001242 JSON_THROW(parse_error::create(105, 0, fmt::format("{} must have member '{}'", error_msg, member)));
Brian Silverman8fce7482020-01-05 13:18:21 -08001243 }
1244
1245 // check if result is of type string
1246 if (JSON_UNLIKELY(string_type and not it->second.is_string()))
1247 {
Austin Schuh812d0d12021-11-04 20:16:48 -07001248 JSON_THROW(parse_error::create(105, 0, fmt::format("{} must have string member '{}'", error_msg, member)));
Brian Silverman8fce7482020-01-05 13:18:21 -08001249 }
1250
1251 // no error: return value
1252 return it->second;
1253 };
1254
1255 // type check: every element of the array must be an object
1256 if (JSON_UNLIKELY(not val.is_object()))
1257 {
1258 JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
1259 }
1260
1261 // collect mandatory members
1262 const std::string op = get_value("op", "op", true);
1263 const std::string path = get_value(op, "path", true);
1264 json_pointer ptr(path);
1265
1266 switch (get_op(op))
1267 {
1268 case patch_operations::add:
1269 {
1270 operation_add(ptr, get_value("add", "value", false));
1271 break;
1272 }
1273
1274 case patch_operations::remove:
1275 {
1276 operation_remove(ptr);
1277 break;
1278 }
1279
1280 case patch_operations::replace:
1281 {
1282 // the "path" location must exist - use at()
1283 result.at(ptr) = get_value("replace", "value", false);
1284 break;
1285 }
1286
1287 case patch_operations::move:
1288 {
1289 const std::string from_path = get_value("move", "from", true);
1290 json_pointer from_ptr(from_path);
1291
1292 // the "from" location must exist - use at()
1293 json v = result.at(from_ptr);
1294
1295 // The move operation is functionally identical to a
1296 // "remove" operation on the "from" location, followed
1297 // immediately by an "add" operation at the target
1298 // location with the value that was just removed.
1299 operation_remove(from_ptr);
1300 operation_add(ptr, v);
1301 break;
1302 }
1303
1304 case patch_operations::copy:
1305 {
1306 const std::string from_path = get_value("copy", "from", true);
1307 const json_pointer from_ptr(from_path);
1308
1309 // the "from" location must exist - use at()
1310 json v = result.at(from_ptr);
1311
1312 // The copy is functionally identical to an "add"
1313 // operation at the target location using the value
1314 // specified in the "from" member.
1315 operation_add(ptr, v);
1316 break;
1317 }
1318
1319 case patch_operations::test:
1320 {
1321 bool success = false;
1322 JSON_TRY
1323 {
1324 // check if "value" matches the one at "path"
1325 // the "path" location must exist - use at()
1326 success = (result.at(ptr) == get_value("test", "value", false));
1327 }
1328 JSON_CATCH (out_of_range&)
1329 {
1330 // ignore out of range errors: success remains false
1331 }
1332
1333 // throw an exception if test fails
1334 if (JSON_UNLIKELY(not success))
1335 {
Austin Schuh812d0d12021-11-04 20:16:48 -07001336 JSON_THROW(other_error::create(501, fmt::format("unsuccessful: {}", val.dump())));
Brian Silverman8fce7482020-01-05 13:18:21 -08001337 }
1338
1339 break;
1340 }
1341
1342 case patch_operations::invalid:
1343 {
1344 // op must be "add", "remove", "replace", "move", "copy", or
1345 // "test"
Austin Schuh812d0d12021-11-04 20:16:48 -07001346 JSON_THROW(parse_error::create(105, 0, fmt::format("operation value '{}' is invalid", op)));
Brian Silverman8fce7482020-01-05 13:18:21 -08001347 }
1348 }
1349 }
1350
1351 return result;
1352}
1353
1354json json::diff(const json& source, const json& target,
1355 const std::string& path)
1356{
1357 // the patch
1358 json result(value_t::array);
1359
1360 // if the values are the same, return empty patch
1361 if (source == target)
1362 {
1363 return result;
1364 }
1365
1366 if (source.type() != target.type())
1367 {
1368 // different types: replace value
1369 result.push_back(
1370 {
1371 {"op", "replace"}, {"path", path}, {"value", target}
1372 });
1373 }
1374 else
1375 {
1376 switch (source.type())
1377 {
1378 case value_t::array:
1379 {
1380 // first pass: traverse common elements
1381 std::size_t i = 0;
1382 while (i < source.size() and i < target.size())
1383 {
1384 // recursive call to compare array values at index i
1385 auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
1386 result.insert(result.end(), temp_diff.begin(), temp_diff.end());
1387 ++i;
1388 }
1389
1390 // i now reached the end of at least one array
1391 // in a second pass, traverse the remaining elements
1392
1393 // remove my remaining elements
1394 const auto end_index = static_cast<difference_type>(result.size());
1395 while (i < source.size())
1396 {
1397 // add operations in reverse order to avoid invalid
1398 // indices
1399 result.insert(result.begin() + end_index, object(
1400 {
1401 {"op", "remove"},
1402 {"path", path + "/" + std::to_string(i)}
1403 }));
1404 ++i;
1405 }
1406
1407 // add other remaining elements
1408 while (i < target.size())
1409 {
1410 result.push_back(
1411 {
1412 {"op", "add"},
1413 {"path", path + "/" + std::to_string(i)},
1414 {"value", target[i]}
1415 });
1416 ++i;
1417 }
1418
1419 break;
1420 }
1421
1422 case value_t::object:
1423 {
1424 // first pass: traverse this object's elements
1425 for (auto it = source.cbegin(); it != source.cend(); ++it)
1426 {
1427 // escape the key name to be used in a JSON patch
Austin Schuh812d0d12021-11-04 20:16:48 -07001428 const auto key = json_pointer::escape(std::string{it.key()});
Brian Silverman8fce7482020-01-05 13:18:21 -08001429
1430 if (target.find(it.key()) != target.end())
1431 {
1432 // recursive call to compare object values at key it
1433 auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key);
1434 result.insert(result.end(), temp_diff.begin(), temp_diff.end());
1435 }
1436 else
1437 {
1438 // found a key that is not in o -> remove it
1439 result.push_back(object(
1440 {
1441 {"op", "remove"}, {"path", path + "/" + key}
1442 }));
1443 }
1444 }
1445
1446 // second pass: traverse other object's elements
1447 for (auto it = target.cbegin(); it != target.cend(); ++it)
1448 {
1449 if (source.find(it.key()) == source.end())
1450 {
1451 // found a key that is not in this -> add it
Austin Schuh812d0d12021-11-04 20:16:48 -07001452 const auto key = json_pointer::escape(std::string{it.key()});
Brian Silverman8fce7482020-01-05 13:18:21 -08001453 result.push_back(
1454 {
1455 {"op", "add"}, {"path", path + "/" + key},
1456 {"value", it.value()}
1457 });
1458 }
1459 }
1460
1461 break;
1462 }
1463
1464 default:
1465 {
1466 // both primitive type: replace value
1467 result.push_back(
1468 {
1469 {"op", "replace"}, {"path", path}, {"value", target}
1470 });
1471 break;
1472 }
1473 }
1474 }
1475
1476 return result;
1477}
1478
1479void json::merge_patch(const json& patch)
1480{
1481 if (patch.is_object())
1482 {
1483 if (not is_object())
1484 {
1485 *this = object();
1486 }
1487 for (auto it = patch.begin(); it != patch.end(); ++it)
1488 {
1489 if (it.value().is_null())
1490 {
1491 erase(it.key());
1492 }
1493 else
1494 {
1495 operator[](it.key()).merge_patch(it.value());
1496 }
1497 }
1498 }
1499 else
1500 {
1501 *this = patch;
1502 }
1503}
1504
1505} // namespace wpi