diff --git a/README.md b/README.md index 742bb684af..ee57513c81 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ For the examples below you need to include some header files and initialize a st ```c++ #include -#include +#include #include using namespace jsoncons; // for convenience @@ -430,7 +430,7 @@ For the examples below you need to include some header files and initialize a bu #include #include #include -#include +#include using namespace jsoncons; // for convenience diff --git a/doc/Examples.md b/doc/Examples.md index f22c2f2a48..5075a6472e 100644 --- a/doc/Examples.md +++ b/doc/Examples.md @@ -2199,7 +2199,7 @@ double sale_price = j.get_value_or("sale_price", 22.0); // returns 22.0 ```c++ #include #include -#include +#include int main() { @@ -2349,7 +2349,7 @@ You can use [json_replace](ref/jsonpath/json_replace.md) in the `jsonpath` exten ```c++ #include -#include +#include using namespace jsoncons; diff --git a/doc/Pages/index.md b/doc/Pages/index.md index f8412b9d2d..830528be47 100644 --- a/doc/Pages/index.md +++ b/doc/Pages/index.md @@ -771,7 +771,7 @@ Example JSON file (booklist.json): ``` JSONPath examples: ```c++ -#include +#include using jsoncons::jsonpath::json_query; diff --git a/doc/Tutorials/Basics.md b/doc/Tutorials/Basics.md index d50dd63bd8..cf78ae63f1 100644 --- a/doc/Tutorials/Basics.md +++ b/doc/Tutorials/Basics.md @@ -182,7 +182,7 @@ The JSON output `booklist.json` ```c++ #include #include -#include +#include // For convenience using jsoncons::json; diff --git a/doc/ref/cbor/cbor.md b/doc/ref/cbor/cbor.md index c26b6515c8..6631bba305 100644 --- a/doc/ref/cbor/cbor.md +++ b/doc/ref/cbor/cbor.md @@ -120,7 +120,7 @@ For the examples below you need to include some header files and initialize a bu #include #include #include -#include +#include using namespace jsoncons; // for convenience @@ -500,7 +500,7 @@ Marilyn C, 0.9 ```c++ #include #include -#include +#include #include #include #include diff --git a/doc/ref/jsonpath/json_query.md b/doc/ref/jsonpath/json_query.md index 17f76431a4..0d5fb0ea1d 100644 --- a/doc/ref/jsonpath/json_query.md +++ b/doc/ref/jsonpath/json_query.md @@ -85,7 +85,7 @@ The examples below use the JSON text from [Stefan Goessner's JSONPath](http://go ```c++ #include -#include +#include #include using namespace jsoncons; @@ -244,7 +244,7 @@ Output: ```c++ #include -#include +#include using namespace jsoncons; diff --git a/doc/ref/jsonpath/json_replace.md b/doc/ref/jsonpath/json_replace.md index 9812633423..bb56469585 100644 --- a/doc/ref/jsonpath/json_replace.md +++ b/doc/ref/jsonpath/json_replace.md @@ -63,7 +63,7 @@ Input JSON file `booklist.json`: ``` ```c++ #include -#include +#include using namespace jsoncons; using namespace jsoncons::jsonpath; diff --git a/doc/ref/jsonpointer/unflatten_method.md b/doc/ref/jsonpointer/unflatten_method.md new file mode 100644 index 0000000000..92015e21d5 --- /dev/null +++ b/doc/ref/jsonpointer/unflatten_method.md @@ -0,0 +1,17 @@ +### jsoncons::jsonpointer::unflatten_method + +```c++ +#include + +enum class unflatten_method{object=1,safe}; +``` +`unflatten_method` is used to specify how to unflatten a single depth +object of JSON Pointer-value pairs. There is no unique solution. +An integer appearing in a path could be an index or it could be an object key. + +Value |Definition +-----------|----------- +unflatten_method()|If there is a zero in a path, it will start a new array. +object|Arrays will not be created when calling unflatten, rather, an integer appearing in a path is assumed to be an object key +safe| + diff --git a/doc/ref/ubjson/ubjson.md b/doc/ref/ubjson/ubjson.md index abaaf835dd..5137212f76 100644 --- a/doc/ref/ubjson/ubjson.md +++ b/doc/ref/ubjson/ubjson.md @@ -39,7 +39,7 @@ For the examples below you need to include some header files and initialize a bu #include #include #include -#include +#include using namespace jsoncons; // for convenience diff --git a/examples/src/basics_examples.cpp b/examples/src/basics_examples.cpp index 57e9791f30..e68618cb85 100644 --- a/examples/src/basics_examples.cpp +++ b/examples/src/basics_examples.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include using namespace jsoncons; using namespace jsoncons::jsonpath; diff --git a/examples/src/basics_wexamples.cpp b/examples/src/basics_wexamples.cpp index 3277b0b8cc..93b5613eb9 100644 --- a/examples/src/basics_wexamples.cpp +++ b/examples/src/basics_wexamples.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include using namespace jsoncons; diff --git a/examples/src/cbor_examples.cpp b/examples/src/cbor_examples.cpp index 4044fb811d..572eb7c9ce 100644 --- a/examples/src/cbor_examples.cpp +++ b/examples/src/cbor_examples.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "sample_types.hpp" #include #include diff --git a/examples/src/cbor_typed_array_examples.cpp b/examples/src/cbor_typed_array_examples.cpp index 16db59e492..9bb96966f2 100644 --- a/examples/src/cbor_typed_array_examples.cpp +++ b/examples/src/cbor_typed_array_examples.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "sample_types.hpp" #include #include diff --git a/examples/src/examples.cpp b/examples/src/examples.cpp index 422f5e7092..0a014a5303 100644 --- a/examples/src/examples.cpp +++ b/examples/src/examples.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include using namespace jsoncons; diff --git a/examples/src/json_constructor_examples.cpp b/examples/src/json_constructor_examples.cpp index de8c88a547..78bb604916 100644 --- a/examples/src/json_constructor_examples.cpp +++ b/examples/src/json_constructor_examples.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include using namespace jsoncons; diff --git a/examples/src/readme_examples.cpp b/examples/src/readme_examples.cpp index 4ef6697054..05e130d168 100644 --- a/examples/src/readme_examples.cpp +++ b/examples/src/readme_examples.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/examples/src/ubjson_examples.cpp b/examples/src/ubjson_examples.cpp index d929e183e9..39e6feb545 100644 --- a/examples/src/ubjson_examples.cpp +++ b/examples/src/ubjson_examples.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp index 20da2c046d..1ad669a9b3 100644 --- a/include/jsoncons_ext/jsonpointer/jsonpointer.hpp +++ b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp @@ -984,10 +984,120 @@ enum class pointer_state return result; } + // unflatten + enum class unflatten_method {object=1,safe}; + + template + Json unflatten_to_safe_array (Json& value) + { + if (!value.is_object()) + { + return value; + } + bool safe = true; + std::size_t index = 0; + for (const auto& item : value.object_range()) + { + auto r = jsoncons::detail::to_integer(item.key().data(),item.key().size()); + if (!r || (index++ != r.value())) + { + safe = false; + break; + } + } + + if (safe) + { + Json j(json_array_arg); + j.reserve(value.size()); + for (auto& item : value.object_range()) + { + j.emplace_back(std::move(item.value())); + } + Json a(json_array_arg); + for (auto& item : j.array_range()) + { + a.emplace_back(unflatten_to_safe_array (item)); + } + return a; + } + else + { + Json o(json_object_arg); + for (auto& item : value.object_range()) + { + o.try_emplace(item.key(), unflatten_to_safe_array (item.value())); + } + return o; + } + } + template - Json unflatten(const Json& value) + Json unflatten_to_default(const Json& value) + { + using char_type = typename Json::char_type; + + if (JSONCONS_UNLIKELY(!value.is_object())) + { + JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); + } + Json result; + + for (const auto& item: value.object_range()) + { + Json* part = &result; + basic_json_ptr ptr(item.key()); + for (auto it = ptr.begin(); it != ptr.end(); ) + { + auto s = *it; + auto r = jsoncons::detail::to_integer(s.data(), s.size()); + if (r && (part->is_array() || r.value() == 0)) + { + if (!part->is_array()) + { + *part = Json(json_array_arg); + } + if (++it != ptr.end()) + { + if (r.value()+1 > part->size()) + { + Json& ref = part->emplace_back(); + part = std::addressof(ref); + } + else + { + part = &part->at(r.value()); + } + } + else + { + Json& ref = part->emplace_back(item.value()); + part = std::addressof(ref); + } + } + else + { + if (++it != ptr.end()) + { + auto res = part->try_emplace(s,Json()); + part = &(res.first->value()); + } + else + { + auto res = part->try_emplace(s, item.value()); + part = &(res.first->value()); + } + } + } + } + + return result; + } + + template + Json unflatten_to_object(const Json& value, unflatten_method method = unflatten_method::safe) { using char_type = typename Json::char_type; @@ -1017,7 +1127,13 @@ enum class pointer_state } } - return result; + return method == unflatten_method::safe ? unflatten_to_safe_array (result) : result; + } + + template + Json unflatten(const Json& value, unflatten_method method = unflatten_method()) + { + return method == unflatten_method() ? unflatten_to_default(value) : unflatten_to_object(value,method); } } // namespace jsonpointer diff --git a/tests/src/jsonpointer/jsonpointer_flatten_tests.cpp b/tests/src/jsonpointer/jsonpointer_flatten_tests.cpp index c97c88729a..6f4f1e9724 100644 --- a/tests/src/jsonpointer/jsonpointer_flatten_tests.cpp +++ b/tests/src/jsonpointer/jsonpointer_flatten_tests.cpp @@ -16,6 +16,115 @@ #include using namespace jsoncons; +TEST_CASE("jsonpointer unflatten tests 1") +{ + SECTION("test 1") + { + json input = json::parse(R"( + { + "discards": { + "1000": "Record does not exist", + "1004": "Queue limit exceeded", + "1010": "Discarding timed-out partial msg" + }, + "warnings": { + "0": "Phone number missing country code", + "1": "State code missing", + "2": "Zip code missing" + } + } + )"); + + json flattened = jsonpointer::flatten(input); + + json unflattened1 = jsonpointer::unflatten(flattened); + std::cout << "(1)\n" << pretty_print(unflattened1) << "\n"; + + json unflattened2 = jsonpointer::unflatten(flattened, + jsonpointer::unflatten_method::object); + std::cout << "(2)\n" << pretty_print(unflattened2) << "\n"; + + json unflattened3 = jsonpointer::unflatten(flattened, + jsonpointer::unflatten_method::safe); + std::cout << "(3)\n" << pretty_print(unflattened3) << "\n"; + } +} + +TEST_CASE("jsonpointer unflatten tests 2") +{ + json input = json::parse(R"( + { + "0": { + "1000": "Record does not exist", + "1004": "Queue limit exceeded", + "1010": "Discarding timed-out partial msg" + }, + "1": { + "0": "Phone number missing country code", + "1": "State code missing", + "2": "Zip code missing" + } + } + )"); + + json flattened = jsonpointer::flatten(input); + + SECTION("default test") + { + json expected = json::parse(R"( + [ + { + "1000": "Record does not exist", + "1004": "Queue limit exceeded", + "1010": "Discarding timed-out partial msg" + }, + ["Phone number missing country code", "State code missing", "Zip code missing"] + ] + )"); + + json unflattened = jsonpointer::unflatten(flattened); + CHECK(unflattened == expected); + std::cout << "(1)\n" << pretty_print(unflattened) << "\n"; + } + SECTION("object test") + { + json expected = json::parse(R"( + { + "0": { + "1000": "Record does not exist", + "1004": "Queue limit exceeded", + "1010": "Discarding timed-out partial msg" + }, + "1": { + "0": "Phone number missing country code", + "1": "State code missing", + "2": "Zip code missing" + } + } + )"); + + json unflattened = jsonpointer::unflatten(flattened, jsonpointer::unflatten_method::object); + CHECK(unflattened == expected); + std::cout << "(2)\n" << pretty_print(unflattened) << "\n"; + } + SECTION("safe test") + { + json expected = json::parse(R"( + [ + { + "1000": "Record does not exist", + "1004": "Queue limit exceeded", + "1010": "Discarding timed-out partial msg" + }, + ["Phone number missing country code", "State code missing", "Zip code missing"] + ] + )"); + + json unflattened = jsonpointer::unflatten(flattened, jsonpointer::unflatten_method::safe); + CHECK(unflattened == expected); + std::cout << "(3)\n" << pretty_print(unflattened) << "\n"; + } +} TEST_CASE("flatten test") { @@ -39,26 +148,6 @@ TEST_CASE("flatten test") } )"); - json expected_unflattened = json::parse(R"( - { - "application": "hiking", - "reputons": { - "0": { - "assertion": "advanced", - "rated": "Marilyn C", - "rater": "HikingAsylum", - "rating": 0.9 - }, - "1": { - "assertion": "intermediate", - "rated": "Hongmin", - "rater": "HikingAsylum", - "rating": 0.75 - } - } - } - )"); - SECTION("flatten") { json result = jsonpointer::flatten(input); @@ -78,8 +167,8 @@ TEST_CASE("flatten test") //std::cout << pretty_print(result) << "\n"; json unflattened = jsonpointer::unflatten(result); - CHECK(unflattened == expected_unflattened); - std::cout << pretty_print(unflattened) << "\n"; + CHECK(unflattened == input); + //std::cout << pretty_print(unflattened) << "\n"; } }