1c5f01b2fSopenharmony_ci// __ _____ _____ _____ 2c5f01b2fSopenharmony_ci// __| | __| | | | JSON for Modern C++ (supporting code) 3c5f01b2fSopenharmony_ci// | | |__ | | | | | | version 3.11.2 4c5f01b2fSopenharmony_ci// |_____|_____|_____|_|___| https://github.com/nlohmann/json 5c5f01b2fSopenharmony_ci// 6c5f01b2fSopenharmony_ci// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me> 7c5f01b2fSopenharmony_ci// SPDX-License-Identifier: MIT 8c5f01b2fSopenharmony_ci 9c5f01b2fSopenharmony_ci#include "doctest_compatibility.h" 10c5f01b2fSopenharmony_ci 11c5f01b2fSopenharmony_ci#include <nlohmann/json.hpp> 12c5f01b2fSopenharmony_ciusing nlohmann::json; 13c5f01b2fSopenharmony_ci#ifdef JSON_TEST_NO_GLOBAL_UDLS 14c5f01b2fSopenharmony_ci using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) 15c5f01b2fSopenharmony_ci#endif 16c5f01b2fSopenharmony_ci 17c5f01b2fSopenharmony_ci#include <fstream> 18c5f01b2fSopenharmony_ci#include "make_test_data_available.hpp" 19c5f01b2fSopenharmony_ci 20c5f01b2fSopenharmony_ciTEST_CASE("JSON patch") 21c5f01b2fSopenharmony_ci{ 22c5f01b2fSopenharmony_ci SECTION("examples from RFC 6902") 23c5f01b2fSopenharmony_ci { 24c5f01b2fSopenharmony_ci SECTION("4. Operations") 25c5f01b2fSopenharmony_ci { 26c5f01b2fSopenharmony_ci // the ordering of members in JSON objects is not significant: 27c5f01b2fSopenharmony_ci json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json; 28c5f01b2fSopenharmony_ci json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json; 29c5f01b2fSopenharmony_ci json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json; 30c5f01b2fSopenharmony_ci 31c5f01b2fSopenharmony_ci // check if the operation objects are equivalent 32c5f01b2fSopenharmony_ci CHECK(op1 == op2); 33c5f01b2fSopenharmony_ci CHECK(op1 == op3); 34c5f01b2fSopenharmony_ci } 35c5f01b2fSopenharmony_ci 36c5f01b2fSopenharmony_ci SECTION("4.1 add") 37c5f01b2fSopenharmony_ci { 38c5f01b2fSopenharmony_ci json patch1 = R"([{ "op": "add", "path": "/a/b", "value": [ "foo", "bar" ] }])"_json; 39c5f01b2fSopenharmony_ci 40c5f01b2fSopenharmony_ci // However, the object itself or an array containing it does need 41c5f01b2fSopenharmony_ci // to exist, and it remains an error for that not to be the case. 42c5f01b2fSopenharmony_ci // For example, an "add" with a target location of "/a/b" starting 43c5f01b2fSopenharmony_ci // with this document 44c5f01b2fSopenharmony_ci json doc1 = R"({ "a": { "foo": 1 } })"_json; 45c5f01b2fSopenharmony_ci 46c5f01b2fSopenharmony_ci // is not an error, because "a" exists, and "b" will be added to 47c5f01b2fSopenharmony_ci // its value. 48c5f01b2fSopenharmony_ci CHECK_NOTHROW(doc1.patch(patch1)); 49c5f01b2fSopenharmony_ci auto doc1_ans = R"( 50c5f01b2fSopenharmony_ci { 51c5f01b2fSopenharmony_ci "a": { 52c5f01b2fSopenharmony_ci "foo": 1, 53c5f01b2fSopenharmony_ci "b": [ "foo", "bar" ] 54c5f01b2fSopenharmony_ci } 55c5f01b2fSopenharmony_ci } 56c5f01b2fSopenharmony_ci )"_json; 57c5f01b2fSopenharmony_ci CHECK(doc1.patch(patch1) == doc1_ans); 58c5f01b2fSopenharmony_ci 59c5f01b2fSopenharmony_ci // It is an error in this document: 60c5f01b2fSopenharmony_ci json doc2 = R"({ "q": { "bar": 2 } })"_json; 61c5f01b2fSopenharmony_ci 62c5f01b2fSopenharmony_ci // because "a" does not exist. 63c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(doc2.patch(patch1), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); 64c5f01b2fSopenharmony_ci 65c5f01b2fSopenharmony_ci json doc3 = R"({ "a": {} })"_json; 66c5f01b2fSopenharmony_ci json patch2 = R"([{ "op": "add", "path": "/a/b/c", "value": 1 }])"_json; 67c5f01b2fSopenharmony_ci 68c5f01b2fSopenharmony_ci // should cause an error because "b" does not exist in doc3 69c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 70c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(doc3.patch(patch2), "[json.exception.out_of_range.403] (/a) key 'b' not found", json::out_of_range&); 71c5f01b2fSopenharmony_ci#else 72c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(doc3.patch(patch2), "[json.exception.out_of_range.403] key 'b' not found", json::out_of_range&); 73c5f01b2fSopenharmony_ci#endif 74c5f01b2fSopenharmony_ci } 75c5f01b2fSopenharmony_ci 76c5f01b2fSopenharmony_ci SECTION("4.2 remove") 77c5f01b2fSopenharmony_ci { 78c5f01b2fSopenharmony_ci // If removing an element from an array, any elements above the 79c5f01b2fSopenharmony_ci // specified index are shifted one position to the left. 80c5f01b2fSopenharmony_ci json doc = {1, 2, 3, 4}; 81c5f01b2fSopenharmony_ci json patch = {{{"op", "remove"}, {"path", "/1"}}}; 82c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == json({1, 3, 4})); 83c5f01b2fSopenharmony_ci } 84c5f01b2fSopenharmony_ci 85c5f01b2fSopenharmony_ci SECTION("A.1. Adding an Object Member") 86c5f01b2fSopenharmony_ci { 87c5f01b2fSopenharmony_ci // An example target JSON document: 88c5f01b2fSopenharmony_ci json doc = R"( 89c5f01b2fSopenharmony_ci { "foo": "bar"} 90c5f01b2fSopenharmony_ci )"_json; 91c5f01b2fSopenharmony_ci 92c5f01b2fSopenharmony_ci // A JSON Patch document: 93c5f01b2fSopenharmony_ci json patch = R"( 94c5f01b2fSopenharmony_ci [ 95c5f01b2fSopenharmony_ci { "op": "add", "path": "/baz", "value": "qux" } 96c5f01b2fSopenharmony_ci ] 97c5f01b2fSopenharmony_ci )"_json; 98c5f01b2fSopenharmony_ci 99c5f01b2fSopenharmony_ci // The resulting JSON document: 100c5f01b2fSopenharmony_ci json expected = R"( 101c5f01b2fSopenharmony_ci { 102c5f01b2fSopenharmony_ci "baz": "qux", 103c5f01b2fSopenharmony_ci "foo": "bar" 104c5f01b2fSopenharmony_ci } 105c5f01b2fSopenharmony_ci )"_json; 106c5f01b2fSopenharmony_ci 107c5f01b2fSopenharmony_ci // check if patched value is as expected 108c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 109c5f01b2fSopenharmony_ci 110c5f01b2fSopenharmony_ci // check roundtrip 111c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 112c5f01b2fSopenharmony_ci } 113c5f01b2fSopenharmony_ci 114c5f01b2fSopenharmony_ci SECTION("A.2. Adding an Array Element") 115c5f01b2fSopenharmony_ci { 116c5f01b2fSopenharmony_ci // An example target JSON document: 117c5f01b2fSopenharmony_ci json doc = R"( 118c5f01b2fSopenharmony_ci { "foo": [ "bar", "baz" ] } 119c5f01b2fSopenharmony_ci )"_json; 120c5f01b2fSopenharmony_ci 121c5f01b2fSopenharmony_ci // A JSON Patch document: 122c5f01b2fSopenharmony_ci json patch = R"( 123c5f01b2fSopenharmony_ci [ 124c5f01b2fSopenharmony_ci { "op": "add", "path": "/foo/1", "value": "qux" } 125c5f01b2fSopenharmony_ci ] 126c5f01b2fSopenharmony_ci )"_json; 127c5f01b2fSopenharmony_ci 128c5f01b2fSopenharmony_ci // The resulting JSON document: 129c5f01b2fSopenharmony_ci json expected = R"( 130c5f01b2fSopenharmony_ci { "foo": [ "bar", "qux", "baz" ] } 131c5f01b2fSopenharmony_ci )"_json; 132c5f01b2fSopenharmony_ci 133c5f01b2fSopenharmony_ci // check if patched value is as expected 134c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 135c5f01b2fSopenharmony_ci 136c5f01b2fSopenharmony_ci // check roundtrip 137c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 138c5f01b2fSopenharmony_ci } 139c5f01b2fSopenharmony_ci 140c5f01b2fSopenharmony_ci SECTION("A.3. Removing an Object Member") 141c5f01b2fSopenharmony_ci { 142c5f01b2fSopenharmony_ci // An example target JSON document: 143c5f01b2fSopenharmony_ci json doc = R"( 144c5f01b2fSopenharmony_ci { 145c5f01b2fSopenharmony_ci "baz": "qux", 146c5f01b2fSopenharmony_ci "foo": "bar" 147c5f01b2fSopenharmony_ci } 148c5f01b2fSopenharmony_ci )"_json; 149c5f01b2fSopenharmony_ci 150c5f01b2fSopenharmony_ci // A JSON Patch document: 151c5f01b2fSopenharmony_ci json patch = R"( 152c5f01b2fSopenharmony_ci [ 153c5f01b2fSopenharmony_ci { "op": "remove", "path": "/baz" } 154c5f01b2fSopenharmony_ci ] 155c5f01b2fSopenharmony_ci )"_json; 156c5f01b2fSopenharmony_ci 157c5f01b2fSopenharmony_ci // The resulting JSON document: 158c5f01b2fSopenharmony_ci json expected = R"( 159c5f01b2fSopenharmony_ci { "foo": "bar" } 160c5f01b2fSopenharmony_ci )"_json; 161c5f01b2fSopenharmony_ci 162c5f01b2fSopenharmony_ci // check if patched value is as expected 163c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 164c5f01b2fSopenharmony_ci 165c5f01b2fSopenharmony_ci // check roundtrip 166c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 167c5f01b2fSopenharmony_ci } 168c5f01b2fSopenharmony_ci 169c5f01b2fSopenharmony_ci SECTION("A.4. Removing an Array Element") 170c5f01b2fSopenharmony_ci { 171c5f01b2fSopenharmony_ci // An example target JSON document: 172c5f01b2fSopenharmony_ci json doc = R"( 173c5f01b2fSopenharmony_ci { "foo": [ "bar", "qux", "baz" ] } 174c5f01b2fSopenharmony_ci )"_json; 175c5f01b2fSopenharmony_ci 176c5f01b2fSopenharmony_ci // A JSON Patch document: 177c5f01b2fSopenharmony_ci json patch = R"( 178c5f01b2fSopenharmony_ci [ 179c5f01b2fSopenharmony_ci { "op": "remove", "path": "/foo/1" } 180c5f01b2fSopenharmony_ci ] 181c5f01b2fSopenharmony_ci )"_json; 182c5f01b2fSopenharmony_ci 183c5f01b2fSopenharmony_ci // The resulting JSON document: 184c5f01b2fSopenharmony_ci json expected = R"( 185c5f01b2fSopenharmony_ci { "foo": [ "bar", "baz" ] } 186c5f01b2fSopenharmony_ci )"_json; 187c5f01b2fSopenharmony_ci 188c5f01b2fSopenharmony_ci // check if patched value is as expected 189c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 190c5f01b2fSopenharmony_ci 191c5f01b2fSopenharmony_ci // check roundtrip 192c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 193c5f01b2fSopenharmony_ci } 194c5f01b2fSopenharmony_ci 195c5f01b2fSopenharmony_ci SECTION("A.5. Replacing a Value") 196c5f01b2fSopenharmony_ci { 197c5f01b2fSopenharmony_ci // An example target JSON document: 198c5f01b2fSopenharmony_ci json doc = R"( 199c5f01b2fSopenharmony_ci { 200c5f01b2fSopenharmony_ci "baz": "qux", 201c5f01b2fSopenharmony_ci "foo": "bar" 202c5f01b2fSopenharmony_ci } 203c5f01b2fSopenharmony_ci )"_json; 204c5f01b2fSopenharmony_ci 205c5f01b2fSopenharmony_ci // A JSON Patch document: 206c5f01b2fSopenharmony_ci json patch = R"( 207c5f01b2fSopenharmony_ci [ 208c5f01b2fSopenharmony_ci { "op": "replace", "path": "/baz", "value": "boo" } 209c5f01b2fSopenharmony_ci ] 210c5f01b2fSopenharmony_ci )"_json; 211c5f01b2fSopenharmony_ci 212c5f01b2fSopenharmony_ci json expected = R"( 213c5f01b2fSopenharmony_ci { 214c5f01b2fSopenharmony_ci "baz": "boo", 215c5f01b2fSopenharmony_ci "foo": "bar" 216c5f01b2fSopenharmony_ci } 217c5f01b2fSopenharmony_ci )"_json; 218c5f01b2fSopenharmony_ci 219c5f01b2fSopenharmony_ci // check if patched value is as expected 220c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 221c5f01b2fSopenharmony_ci 222c5f01b2fSopenharmony_ci // check roundtrip 223c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 224c5f01b2fSopenharmony_ci } 225c5f01b2fSopenharmony_ci 226c5f01b2fSopenharmony_ci SECTION("A.6. Moving a Value") 227c5f01b2fSopenharmony_ci { 228c5f01b2fSopenharmony_ci // An example target JSON document: 229c5f01b2fSopenharmony_ci json doc = R"( 230c5f01b2fSopenharmony_ci { 231c5f01b2fSopenharmony_ci "foo": { 232c5f01b2fSopenharmony_ci "bar": "baz", 233c5f01b2fSopenharmony_ci "waldo": "fred" 234c5f01b2fSopenharmony_ci }, 235c5f01b2fSopenharmony_ci "qux": { 236c5f01b2fSopenharmony_ci "corge": "grault" 237c5f01b2fSopenharmony_ci } 238c5f01b2fSopenharmony_ci } 239c5f01b2fSopenharmony_ci )"_json; 240c5f01b2fSopenharmony_ci 241c5f01b2fSopenharmony_ci // A JSON Patch document: 242c5f01b2fSopenharmony_ci json patch = R"( 243c5f01b2fSopenharmony_ci [ 244c5f01b2fSopenharmony_ci { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } 245c5f01b2fSopenharmony_ci ] 246c5f01b2fSopenharmony_ci )"_json; 247c5f01b2fSopenharmony_ci 248c5f01b2fSopenharmony_ci // The resulting JSON document: 249c5f01b2fSopenharmony_ci json expected = R"( 250c5f01b2fSopenharmony_ci { 251c5f01b2fSopenharmony_ci "foo": { 252c5f01b2fSopenharmony_ci "bar": "baz" 253c5f01b2fSopenharmony_ci }, 254c5f01b2fSopenharmony_ci "qux": { 255c5f01b2fSopenharmony_ci "corge": "grault", 256c5f01b2fSopenharmony_ci "thud": "fred" 257c5f01b2fSopenharmony_ci } 258c5f01b2fSopenharmony_ci } 259c5f01b2fSopenharmony_ci )"_json; 260c5f01b2fSopenharmony_ci 261c5f01b2fSopenharmony_ci // check if patched value is as expected 262c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 263c5f01b2fSopenharmony_ci 264c5f01b2fSopenharmony_ci // check roundtrip 265c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 266c5f01b2fSopenharmony_ci } 267c5f01b2fSopenharmony_ci 268c5f01b2fSopenharmony_ci SECTION("A.7. Moving a Value") 269c5f01b2fSopenharmony_ci { 270c5f01b2fSopenharmony_ci // An example target JSON document: 271c5f01b2fSopenharmony_ci json doc = R"( 272c5f01b2fSopenharmony_ci { "foo": [ "all", "grass", "cows", "eat" ] } 273c5f01b2fSopenharmony_ci )"_json; 274c5f01b2fSopenharmony_ci 275c5f01b2fSopenharmony_ci // A JSON Patch document: 276c5f01b2fSopenharmony_ci json patch = R"( 277c5f01b2fSopenharmony_ci [ 278c5f01b2fSopenharmony_ci { "op": "move", "from": "/foo/1", "path": "/foo/3" } 279c5f01b2fSopenharmony_ci ] 280c5f01b2fSopenharmony_ci )"_json; 281c5f01b2fSopenharmony_ci 282c5f01b2fSopenharmony_ci // The resulting JSON document: 283c5f01b2fSopenharmony_ci json expected = R"( 284c5f01b2fSopenharmony_ci { "foo": [ "all", "cows", "eat", "grass" ] } 285c5f01b2fSopenharmony_ci )"_json; 286c5f01b2fSopenharmony_ci 287c5f01b2fSopenharmony_ci // check if patched value is as expected 288c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 289c5f01b2fSopenharmony_ci 290c5f01b2fSopenharmony_ci // check roundtrip 291c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 292c5f01b2fSopenharmony_ci } 293c5f01b2fSopenharmony_ci 294c5f01b2fSopenharmony_ci SECTION("A.8. Testing a Value: Success") 295c5f01b2fSopenharmony_ci { 296c5f01b2fSopenharmony_ci // An example target JSON document: 297c5f01b2fSopenharmony_ci json doc = R"( 298c5f01b2fSopenharmony_ci { 299c5f01b2fSopenharmony_ci "baz": "qux", 300c5f01b2fSopenharmony_ci "foo": [ "a", 2, "c" ] 301c5f01b2fSopenharmony_ci } 302c5f01b2fSopenharmony_ci )"_json; 303c5f01b2fSopenharmony_ci 304c5f01b2fSopenharmony_ci // A JSON Patch document that will result in successful evaluation: 305c5f01b2fSopenharmony_ci json patch = R"( 306c5f01b2fSopenharmony_ci [ 307c5f01b2fSopenharmony_ci { "op": "test", "path": "/baz", "value": "qux" }, 308c5f01b2fSopenharmony_ci { "op": "test", "path": "/foo/1", "value": 2 } 309c5f01b2fSopenharmony_ci ] 310c5f01b2fSopenharmony_ci )"_json; 311c5f01b2fSopenharmony_ci 312c5f01b2fSopenharmony_ci // check if evaluation does not throw 313c5f01b2fSopenharmony_ci CHECK_NOTHROW(doc.patch(patch)); 314c5f01b2fSopenharmony_ci // check if patched document is unchanged 315c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == doc); 316c5f01b2fSopenharmony_ci } 317c5f01b2fSopenharmony_ci 318c5f01b2fSopenharmony_ci SECTION("A.9. Testing a Value: Error") 319c5f01b2fSopenharmony_ci { 320c5f01b2fSopenharmony_ci // An example target JSON document: 321c5f01b2fSopenharmony_ci json doc = R"( 322c5f01b2fSopenharmony_ci { "baz": "qux" } 323c5f01b2fSopenharmony_ci )"_json; 324c5f01b2fSopenharmony_ci 325c5f01b2fSopenharmony_ci // A JSON Patch document that will result in an error condition: 326c5f01b2fSopenharmony_ci json patch = R"( 327c5f01b2fSopenharmony_ci [ 328c5f01b2fSopenharmony_ci { "op": "test", "path": "/baz", "value": "bar" } 329c5f01b2fSopenharmony_ci ] 330c5f01b2fSopenharmony_ci )"_json; 331c5f01b2fSopenharmony_ci 332c5f01b2fSopenharmony_ci // check that evaluation throws 333c5f01b2fSopenharmony_ci CHECK_THROWS_AS(doc.patch(patch), json::other_error&); 334c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 335c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); 336c5f01b2fSopenharmony_ci#else 337c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); 338c5f01b2fSopenharmony_ci#endif 339c5f01b2fSopenharmony_ci } 340c5f01b2fSopenharmony_ci 341c5f01b2fSopenharmony_ci SECTION("A.10. Adding a Nested Member Object") 342c5f01b2fSopenharmony_ci { 343c5f01b2fSopenharmony_ci // An example target JSON document: 344c5f01b2fSopenharmony_ci json doc = R"( 345c5f01b2fSopenharmony_ci { "foo": "bar" } 346c5f01b2fSopenharmony_ci )"_json; 347c5f01b2fSopenharmony_ci 348c5f01b2fSopenharmony_ci // A JSON Patch document: 349c5f01b2fSopenharmony_ci json patch = R"( 350c5f01b2fSopenharmony_ci [ 351c5f01b2fSopenharmony_ci { "op": "add", "path": "/child", "value": { "grandchild": { } } } 352c5f01b2fSopenharmony_ci ] 353c5f01b2fSopenharmony_ci )"_json; 354c5f01b2fSopenharmony_ci 355c5f01b2fSopenharmony_ci // The resulting JSON document: 356c5f01b2fSopenharmony_ci json expected = R"( 357c5f01b2fSopenharmony_ci { 358c5f01b2fSopenharmony_ci "foo": "bar", 359c5f01b2fSopenharmony_ci "child": { 360c5f01b2fSopenharmony_ci "grandchild": { 361c5f01b2fSopenharmony_ci } 362c5f01b2fSopenharmony_ci } 363c5f01b2fSopenharmony_ci } 364c5f01b2fSopenharmony_ci )"_json; 365c5f01b2fSopenharmony_ci 366c5f01b2fSopenharmony_ci // check if patched value is as expected 367c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 368c5f01b2fSopenharmony_ci 369c5f01b2fSopenharmony_ci // check roundtrip 370c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 371c5f01b2fSopenharmony_ci } 372c5f01b2fSopenharmony_ci 373c5f01b2fSopenharmony_ci SECTION("A.11. Ignoring Unrecognized Elements") 374c5f01b2fSopenharmony_ci { 375c5f01b2fSopenharmony_ci // An example target JSON document: 376c5f01b2fSopenharmony_ci json doc = R"( 377c5f01b2fSopenharmony_ci { "foo": "bar" } 378c5f01b2fSopenharmony_ci )"_json; 379c5f01b2fSopenharmony_ci 380c5f01b2fSopenharmony_ci // A JSON Patch document: 381c5f01b2fSopenharmony_ci json patch = R"( 382c5f01b2fSopenharmony_ci [ 383c5f01b2fSopenharmony_ci { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } 384c5f01b2fSopenharmony_ci ] 385c5f01b2fSopenharmony_ci )"_json; 386c5f01b2fSopenharmony_ci 387c5f01b2fSopenharmony_ci json expected = R"( 388c5f01b2fSopenharmony_ci { 389c5f01b2fSopenharmony_ci "foo": "bar", 390c5f01b2fSopenharmony_ci "baz": "qux" 391c5f01b2fSopenharmony_ci } 392c5f01b2fSopenharmony_ci )"_json; 393c5f01b2fSopenharmony_ci 394c5f01b2fSopenharmony_ci // check if patched value is as expected 395c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 396c5f01b2fSopenharmony_ci 397c5f01b2fSopenharmony_ci // check roundtrip 398c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 399c5f01b2fSopenharmony_ci } 400c5f01b2fSopenharmony_ci 401c5f01b2fSopenharmony_ci SECTION("A.12. Adding to a Nonexistent Target") 402c5f01b2fSopenharmony_ci { 403c5f01b2fSopenharmony_ci // An example target JSON document: 404c5f01b2fSopenharmony_ci json doc = R"( 405c5f01b2fSopenharmony_ci { "foo": "bar" } 406c5f01b2fSopenharmony_ci )"_json; 407c5f01b2fSopenharmony_ci 408c5f01b2fSopenharmony_ci // A JSON Patch document: 409c5f01b2fSopenharmony_ci json patch = R"( 410c5f01b2fSopenharmony_ci [ 411c5f01b2fSopenharmony_ci { "op": "add", "path": "/baz/bat", "value": "qux" } 412c5f01b2fSopenharmony_ci ] 413c5f01b2fSopenharmony_ci )"_json; 414c5f01b2fSopenharmony_ci 415c5f01b2fSopenharmony_ci // This JSON Patch document, applied to the target JSON document 416c5f01b2fSopenharmony_ci // above, would result in an error (therefore, it would not be 417c5f01b2fSopenharmony_ci // applied), because the "add" operation's target location that 418c5f01b2fSopenharmony_ci // references neither the root of the document, nor a member of 419c5f01b2fSopenharmony_ci // an existing object, nor a member of an existing array. 420c5f01b2fSopenharmony_ci 421c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); 422c5f01b2fSopenharmony_ci } 423c5f01b2fSopenharmony_ci 424c5f01b2fSopenharmony_ci // A.13. Invalid JSON Patch Document 425c5f01b2fSopenharmony_ci // not applicable 426c5f01b2fSopenharmony_ci 427c5f01b2fSopenharmony_ci SECTION("A.14. Escape Ordering") 428c5f01b2fSopenharmony_ci { 429c5f01b2fSopenharmony_ci // An example target JSON document: 430c5f01b2fSopenharmony_ci json doc = R"( 431c5f01b2fSopenharmony_ci { 432c5f01b2fSopenharmony_ci "/": 9, 433c5f01b2fSopenharmony_ci "~1": 10 434c5f01b2fSopenharmony_ci } 435c5f01b2fSopenharmony_ci )"_json; 436c5f01b2fSopenharmony_ci 437c5f01b2fSopenharmony_ci // A JSON Patch document: 438c5f01b2fSopenharmony_ci json patch = R"( 439c5f01b2fSopenharmony_ci [ 440c5f01b2fSopenharmony_ci {"op": "test", "path": "/~01", "value": 10} 441c5f01b2fSopenharmony_ci ] 442c5f01b2fSopenharmony_ci )"_json; 443c5f01b2fSopenharmony_ci 444c5f01b2fSopenharmony_ci json expected = R"( 445c5f01b2fSopenharmony_ci { 446c5f01b2fSopenharmony_ci "/": 9, 447c5f01b2fSopenharmony_ci "~1": 10 448c5f01b2fSopenharmony_ci } 449c5f01b2fSopenharmony_ci )"_json; 450c5f01b2fSopenharmony_ci 451c5f01b2fSopenharmony_ci // check if patched value is as expected 452c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 453c5f01b2fSopenharmony_ci 454c5f01b2fSopenharmony_ci // check roundtrip 455c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 456c5f01b2fSopenharmony_ci } 457c5f01b2fSopenharmony_ci 458c5f01b2fSopenharmony_ci SECTION("A.15. Comparing Strings and Numbers") 459c5f01b2fSopenharmony_ci { 460c5f01b2fSopenharmony_ci // An example target JSON document: 461c5f01b2fSopenharmony_ci json doc = R"( 462c5f01b2fSopenharmony_ci { 463c5f01b2fSopenharmony_ci "/": 9, 464c5f01b2fSopenharmony_ci "~1": 10 465c5f01b2fSopenharmony_ci } 466c5f01b2fSopenharmony_ci )"_json; 467c5f01b2fSopenharmony_ci 468c5f01b2fSopenharmony_ci // A JSON Patch document that will result in an error condition: 469c5f01b2fSopenharmony_ci json patch = R"( 470c5f01b2fSopenharmony_ci [ 471c5f01b2fSopenharmony_ci {"op": "test", "path": "/~01", "value": "10"} 472c5f01b2fSopenharmony_ci ] 473c5f01b2fSopenharmony_ci )"_json; 474c5f01b2fSopenharmony_ci 475c5f01b2fSopenharmony_ci // check that evaluation throws 476c5f01b2fSopenharmony_ci CHECK_THROWS_AS(doc.patch(patch), json::other_error&); 477c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 478c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); 479c5f01b2fSopenharmony_ci#else 480c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); 481c5f01b2fSopenharmony_ci#endif 482c5f01b2fSopenharmony_ci } 483c5f01b2fSopenharmony_ci 484c5f01b2fSopenharmony_ci SECTION("A.16. Adding an Array Value") 485c5f01b2fSopenharmony_ci { 486c5f01b2fSopenharmony_ci // An example target JSON document: 487c5f01b2fSopenharmony_ci json doc = R"( 488c5f01b2fSopenharmony_ci { "foo": ["bar"] } 489c5f01b2fSopenharmony_ci )"_json; 490c5f01b2fSopenharmony_ci 491c5f01b2fSopenharmony_ci // A JSON Patch document: 492c5f01b2fSopenharmony_ci json patch = R"( 493c5f01b2fSopenharmony_ci [ 494c5f01b2fSopenharmony_ci { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } 495c5f01b2fSopenharmony_ci ] 496c5f01b2fSopenharmony_ci )"_json; 497c5f01b2fSopenharmony_ci 498c5f01b2fSopenharmony_ci // The resulting JSON document: 499c5f01b2fSopenharmony_ci json expected = R"( 500c5f01b2fSopenharmony_ci { "foo": ["bar", ["abc", "def"]] } 501c5f01b2fSopenharmony_ci )"_json; 502c5f01b2fSopenharmony_ci 503c5f01b2fSopenharmony_ci // check if patched value is as expected 504c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 505c5f01b2fSopenharmony_ci 506c5f01b2fSopenharmony_ci // check roundtrip 507c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 508c5f01b2fSopenharmony_ci } 509c5f01b2fSopenharmony_ci } 510c5f01b2fSopenharmony_ci 511c5f01b2fSopenharmony_ci SECTION("own examples") 512c5f01b2fSopenharmony_ci { 513c5f01b2fSopenharmony_ci SECTION("add") 514c5f01b2fSopenharmony_ci { 515c5f01b2fSopenharmony_ci SECTION("add to the root element") 516c5f01b2fSopenharmony_ci { 517c5f01b2fSopenharmony_ci // If the path is the root of the target document - the 518c5f01b2fSopenharmony_ci // specified value becomes the entire content of the target 519c5f01b2fSopenharmony_ci // document. 520c5f01b2fSopenharmony_ci 521c5f01b2fSopenharmony_ci // An example target JSON document: 522c5f01b2fSopenharmony_ci json doc = 17; 523c5f01b2fSopenharmony_ci 524c5f01b2fSopenharmony_ci // A JSON Patch document: 525c5f01b2fSopenharmony_ci json patch = R"( 526c5f01b2fSopenharmony_ci [ 527c5f01b2fSopenharmony_ci { "op": "add", "path": "", "value": [1,2,3] } 528c5f01b2fSopenharmony_ci ] 529c5f01b2fSopenharmony_ci )"_json; 530c5f01b2fSopenharmony_ci 531c5f01b2fSopenharmony_ci // The resulting JSON document: 532c5f01b2fSopenharmony_ci json expected = {1, 2, 3}; 533c5f01b2fSopenharmony_ci 534c5f01b2fSopenharmony_ci // check if patched value is as expected 535c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 536c5f01b2fSopenharmony_ci 537c5f01b2fSopenharmony_ci // check roundtrip 538c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 539c5f01b2fSopenharmony_ci } 540c5f01b2fSopenharmony_ci 541c5f01b2fSopenharmony_ci SECTION("add to end of the array") 542c5f01b2fSopenharmony_ci { 543c5f01b2fSopenharmony_ci // The specified index MUST NOT be greater than the number of 544c5f01b2fSopenharmony_ci // elements in the array. The example below uses and index of 545c5f01b2fSopenharmony_ci // exactly the number of elements in the array which is legal. 546c5f01b2fSopenharmony_ci 547c5f01b2fSopenharmony_ci // An example target JSON document: 548c5f01b2fSopenharmony_ci json doc = {0, 1, 2}; 549c5f01b2fSopenharmony_ci 550c5f01b2fSopenharmony_ci // A JSON Patch document: 551c5f01b2fSopenharmony_ci json patch = R"( 552c5f01b2fSopenharmony_ci [ 553c5f01b2fSopenharmony_ci { "op": "add", "path": "/3", "value": 3 } 554c5f01b2fSopenharmony_ci ] 555c5f01b2fSopenharmony_ci )"_json; 556c5f01b2fSopenharmony_ci 557c5f01b2fSopenharmony_ci // The resulting JSON document: 558c5f01b2fSopenharmony_ci json expected = {0, 1, 2, 3}; 559c5f01b2fSopenharmony_ci 560c5f01b2fSopenharmony_ci // check if patched value is as expected 561c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 562c5f01b2fSopenharmony_ci 563c5f01b2fSopenharmony_ci // check roundtrip 564c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 565c5f01b2fSopenharmony_ci } 566c5f01b2fSopenharmony_ci } 567c5f01b2fSopenharmony_ci 568c5f01b2fSopenharmony_ci SECTION("copy") 569c5f01b2fSopenharmony_ci { 570c5f01b2fSopenharmony_ci // An example target JSON document: 571c5f01b2fSopenharmony_ci json doc = R"( 572c5f01b2fSopenharmony_ci { 573c5f01b2fSopenharmony_ci "foo": { 574c5f01b2fSopenharmony_ci "bar": "baz", 575c5f01b2fSopenharmony_ci "waldo": "fred" 576c5f01b2fSopenharmony_ci }, 577c5f01b2fSopenharmony_ci "qux": { 578c5f01b2fSopenharmony_ci "corge": "grault" 579c5f01b2fSopenharmony_ci } 580c5f01b2fSopenharmony_ci } 581c5f01b2fSopenharmony_ci )"_json; 582c5f01b2fSopenharmony_ci 583c5f01b2fSopenharmony_ci // A JSON Patch document: 584c5f01b2fSopenharmony_ci json patch = R"( 585c5f01b2fSopenharmony_ci [ 586c5f01b2fSopenharmony_ci { "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" } 587c5f01b2fSopenharmony_ci ] 588c5f01b2fSopenharmony_ci )"_json; 589c5f01b2fSopenharmony_ci 590c5f01b2fSopenharmony_ci // The resulting JSON document: 591c5f01b2fSopenharmony_ci json expected = R"( 592c5f01b2fSopenharmony_ci { 593c5f01b2fSopenharmony_ci "foo": { 594c5f01b2fSopenharmony_ci "bar": "baz", 595c5f01b2fSopenharmony_ci "waldo": "fred" 596c5f01b2fSopenharmony_ci }, 597c5f01b2fSopenharmony_ci "qux": { 598c5f01b2fSopenharmony_ci "corge": "grault", 599c5f01b2fSopenharmony_ci "thud": "fred" 600c5f01b2fSopenharmony_ci } 601c5f01b2fSopenharmony_ci } 602c5f01b2fSopenharmony_ci )"_json; 603c5f01b2fSopenharmony_ci 604c5f01b2fSopenharmony_ci // check if patched value is as expected 605c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 606c5f01b2fSopenharmony_ci 607c5f01b2fSopenharmony_ci // check roundtrip 608c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, expected)) == expected); 609c5f01b2fSopenharmony_ci } 610c5f01b2fSopenharmony_ci 611c5f01b2fSopenharmony_ci SECTION("replace") 612c5f01b2fSopenharmony_ci { 613c5f01b2fSopenharmony_ci json j = "string"; 614c5f01b2fSopenharmony_ci json patch = {{{"op", "replace"}, {"path", ""}, {"value", 1}}}; 615c5f01b2fSopenharmony_ci CHECK(j.patch(patch) == json(1)); 616c5f01b2fSopenharmony_ci } 617c5f01b2fSopenharmony_ci 618c5f01b2fSopenharmony_ci SECTION("documentation GIF") 619c5f01b2fSopenharmony_ci { 620c5f01b2fSopenharmony_ci { 621c5f01b2fSopenharmony_ci // a JSON patch 622c5f01b2fSopenharmony_ci json p1 = R"( 623c5f01b2fSopenharmony_ci [{"op": "add", "path": "/GB", "value": "London"}] 624c5f01b2fSopenharmony_ci )"_json; 625c5f01b2fSopenharmony_ci 626c5f01b2fSopenharmony_ci // a JSON value 627c5f01b2fSopenharmony_ci json source = R"( 628c5f01b2fSopenharmony_ci {"D": "Berlin", "F": "Paris"} 629c5f01b2fSopenharmony_ci )"_json; 630c5f01b2fSopenharmony_ci 631c5f01b2fSopenharmony_ci // apply the patch 632c5f01b2fSopenharmony_ci json target = source.patch(p1); 633c5f01b2fSopenharmony_ci // target = { "D": "Berlin", "F": "Paris", "GB": "London" } 634c5f01b2fSopenharmony_ci CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json); 635c5f01b2fSopenharmony_ci 636c5f01b2fSopenharmony_ci // create a diff from two JSONs 637c5f01b2fSopenharmony_ci json p2 = json::diff(target, source); // NOLINT(readability-suspicious-call-argument) 638c5f01b2fSopenharmony_ci // p2 = [{"op": "delete", "path": "/GB"}] 639c5f01b2fSopenharmony_ci CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json); 640c5f01b2fSopenharmony_ci } 641c5f01b2fSopenharmony_ci { 642c5f01b2fSopenharmony_ci // a JSON value 643c5f01b2fSopenharmony_ci json j = {"good", "bad", "ugly"}; 644c5f01b2fSopenharmony_ci 645c5f01b2fSopenharmony_ci // a JSON pointer 646c5f01b2fSopenharmony_ci auto ptr = json::json_pointer("/2"); 647c5f01b2fSopenharmony_ci 648c5f01b2fSopenharmony_ci // use to access elements 649c5f01b2fSopenharmony_ci j[ptr] = {{"it", "cattivo"}}; 650c5f01b2fSopenharmony_ci CHECK(j == R"(["good","bad",{"it":"cattivo"}])"_json); 651c5f01b2fSopenharmony_ci 652c5f01b2fSopenharmony_ci // use user-defined string literal 653c5f01b2fSopenharmony_ci j["/2/en"_json_pointer] = "ugly"; 654c5f01b2fSopenharmony_ci CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json); 655c5f01b2fSopenharmony_ci 656c5f01b2fSopenharmony_ci json flat = j.flatten(); 657c5f01b2fSopenharmony_ci CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json); 658c5f01b2fSopenharmony_ci } 659c5f01b2fSopenharmony_ci } 660c5f01b2fSopenharmony_ci } 661c5f01b2fSopenharmony_ci 662c5f01b2fSopenharmony_ci SECTION("errors") 663c5f01b2fSopenharmony_ci { 664c5f01b2fSopenharmony_ci SECTION("unknown operation") 665c5f01b2fSopenharmony_ci { 666c5f01b2fSopenharmony_ci SECTION("not an array") 667c5f01b2fSopenharmony_ci { 668c5f01b2fSopenharmony_ci json j; 669c5f01b2fSopenharmony_ci json patch = {{"op", "add"}, {"path", ""}, {"value", 1}}; 670c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects", json::parse_error&); 671c5f01b2fSopenharmony_ci } 672c5f01b2fSopenharmony_ci 673c5f01b2fSopenharmony_ci SECTION("not an array of objects") 674c5f01b2fSopenharmony_ci { 675c5f01b2fSopenharmony_ci json j; 676c5f01b2fSopenharmony_ci json patch = {"op", "add", "path", "", "value", 1}; 677c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 678c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.104] parse error: (/0) JSON patch must be an array of objects", json::parse_error&); 679c5f01b2fSopenharmony_ci#else 680c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects", json::parse_error&); 681c5f01b2fSopenharmony_ci#endif 682c5f01b2fSopenharmony_ci } 683c5f01b2fSopenharmony_ci 684c5f01b2fSopenharmony_ci SECTION("missing 'op'") 685c5f01b2fSopenharmony_ci { 686c5f01b2fSopenharmony_ci json j; 687c5f01b2fSopenharmony_ci json patch = {{{"foo", "bar"}}}; 688c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 689c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have member 'op'", json::parse_error&); 690c5f01b2fSopenharmony_ci#else 691c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have member 'op'", json::parse_error&); 692c5f01b2fSopenharmony_ci#endif 693c5f01b2fSopenharmony_ci } 694c5f01b2fSopenharmony_ci 695c5f01b2fSopenharmony_ci SECTION("non-string 'op'") 696c5f01b2fSopenharmony_ci { 697c5f01b2fSopenharmony_ci json j; 698c5f01b2fSopenharmony_ci json patch = {{{"op", 1}}}; 699c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 700c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have string member 'op'", json::parse_error&); 701c5f01b2fSopenharmony_ci#else 702c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have string member 'op'", json::parse_error&); 703c5f01b2fSopenharmony_ci#endif 704c5f01b2fSopenharmony_ci } 705c5f01b2fSopenharmony_ci 706c5f01b2fSopenharmony_ci SECTION("invalid operation") 707c5f01b2fSopenharmony_ci { 708c5f01b2fSopenharmony_ci json j; 709c5f01b2fSopenharmony_ci json patch = {{{"op", "foo"}, {"path", ""}}}; 710c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 711c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation value 'foo' is invalid", json::parse_error&); 712c5f01b2fSopenharmony_ci#else 713c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation value 'foo' is invalid", json::parse_error&); 714c5f01b2fSopenharmony_ci#endif 715c5f01b2fSopenharmony_ci } 716c5f01b2fSopenharmony_ci } 717c5f01b2fSopenharmony_ci 718c5f01b2fSopenharmony_ci SECTION("add") 719c5f01b2fSopenharmony_ci { 720c5f01b2fSopenharmony_ci SECTION("missing 'path'") 721c5f01b2fSopenharmony_ci { 722c5f01b2fSopenharmony_ci json j; 723c5f01b2fSopenharmony_ci json patch = {{{"op", "add"}}}; 724c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 725c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'path'", json::parse_error&); 726c5f01b2fSopenharmony_ci#else 727c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'", json::parse_error&); 728c5f01b2fSopenharmony_ci#endif 729c5f01b2fSopenharmony_ci } 730c5f01b2fSopenharmony_ci 731c5f01b2fSopenharmony_ci SECTION("non-string 'path'") 732c5f01b2fSopenharmony_ci { 733c5f01b2fSopenharmony_ci json j; 734c5f01b2fSopenharmony_ci json patch = {{{"op", "add"}, {"path", 1}}}; 735c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 736c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have string member 'path'", json::parse_error&); 737c5f01b2fSopenharmony_ci#else 738c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'", json::parse_error&); 739c5f01b2fSopenharmony_ci#endif 740c5f01b2fSopenharmony_ci } 741c5f01b2fSopenharmony_ci 742c5f01b2fSopenharmony_ci SECTION("missing 'value'") 743c5f01b2fSopenharmony_ci { 744c5f01b2fSopenharmony_ci json j; 745c5f01b2fSopenharmony_ci json patch = {{{"op", "add"}, {"path", ""}}}; 746c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 747c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'value'", json::parse_error&); 748c5f01b2fSopenharmony_ci#else 749c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'", json::parse_error&); 750c5f01b2fSopenharmony_ci#endif 751c5f01b2fSopenharmony_ci } 752c5f01b2fSopenharmony_ci 753c5f01b2fSopenharmony_ci SECTION("invalid array index") 754c5f01b2fSopenharmony_ci { 755c5f01b2fSopenharmony_ci json j = {1, 2}; 756c5f01b2fSopenharmony_ci json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}}; 757c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 4 is out of range", json::out_of_range&); 758c5f01b2fSopenharmony_ci } 759c5f01b2fSopenharmony_ci } 760c5f01b2fSopenharmony_ci 761c5f01b2fSopenharmony_ci SECTION("remove") 762c5f01b2fSopenharmony_ci { 763c5f01b2fSopenharmony_ci SECTION("missing 'path'") 764c5f01b2fSopenharmony_ci { 765c5f01b2fSopenharmony_ci json j; 766c5f01b2fSopenharmony_ci json patch = {{{"op", "remove"}}}; 767c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 768c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have member 'path'", json::parse_error&); 769c5f01b2fSopenharmony_ci#else 770c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'", json::parse_error&); 771c5f01b2fSopenharmony_ci#endif 772c5f01b2fSopenharmony_ci } 773c5f01b2fSopenharmony_ci 774c5f01b2fSopenharmony_ci SECTION("non-string 'path'") 775c5f01b2fSopenharmony_ci { 776c5f01b2fSopenharmony_ci json j; 777c5f01b2fSopenharmony_ci json patch = {{{"op", "remove"}, {"path", 1}}}; 778c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 779c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have string member 'path'", json::parse_error&); 780c5f01b2fSopenharmony_ci#else 781c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'", json::parse_error&); 782c5f01b2fSopenharmony_ci#endif 783c5f01b2fSopenharmony_ci } 784c5f01b2fSopenharmony_ci 785c5f01b2fSopenharmony_ci SECTION("nonexisting target location (array)") 786c5f01b2fSopenharmony_ci { 787c5f01b2fSopenharmony_ci json j = {1, 2, 3}; 788c5f01b2fSopenharmony_ci json patch = {{{"op", "remove"}, {"path", "/17"}}}; 789c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 17 is out of range", json::out_of_range&); 790c5f01b2fSopenharmony_ci } 791c5f01b2fSopenharmony_ci 792c5f01b2fSopenharmony_ci SECTION("nonexisting target location (object)") 793c5f01b2fSopenharmony_ci { 794c5f01b2fSopenharmony_ci json j = {{"foo", 1}, {"bar", 2}}; 795c5f01b2fSopenharmony_ci json patch = {{{"op", "remove"}, {"path", "/baz"}}}; 796c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); 797c5f01b2fSopenharmony_ci } 798c5f01b2fSopenharmony_ci 799c5f01b2fSopenharmony_ci SECTION("root element as target location") 800c5f01b2fSopenharmony_ci { 801c5f01b2fSopenharmony_ci json j = "string"; 802c5f01b2fSopenharmony_ci json patch = {{{"op", "remove"}, {"path", ""}}}; 803c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.405] JSON pointer has no parent", json::out_of_range&); 804c5f01b2fSopenharmony_ci } 805c5f01b2fSopenharmony_ci } 806c5f01b2fSopenharmony_ci 807c5f01b2fSopenharmony_ci SECTION("replace") 808c5f01b2fSopenharmony_ci { 809c5f01b2fSopenharmony_ci SECTION("missing 'path'") 810c5f01b2fSopenharmony_ci { 811c5f01b2fSopenharmony_ci json j; 812c5f01b2fSopenharmony_ci json patch = {{{"op", "replace"}}}; 813c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 814c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'path'", json::parse_error&); 815c5f01b2fSopenharmony_ci#else 816c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'", json::parse_error&); 817c5f01b2fSopenharmony_ci#endif 818c5f01b2fSopenharmony_ci } 819c5f01b2fSopenharmony_ci 820c5f01b2fSopenharmony_ci SECTION("non-string 'path'") 821c5f01b2fSopenharmony_ci { 822c5f01b2fSopenharmony_ci json j; 823c5f01b2fSopenharmony_ci json patch = {{{"op", "replace"}, {"path", 1}}}; 824c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 825c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have string member 'path'", json::parse_error&); 826c5f01b2fSopenharmony_ci#else 827c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'", json::parse_error&); 828c5f01b2fSopenharmony_ci#endif 829c5f01b2fSopenharmony_ci } 830c5f01b2fSopenharmony_ci 831c5f01b2fSopenharmony_ci SECTION("missing 'value'") 832c5f01b2fSopenharmony_ci { 833c5f01b2fSopenharmony_ci json j; 834c5f01b2fSopenharmony_ci json patch = {{{"op", "replace"}, {"path", ""}}}; 835c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 836c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'value'", json::parse_error&); 837c5f01b2fSopenharmony_ci#else 838c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'", json::parse_error&); 839c5f01b2fSopenharmony_ci#endif 840c5f01b2fSopenharmony_ci } 841c5f01b2fSopenharmony_ci 842c5f01b2fSopenharmony_ci SECTION("nonexisting target location (array)") 843c5f01b2fSopenharmony_ci { 844c5f01b2fSopenharmony_ci json j = {1, 2, 3}; 845c5f01b2fSopenharmony_ci json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}}; 846c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 17 is out of range", json::out_of_range&); 847c5f01b2fSopenharmony_ci } 848c5f01b2fSopenharmony_ci 849c5f01b2fSopenharmony_ci SECTION("nonexisting target location (object)") 850c5f01b2fSopenharmony_ci { 851c5f01b2fSopenharmony_ci json j = {{"foo", 1}, {"bar", 2}}; 852c5f01b2fSopenharmony_ci json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}}; 853c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); 854c5f01b2fSopenharmony_ci } 855c5f01b2fSopenharmony_ci } 856c5f01b2fSopenharmony_ci 857c5f01b2fSopenharmony_ci SECTION("move") 858c5f01b2fSopenharmony_ci { 859c5f01b2fSopenharmony_ci SECTION("missing 'path'") 860c5f01b2fSopenharmony_ci { 861c5f01b2fSopenharmony_ci json j; 862c5f01b2fSopenharmony_ci json patch = {{{"op", "move"}}}; 863c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 864c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'path'", json::parse_error&); 865c5f01b2fSopenharmony_ci#else 866c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'", json::parse_error&); 867c5f01b2fSopenharmony_ci#endif 868c5f01b2fSopenharmony_ci } 869c5f01b2fSopenharmony_ci 870c5f01b2fSopenharmony_ci SECTION("non-string 'path'") 871c5f01b2fSopenharmony_ci { 872c5f01b2fSopenharmony_ci json j; 873c5f01b2fSopenharmony_ci json patch = {{{"op", "move"}, {"path", 1}}}; 874c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 875c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'path'", json::parse_error&); 876c5f01b2fSopenharmony_ci#else 877c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'", json::parse_error&); 878c5f01b2fSopenharmony_ci#endif 879c5f01b2fSopenharmony_ci } 880c5f01b2fSopenharmony_ci 881c5f01b2fSopenharmony_ci SECTION("missing 'from'") 882c5f01b2fSopenharmony_ci { 883c5f01b2fSopenharmony_ci json j; 884c5f01b2fSopenharmony_ci json patch = {{{"op", "move"}, {"path", ""}}}; 885c5f01b2fSopenharmony_ci CHECK_THROWS_AS(j.patch(patch), json::parse_error&); 886c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 887c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'from'", json::parse_error&); 888c5f01b2fSopenharmony_ci#else 889c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'", json::parse_error&); 890c5f01b2fSopenharmony_ci#endif 891c5f01b2fSopenharmony_ci } 892c5f01b2fSopenharmony_ci 893c5f01b2fSopenharmony_ci SECTION("non-string 'from'") 894c5f01b2fSopenharmony_ci { 895c5f01b2fSopenharmony_ci json j; 896c5f01b2fSopenharmony_ci json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; 897c5f01b2fSopenharmony_ci CHECK_THROWS_AS(j.patch(patch), json::parse_error&); 898c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 899c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'from'", json::parse_error&); 900c5f01b2fSopenharmony_ci#else 901c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'", json::parse_error&); 902c5f01b2fSopenharmony_ci#endif 903c5f01b2fSopenharmony_ci } 904c5f01b2fSopenharmony_ci 905c5f01b2fSopenharmony_ci SECTION("nonexisting from location (array)") 906c5f01b2fSopenharmony_ci { 907c5f01b2fSopenharmony_ci json j = {1, 2, 3}; 908c5f01b2fSopenharmony_ci json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}}; 909c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&); 910c5f01b2fSopenharmony_ci } 911c5f01b2fSopenharmony_ci 912c5f01b2fSopenharmony_ci SECTION("nonexisting from location (object)") 913c5f01b2fSopenharmony_ci { 914c5f01b2fSopenharmony_ci json j = {{"foo", 1}, {"bar", 2}}; 915c5f01b2fSopenharmony_ci json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}}; 916c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); 917c5f01b2fSopenharmony_ci } 918c5f01b2fSopenharmony_ci } 919c5f01b2fSopenharmony_ci 920c5f01b2fSopenharmony_ci SECTION("copy") 921c5f01b2fSopenharmony_ci { 922c5f01b2fSopenharmony_ci SECTION("missing 'path'") 923c5f01b2fSopenharmony_ci { 924c5f01b2fSopenharmony_ci json j; 925c5f01b2fSopenharmony_ci json patch = {{{"op", "copy"}}}; 926c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 927c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'path'", json::parse_error&); 928c5f01b2fSopenharmony_ci#else 929c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'", json::parse_error&); 930c5f01b2fSopenharmony_ci#endif 931c5f01b2fSopenharmony_ci } 932c5f01b2fSopenharmony_ci 933c5f01b2fSopenharmony_ci SECTION("non-string 'path'") 934c5f01b2fSopenharmony_ci { 935c5f01b2fSopenharmony_ci json j; 936c5f01b2fSopenharmony_ci json patch = {{{"op", "copy"}, {"path", 1}}}; 937c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 938c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'path'", json::parse_error&); 939c5f01b2fSopenharmony_ci#else 940c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'", json::parse_error&); 941c5f01b2fSopenharmony_ci#endif 942c5f01b2fSopenharmony_ci } 943c5f01b2fSopenharmony_ci 944c5f01b2fSopenharmony_ci SECTION("missing 'from'") 945c5f01b2fSopenharmony_ci { 946c5f01b2fSopenharmony_ci json j; 947c5f01b2fSopenharmony_ci json patch = {{{"op", "copy"}, {"path", ""}}}; 948c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 949c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'from'", json::parse_error&); 950c5f01b2fSopenharmony_ci#else 951c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'", json::parse_error&); 952c5f01b2fSopenharmony_ci#endif 953c5f01b2fSopenharmony_ci } 954c5f01b2fSopenharmony_ci 955c5f01b2fSopenharmony_ci SECTION("non-string 'from'") 956c5f01b2fSopenharmony_ci { 957c5f01b2fSopenharmony_ci json j; 958c5f01b2fSopenharmony_ci json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; 959c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 960c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'from'", json::parse_error&); 961c5f01b2fSopenharmony_ci#else 962c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'", json::parse_error&); 963c5f01b2fSopenharmony_ci#endif 964c5f01b2fSopenharmony_ci } 965c5f01b2fSopenharmony_ci 966c5f01b2fSopenharmony_ci SECTION("nonexisting from location (array)") 967c5f01b2fSopenharmony_ci { 968c5f01b2fSopenharmony_ci json j = {1, 2, 3}; 969c5f01b2fSopenharmony_ci json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}}; 970c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&); 971c5f01b2fSopenharmony_ci } 972c5f01b2fSopenharmony_ci 973c5f01b2fSopenharmony_ci SECTION("nonexisting from location (object)") 974c5f01b2fSopenharmony_ci { 975c5f01b2fSopenharmony_ci json j = {{"foo", 1}, {"bar", 2}}; 976c5f01b2fSopenharmony_ci json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}}; 977c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); 978c5f01b2fSopenharmony_ci } 979c5f01b2fSopenharmony_ci } 980c5f01b2fSopenharmony_ci 981c5f01b2fSopenharmony_ci SECTION("test") 982c5f01b2fSopenharmony_ci { 983c5f01b2fSopenharmony_ci SECTION("missing 'path'") 984c5f01b2fSopenharmony_ci { 985c5f01b2fSopenharmony_ci json j; 986c5f01b2fSopenharmony_ci json patch = {{{"op", "test"}}}; 987c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 988c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'path'", json::parse_error&); 989c5f01b2fSopenharmony_ci#else 990c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'", json::parse_error&); 991c5f01b2fSopenharmony_ci#endif 992c5f01b2fSopenharmony_ci } 993c5f01b2fSopenharmony_ci 994c5f01b2fSopenharmony_ci SECTION("non-string 'path'") 995c5f01b2fSopenharmony_ci { 996c5f01b2fSopenharmony_ci json j; 997c5f01b2fSopenharmony_ci json patch = {{{"op", "test"}, {"path", 1}}}; 998c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 999c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have string member 'path'", json::parse_error&); 1000c5f01b2fSopenharmony_ci#else 1001c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'", json::parse_error&); 1002c5f01b2fSopenharmony_ci#endif 1003c5f01b2fSopenharmony_ci } 1004c5f01b2fSopenharmony_ci 1005c5f01b2fSopenharmony_ci SECTION("missing 'value'") 1006c5f01b2fSopenharmony_ci { 1007c5f01b2fSopenharmony_ci json j; 1008c5f01b2fSopenharmony_ci json patch = {{{"op", "test"}, {"path", ""}}}; 1009c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 1010c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'value'", json::parse_error&); 1011c5f01b2fSopenharmony_ci#else 1012c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'", json::parse_error&); 1013c5f01b2fSopenharmony_ci#endif 1014c5f01b2fSopenharmony_ci } 1015c5f01b2fSopenharmony_ci } 1016c5f01b2fSopenharmony_ci } 1017c5f01b2fSopenharmony_ci 1018c5f01b2fSopenharmony_ci SECTION("Examples from jsonpatch.com") 1019c5f01b2fSopenharmony_ci { 1020c5f01b2fSopenharmony_ci SECTION("Simple Example") 1021c5f01b2fSopenharmony_ci { 1022c5f01b2fSopenharmony_ci // The original document 1023c5f01b2fSopenharmony_ci json doc = R"( 1024c5f01b2fSopenharmony_ci { 1025c5f01b2fSopenharmony_ci "baz": "qux", 1026c5f01b2fSopenharmony_ci "foo": "bar" 1027c5f01b2fSopenharmony_ci } 1028c5f01b2fSopenharmony_ci )"_json; 1029c5f01b2fSopenharmony_ci 1030c5f01b2fSopenharmony_ci // The patch 1031c5f01b2fSopenharmony_ci json patch = R"( 1032c5f01b2fSopenharmony_ci [ 1033c5f01b2fSopenharmony_ci { "op": "replace", "path": "/baz", "value": "boo" }, 1034c5f01b2fSopenharmony_ci { "op": "add", "path": "/hello", "value": ["world"] }, 1035c5f01b2fSopenharmony_ci { "op": "remove", "path": "/foo"} 1036c5f01b2fSopenharmony_ci ] 1037c5f01b2fSopenharmony_ci )"_json; 1038c5f01b2fSopenharmony_ci 1039c5f01b2fSopenharmony_ci // The result 1040c5f01b2fSopenharmony_ci json result = R"( 1041c5f01b2fSopenharmony_ci { 1042c5f01b2fSopenharmony_ci "baz": "boo", 1043c5f01b2fSopenharmony_ci "hello": ["world"] 1044c5f01b2fSopenharmony_ci } 1045c5f01b2fSopenharmony_ci )"_json; 1046c5f01b2fSopenharmony_ci 1047c5f01b2fSopenharmony_ci // check if patched value is as expected 1048c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == result); 1049c5f01b2fSopenharmony_ci 1050c5f01b2fSopenharmony_ci // check roundtrip 1051c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, result)) == result); 1052c5f01b2fSopenharmony_ci } 1053c5f01b2fSopenharmony_ci 1054c5f01b2fSopenharmony_ci SECTION("Operations") 1055c5f01b2fSopenharmony_ci { 1056c5f01b2fSopenharmony_ci // The original document 1057c5f01b2fSopenharmony_ci json doc = R"( 1058c5f01b2fSopenharmony_ci { 1059c5f01b2fSopenharmony_ci "biscuits": [ 1060c5f01b2fSopenharmony_ci {"name":"Digestive"}, 1061c5f01b2fSopenharmony_ci {"name": "Choco Liebniz"} 1062c5f01b2fSopenharmony_ci ] 1063c5f01b2fSopenharmony_ci } 1064c5f01b2fSopenharmony_ci )"_json; 1065c5f01b2fSopenharmony_ci 1066c5f01b2fSopenharmony_ci SECTION("add") 1067c5f01b2fSopenharmony_ci { 1068c5f01b2fSopenharmony_ci // The patch 1069c5f01b2fSopenharmony_ci json patch = R"( 1070c5f01b2fSopenharmony_ci [ 1071c5f01b2fSopenharmony_ci {"op": "add", "path": "/biscuits/1", "value": {"name": "Ginger Nut"}} 1072c5f01b2fSopenharmony_ci ] 1073c5f01b2fSopenharmony_ci )"_json; 1074c5f01b2fSopenharmony_ci 1075c5f01b2fSopenharmony_ci // The result 1076c5f01b2fSopenharmony_ci json result = R"( 1077c5f01b2fSopenharmony_ci { 1078c5f01b2fSopenharmony_ci "biscuits": [ 1079c5f01b2fSopenharmony_ci {"name": "Digestive"}, 1080c5f01b2fSopenharmony_ci {"name": "Ginger Nut"}, 1081c5f01b2fSopenharmony_ci {"name": "Choco Liebniz"} 1082c5f01b2fSopenharmony_ci ] 1083c5f01b2fSopenharmony_ci } 1084c5f01b2fSopenharmony_ci )"_json; 1085c5f01b2fSopenharmony_ci 1086c5f01b2fSopenharmony_ci // check if patched value is as expected 1087c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == result); 1088c5f01b2fSopenharmony_ci 1089c5f01b2fSopenharmony_ci // check roundtrip 1090c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, result)) == result); 1091c5f01b2fSopenharmony_ci } 1092c5f01b2fSopenharmony_ci 1093c5f01b2fSopenharmony_ci SECTION("remove") 1094c5f01b2fSopenharmony_ci { 1095c5f01b2fSopenharmony_ci // The patch 1096c5f01b2fSopenharmony_ci json patch = R"( 1097c5f01b2fSopenharmony_ci [ 1098c5f01b2fSopenharmony_ci {"op": "remove", "path": "/biscuits"} 1099c5f01b2fSopenharmony_ci ] 1100c5f01b2fSopenharmony_ci )"_json; 1101c5f01b2fSopenharmony_ci 1102c5f01b2fSopenharmony_ci // The result 1103c5f01b2fSopenharmony_ci json result = R"( 1104c5f01b2fSopenharmony_ci {} 1105c5f01b2fSopenharmony_ci )"_json; 1106c5f01b2fSopenharmony_ci 1107c5f01b2fSopenharmony_ci // check if patched value is as expected 1108c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == result); 1109c5f01b2fSopenharmony_ci 1110c5f01b2fSopenharmony_ci // check roundtrip 1111c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, result)) == result); 1112c5f01b2fSopenharmony_ci } 1113c5f01b2fSopenharmony_ci 1114c5f01b2fSopenharmony_ci SECTION("replace") 1115c5f01b2fSopenharmony_ci { 1116c5f01b2fSopenharmony_ci // The patch 1117c5f01b2fSopenharmony_ci json patch = R"( 1118c5f01b2fSopenharmony_ci [ 1119c5f01b2fSopenharmony_ci {"op": "replace", "path": "/biscuits/0/name", "value": "Chocolate Digestive"} 1120c5f01b2fSopenharmony_ci ] 1121c5f01b2fSopenharmony_ci )"_json; 1122c5f01b2fSopenharmony_ci 1123c5f01b2fSopenharmony_ci // The result 1124c5f01b2fSopenharmony_ci json result = R"( 1125c5f01b2fSopenharmony_ci { 1126c5f01b2fSopenharmony_ci "biscuits": [ 1127c5f01b2fSopenharmony_ci {"name": "Chocolate Digestive"}, 1128c5f01b2fSopenharmony_ci {"name": "Choco Liebniz"} 1129c5f01b2fSopenharmony_ci ] 1130c5f01b2fSopenharmony_ci } 1131c5f01b2fSopenharmony_ci )"_json; 1132c5f01b2fSopenharmony_ci 1133c5f01b2fSopenharmony_ci // check if patched value is as expected 1134c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == result); 1135c5f01b2fSopenharmony_ci 1136c5f01b2fSopenharmony_ci // check roundtrip 1137c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, result)) == result); 1138c5f01b2fSopenharmony_ci } 1139c5f01b2fSopenharmony_ci 1140c5f01b2fSopenharmony_ci SECTION("copy") 1141c5f01b2fSopenharmony_ci { 1142c5f01b2fSopenharmony_ci // The patch 1143c5f01b2fSopenharmony_ci json patch = R"( 1144c5f01b2fSopenharmony_ci [ 1145c5f01b2fSopenharmony_ci {"op": "copy", "from": "/biscuits/0", "path": "/best_biscuit"} 1146c5f01b2fSopenharmony_ci ] 1147c5f01b2fSopenharmony_ci )"_json; 1148c5f01b2fSopenharmony_ci 1149c5f01b2fSopenharmony_ci // The result 1150c5f01b2fSopenharmony_ci json result = R"( 1151c5f01b2fSopenharmony_ci { 1152c5f01b2fSopenharmony_ci "biscuits": [ 1153c5f01b2fSopenharmony_ci {"name": "Digestive"}, 1154c5f01b2fSopenharmony_ci {"name": "Choco Liebniz"} 1155c5f01b2fSopenharmony_ci ], 1156c5f01b2fSopenharmony_ci "best_biscuit": { 1157c5f01b2fSopenharmony_ci "name": "Digestive" 1158c5f01b2fSopenharmony_ci } 1159c5f01b2fSopenharmony_ci } 1160c5f01b2fSopenharmony_ci )"_json; 1161c5f01b2fSopenharmony_ci 1162c5f01b2fSopenharmony_ci // check if patched value is as expected 1163c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == result); 1164c5f01b2fSopenharmony_ci 1165c5f01b2fSopenharmony_ci // check roundtrip 1166c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, result)) == result); 1167c5f01b2fSopenharmony_ci } 1168c5f01b2fSopenharmony_ci 1169c5f01b2fSopenharmony_ci SECTION("move") 1170c5f01b2fSopenharmony_ci { 1171c5f01b2fSopenharmony_ci // The patch 1172c5f01b2fSopenharmony_ci json patch = R"( 1173c5f01b2fSopenharmony_ci [ 1174c5f01b2fSopenharmony_ci {"op": "move", "from": "/biscuits", "path": "/cookies"} 1175c5f01b2fSopenharmony_ci ] 1176c5f01b2fSopenharmony_ci )"_json; 1177c5f01b2fSopenharmony_ci 1178c5f01b2fSopenharmony_ci // The result 1179c5f01b2fSopenharmony_ci json result = R"( 1180c5f01b2fSopenharmony_ci { 1181c5f01b2fSopenharmony_ci "cookies": [ 1182c5f01b2fSopenharmony_ci {"name": "Digestive"}, 1183c5f01b2fSopenharmony_ci {"name": "Choco Liebniz"} 1184c5f01b2fSopenharmony_ci ] 1185c5f01b2fSopenharmony_ci } 1186c5f01b2fSopenharmony_ci )"_json; 1187c5f01b2fSopenharmony_ci 1188c5f01b2fSopenharmony_ci // check if patched value is as expected 1189c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == result); 1190c5f01b2fSopenharmony_ci 1191c5f01b2fSopenharmony_ci // check roundtrip 1192c5f01b2fSopenharmony_ci CHECK(doc.patch(json::diff(doc, result)) == result); 1193c5f01b2fSopenharmony_ci } 1194c5f01b2fSopenharmony_ci 1195c5f01b2fSopenharmony_ci SECTION("test") 1196c5f01b2fSopenharmony_ci { 1197c5f01b2fSopenharmony_ci // The patch 1198c5f01b2fSopenharmony_ci json patch = R"( 1199c5f01b2fSopenharmony_ci [ 1200c5f01b2fSopenharmony_ci {"op": "test", "path": "/best_biscuit/name", "value": "Choco Liebniz"} 1201c5f01b2fSopenharmony_ci ] 1202c5f01b2fSopenharmony_ci )"_json; 1203c5f01b2fSopenharmony_ci 1204c5f01b2fSopenharmony_ci // the test will fail 1205c5f01b2fSopenharmony_ci CHECK_THROWS_AS(doc.patch(patch), json::other_error&); 1206c5f01b2fSopenharmony_ci#if JSON_DIAGNOSTICS 1207c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); 1208c5f01b2fSopenharmony_ci#else 1209c5f01b2fSopenharmony_ci CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); 1210c5f01b2fSopenharmony_ci#endif 1211c5f01b2fSopenharmony_ci } 1212c5f01b2fSopenharmony_ci } 1213c5f01b2fSopenharmony_ci } 1214c5f01b2fSopenharmony_ci 1215c5f01b2fSopenharmony_ci SECTION("Examples from bruth.github.io/jsonpatch-js") 1216c5f01b2fSopenharmony_ci { 1217c5f01b2fSopenharmony_ci SECTION("add") 1218c5f01b2fSopenharmony_ci { 1219c5f01b2fSopenharmony_ci CHECK(R"( {} )"_json.patch( 1220c5f01b2fSopenharmony_ci R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json 1221c5f01b2fSopenharmony_ci ) == R"( {"foo": "bar"} )"_json); 1222c5f01b2fSopenharmony_ci 1223c5f01b2fSopenharmony_ci CHECK(R"( {"foo": [1, 3]} )"_json.patch( 1224c5f01b2fSopenharmony_ci R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json 1225c5f01b2fSopenharmony_ci ) == R"( {"foo": "bar"} )"_json); 1226c5f01b2fSopenharmony_ci 1227c5f01b2fSopenharmony_ci CHECK(R"( {"foo": [{}]} )"_json.patch( 1228c5f01b2fSopenharmony_ci R"( [{"op": "add", "path": "/foo/0/bar", "value": "baz"}] )"_json 1229c5f01b2fSopenharmony_ci ) == R"( {"foo": [{"bar": "baz"}]} )"_json); 1230c5f01b2fSopenharmony_ci } 1231c5f01b2fSopenharmony_ci 1232c5f01b2fSopenharmony_ci SECTION("remove") 1233c5f01b2fSopenharmony_ci { 1234c5f01b2fSopenharmony_ci CHECK(R"( {"foo": "bar"} )"_json.patch( 1235c5f01b2fSopenharmony_ci R"( [{"op": "remove", "path": "/foo"}] )"_json 1236c5f01b2fSopenharmony_ci ) == R"( {} )"_json); 1237c5f01b2fSopenharmony_ci 1238c5f01b2fSopenharmony_ci CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( 1239c5f01b2fSopenharmony_ci R"( [{"op": "remove", "path": "/foo/1"}] )"_json 1240c5f01b2fSopenharmony_ci ) == R"( {"foo": [1, 3]} )"_json); 1241c5f01b2fSopenharmony_ci 1242c5f01b2fSopenharmony_ci CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( 1243c5f01b2fSopenharmony_ci R"( [{"op": "remove", "path": "/foo/0/bar"}] )"_json 1244c5f01b2fSopenharmony_ci ) == R"( {"foo": [{}]} )"_json); 1245c5f01b2fSopenharmony_ci } 1246c5f01b2fSopenharmony_ci 1247c5f01b2fSopenharmony_ci SECTION("replace") 1248c5f01b2fSopenharmony_ci { 1249c5f01b2fSopenharmony_ci CHECK(R"( {"foo": "bar"} )"_json.patch( 1250c5f01b2fSopenharmony_ci R"( [{"op": "replace", "path": "/foo", "value": 1}] )"_json 1251c5f01b2fSopenharmony_ci ) == R"( {"foo": 1} )"_json); 1252c5f01b2fSopenharmony_ci 1253c5f01b2fSopenharmony_ci CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( 1254c5f01b2fSopenharmony_ci R"( [{"op": "replace", "path": "/foo/1", "value": 4}] )"_json 1255c5f01b2fSopenharmony_ci ) == R"( {"foo": [1, 4, 3]} )"_json); 1256c5f01b2fSopenharmony_ci 1257c5f01b2fSopenharmony_ci CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( 1258c5f01b2fSopenharmony_ci R"( [{"op": "replace", "path": "/foo/0/bar", "value": 1}] )"_json 1259c5f01b2fSopenharmony_ci ) == R"( {"foo": [{"bar": 1}]} )"_json); 1260c5f01b2fSopenharmony_ci } 1261c5f01b2fSopenharmony_ci 1262c5f01b2fSopenharmony_ci SECTION("move") 1263c5f01b2fSopenharmony_ci { 1264c5f01b2fSopenharmony_ci CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( 1265c5f01b2fSopenharmony_ci R"( [{"op": "move", "from": "/foo", "path": "/bar"}] )"_json 1266c5f01b2fSopenharmony_ci ) == R"( {"bar": [1, 2, 3]} )"_json); 1267c5f01b2fSopenharmony_ci } 1268c5f01b2fSopenharmony_ci 1269c5f01b2fSopenharmony_ci SECTION("copy") 1270c5f01b2fSopenharmony_ci { 1271c5f01b2fSopenharmony_ci CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( 1272c5f01b2fSopenharmony_ci R"( [{"op": "copy", "from": "/foo/1", "path": "/bar"}] )"_json 1273c5f01b2fSopenharmony_ci ) == R"( {"foo": [1, 2, 3], "bar": 2} )"_json); 1274c5f01b2fSopenharmony_ci } 1275c5f01b2fSopenharmony_ci 1276c5f01b2fSopenharmony_ci SECTION("copy") 1277c5f01b2fSopenharmony_ci { 1278c5f01b2fSopenharmony_ci CHECK_NOTHROW(R"( {"foo": "bar"} )"_json.patch( 1279c5f01b2fSopenharmony_ci R"( [{"op": "test", "path": "/foo", "value": "bar"}] )"_json)); 1280c5f01b2fSopenharmony_ci } 1281c5f01b2fSopenharmony_ci } 1282c5f01b2fSopenharmony_ci 1283c5f01b2fSopenharmony_ci SECTION("Tests from github.com/json-patch/json-patch-tests") 1284c5f01b2fSopenharmony_ci { 1285c5f01b2fSopenharmony_ci for (const auto* filename : 1286c5f01b2fSopenharmony_ci { 1287c5f01b2fSopenharmony_ci TEST_DATA_DIRECTORY "/json-patch-tests/spec_tests.json", 1288c5f01b2fSopenharmony_ci TEST_DATA_DIRECTORY "/json-patch-tests/tests.json" 1289c5f01b2fSopenharmony_ci }) 1290c5f01b2fSopenharmony_ci { 1291c5f01b2fSopenharmony_ci CAPTURE(filename) 1292c5f01b2fSopenharmony_ci std::ifstream f(filename); 1293c5f01b2fSopenharmony_ci json suite = json::parse(f); 1294c5f01b2fSopenharmony_ci 1295c5f01b2fSopenharmony_ci for (const auto& test : suite) 1296c5f01b2fSopenharmony_ci { 1297c5f01b2fSopenharmony_ci INFO_WITH_TEMP(test.value("comment", "")); 1298c5f01b2fSopenharmony_ci 1299c5f01b2fSopenharmony_ci // skip tests marked as disabled 1300c5f01b2fSopenharmony_ci if (test.value("disabled", false)) 1301c5f01b2fSopenharmony_ci { 1302c5f01b2fSopenharmony_ci continue; 1303c5f01b2fSopenharmony_ci } 1304c5f01b2fSopenharmony_ci 1305c5f01b2fSopenharmony_ci const auto& doc = test["doc"]; 1306c5f01b2fSopenharmony_ci const auto& patch = test["patch"]; 1307c5f01b2fSopenharmony_ci 1308c5f01b2fSopenharmony_ci if (test.count("error") == 0) 1309c5f01b2fSopenharmony_ci { 1310c5f01b2fSopenharmony_ci // if an expected value is given, use it; use doc otherwise 1311c5f01b2fSopenharmony_ci const auto& expected = test.value("expected", doc); 1312c5f01b2fSopenharmony_ci CHECK(doc.patch(patch) == expected); 1313c5f01b2fSopenharmony_ci } 1314c5f01b2fSopenharmony_ci else 1315c5f01b2fSopenharmony_ci { 1316c5f01b2fSopenharmony_ci CHECK_THROWS(doc.patch(patch)); 1317c5f01b2fSopenharmony_ci } 1318c5f01b2fSopenharmony_ci } 1319c5f01b2fSopenharmony_ci } 1320c5f01b2fSopenharmony_ci } 1321c5f01b2fSopenharmony_ci} 1322