From cff81eb2ab780c43e736a8991e2b799d797e364f Mon Sep 17 00:00:00 2001 From: Tristan Penman Date: Mon, 10 Jul 2017 12:57:14 +1000 Subject: [PATCH] Add some example code that was prepared for a Melbourne C++ lightning talk --- CMakeLists.txt | 25 +++++ examples/array_iteration_basics.cpp | 132 +++++++++++++++++++++++ examples/array_iteration_template_fn.cpp | 65 +++++++++++ examples/json_pointers.cpp | 110 +++++++++++++++++++ examples/object_iteration.cpp | 69 ++++++++++++ examples/remote_resolution.cpp | 107 ++++++++++++++++++ 6 files changed, 508 insertions(+) create mode 100644 examples/array_iteration_basics.cpp create mode 100644 examples/array_iteration_template_fn.cpp create mode 100644 examples/json_pointers.cpp create mode 100644 examples/object_iteration.cpp create mode 100644 examples/remote_resolution.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b45dedf..ad15ef1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,26 @@ add_executable(external_schema examples/external_schema.cpp ) +add_executable(array_iteration_basics + examples/array_iteration_basics.cpp +) + +add_executable(array_iteration_template_fn + examples/array_iteration_template_fn.cpp +) + +add_executable(object_iteration + examples/object_iteration.cpp +) + +add_executable(json_pointers + examples/json_pointers.cpp +) + +add_executable(remote_resolution + examples/remote_resolution.cpp +) + set(TEST_SOURCES tests/test_adapter_comparison.cpp tests/test_fetch_document_callback.cpp @@ -121,3 +141,8 @@ endif() target_link_libraries(test_suite ${TEST_LIBS} ${Boost_LIBRARIES}) target_link_libraries(custom_schema ${Boost_LIBRARIES}) target_link_libraries(external_schema ${Boost_LIBRARIES}) +target_link_libraries(array_iteration_basics jsoncpp) +target_link_libraries(array_iteration_template_fn jsoncpp) +target_link_libraries(object_iteration jsoncpp) +target_link_libraries(json_pointers) +target_link_libraries(remote_resolution curl curlpp) diff --git a/examples/array_iteration_basics.cpp b/examples/array_iteration_basics.cpp new file mode 100644 index 0000000..b606a27 --- /dev/null +++ b/examples/array_iteration_basics.cpp @@ -0,0 +1,132 @@ +/** + * @file + * + * @brief Demonstrates iteration over an array and type check functions + * + */ + +#include +#include + +// jsoncpp +#include +#include +#include + +// RapidJSON +#include +#include +#include + +using std::cerr; +using std::cout; +using std::endl; +using std::runtime_error; + +// The first example uses RapidJson to load a JSON document. If the document +// contains an array, this function will print any array values that have a +// valid string representation. +void usingRapidJson(const char *filename); + +// The second example uses JsonCpp to perform the same task, but unlike the +// RapidJson example, we see how to use strict type checks and exception +// handling. +void usingJsonCpp(const char *filename); + +int main(int argc, char **argv) +{ + if (argc != 2) { + cerr << "Usage: " << endl; + cerr << " " << argv[0] << " " << endl; + return 1; + } + + // Load the document using rapidjson + cout << "-- Iteration using RapidJSON --" << endl; + usingRapidJson(argv[1]); + cout << endl; + + // Load the document using jsoncpp + cout << "-- Iteration using jsoncpp --" << endl; + usingJsonCpp(argv[1]); + cout << endl; + + return 0; +} + +void usingRapidJson(const char *filename) +{ + using valijson::adapters::RapidJsonAdapter; + + rapidjson::Document document; + if (!valijson::utils::loadDocument(filename, document)) { + return; + } + + RapidJsonAdapter adapter(document); + if (!adapter.isArray()) { + cout << "Not an array." << endl; + return; + } + + cout << "Array values:" << endl; + int index = 0; + + // We support the old way of doing things... + const RapidJsonAdapter::Array array = adapter.asArray(); + for (RapidJsonAdapter::Array::const_iterator itr = array.begin(); + itr != array.end(); ++itr) { + cout << " " << index++ << ": "; + + // Each element of the array is just another RapidJsonAdapter + const RapidJsonAdapter &value = *itr; + + // maybeString is a loose type check + if (value.maybeString()) { + // If a value may be a string, we are allowed to get a string + // representation of the value using asString + cout << value.asString(); + } + + cout << endl; + } +} + +void usingJsonCpp(const char *filename) +{ + Json::Value value; + if (!valijson::utils::loadDocument(filename, value)) { + return; + } + + valijson::adapters::JsonCppAdapter adapter(value); + + // isArray is a strict type check + if (!adapter.isArray()) { + cout << "Not an array." << endl; + return; + } + + cout << "Array values:" << endl; + int index = 0; + + // If a value is not an array, then calling getArray will cause a runtime + // exception to be raised. + for (auto value : adapter.getArray()) { + cout << " " << index++ << ": "; + + // isString is another strict type check. Valijson uses the convention + // that strict type check functions are prefixed with 'is'. + if (!value.isString()) { + cout << "Not a string. "; + } + + try { + // Also by convention, functions prefixed with 'get' will raise a + // runtime exception if the value is not of the correct type. + cout << value.getString() << endl; + } catch (runtime_error &e) { + cout << "Caught exception: " << e.what() << endl; + } + } +} diff --git a/examples/array_iteration_template_fn.cpp b/examples/array_iteration_template_fn.cpp new file mode 100644 index 0000000..9e211b9 --- /dev/null +++ b/examples/array_iteration_template_fn.cpp @@ -0,0 +1,65 @@ +/** + * @file + * + * @brief Demonstrates iteration over an array using template functions + * + */ + +#include + +#include +#include +#include + +using std::cerr; +using std::cout; +using std::endl; + +template +void iterateJsonArray(const AdapterType &adapter) +{ + if (!adapter.isArray()) { + cout << "Not an array." << endl; + return; + } + + cout << "Array values:" << endl; + int index = 0; + + for (auto value : adapter.getArray()) { + cout << " " << index++ << ": "; + + if (value.maybeString()) { + cout << value.asString(); + } + + cout << endl; + } +} + +void usingJsonCppWithTemplateFn(const char *filename) +{ + Json::Value value; + if (!valijson::utils::loadDocument(filename, value)) { + return; + } + + valijson::adapters::JsonCppAdapter adapter(value); + iterateJsonArray(adapter); +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + cerr << "Usage: " << endl; + cerr << " " << argv[0] << " " << endl; + return 1; + } + + // Load the document using jsoncpp and iterate over array using function template + cout << "-- Array iteration using jsoncpp via template function --" << endl; + usingJsonCppWithTemplateFn(argv[1]); + cout << endl; + + return 0; +} diff --git a/examples/json_pointers.cpp b/examples/json_pointers.cpp new file mode 100644 index 0000000..1020920 --- /dev/null +++ b/examples/json_pointers.cpp @@ -0,0 +1,110 @@ +/** + * @file + * + * @brief Demonstrates how to resolve JSON pointers against the current document + * + */ + +#include + +#include + +#include +#include +#include +#include + +using std::cerr; +using std::cout; +using std::endl; + +template +std::string maybeResolveRef(const AdapterType &value, const AdapterType &root) +{ + if (!value.isObject()) { + // Not an object, therefore not a JSON reference + return ""; + } + + const auto &object = value.getObject(); + const auto itr = object.find("$ref"); + if (itr == object.end()) { + // Object does not contain $ref property + return ""; + } + + const AdapterType maybeRef = itr->second; + if (!maybeRef.isString()) { + return "[$ref did not contain a string value]"; + } + + // Attempt to extract a JSON pointer + const std::string ref = maybeRef.getString(); + const auto maybePointer = valijson::internal::json_reference::getJsonReferencePointer(ref); + if (!maybePointer) { + return "[$ref did not contain valid JSON pointer]"; + } + + const auto refAdapter = valijson::internal::json_pointer::resolveJsonPointer(root, *maybePointer); + if (!refAdapter.maybeString()) { + return "[$ref did not point to a string value]"; + } + + return refAdapter.asString(); +} + +template +void iterateJsonObject(const AdapterType &adapter) +{ + if (!adapter.maybeObject()) { + cout << "Not an object." << endl; + return; + } + + cout << "Object members:" << endl; + + // JSON objects are an unordered collection of key-value pairs, + // so the members of the object may be printed in an order that is + // different to that in the source JSON document. + for (auto member : adapter.asObject()) { + // The key is a std::string that can be accessed using 'first' + cout << " " << member.first << ": "; + + // The value is just another Adapter, and can be accessed using 'second' + const AdapterType &value = member.second; + if (value.maybeString()) { + cout << value.asString(); + } else { + cout << maybeResolveRef(value, adapter); + } + + cout << endl; + } +} + +void usingJsonCppWithTemplateFn(const char *filename) +{ + rapidjson::Document document; + if (!valijson::utils::loadDocument(filename, document)) { + return; + } + + valijson::adapters::RapidJsonAdapter adapter(document); + iterateJsonObject(adapter); +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + cerr << "Usage: " << endl; + cerr << " " << argv[0] << " " << endl; + return 1; + } + + // Load the document using jsoncpp and iterate over array using function template + cout << "-- Resolving JSON pointers using RapidJSON --" << endl; + usingJsonCppWithTemplateFn(argv[1]); + cout << endl; + + return 0; +} diff --git a/examples/object_iteration.cpp b/examples/object_iteration.cpp new file mode 100644 index 0000000..aaf9fe9 --- /dev/null +++ b/examples/object_iteration.cpp @@ -0,0 +1,69 @@ +/** + * @file + * + * @brief Demonstrates iteration over the members of an object + * + */ + +#include + +#include +#include +#include + +using std::cerr; +using std::cout; +using std::endl; + +template +void iterateJsonObject(const AdapterType &adapter) +{ + if (!adapter.maybeObject()) { + cout << "Not an object." << endl; + return; + } + + cout << "Object members:" << endl; + + // JSON objects are an unordered collection of key-value pairs, + // so the members of the object may be printed in an order that is + // different to that in the source JSON document. + for (auto member : adapter.asObject()) { + // The key is a std::string that can be accessed using 'first' + cout << " " << member.first << ": "; + + // The value is just another Adapter, and can be accessed using 'second' + const AdapterType &value = member.second; + if (value.maybeString()) { + cout << value.asString(); + } + + cout << endl; + } +} + +void usingJsonCppWithTemplateFn(const char *filename) +{ + Json::Value value; + if (!valijson::utils::loadDocument(filename, value)) { + return; + } + + valijson::adapters::JsonCppAdapter adapter(value); + iterateJsonObject(adapter); +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + cerr << "Usage: " << endl; + cerr << " " << argv[0] << " " << endl; + return 1; + } + + cout << "-- Object iteration using jsoncpp via template function --" << endl; + usingJsonCppWithTemplateFn(argv[1]); + cout << endl; + + return 0; +} \ No newline at end of file diff --git a/examples/remote_resolution.cpp b/examples/remote_resolution.cpp new file mode 100644 index 0000000..72a1318 --- /dev/null +++ b/examples/remote_resolution.cpp @@ -0,0 +1,107 @@ +/** + * @file + * + * @brief Demonstrates resolution of remote JSON references using cURLpp + * + */ + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using std::cerr; +using std::cout; +using std::endl; + +using valijson::Schema; +using valijson::SchemaParser; +using valijson::Validator; +using valijson::ValidationResults; +using valijson::adapters::RapidJsonAdapter; + +const rapidjson::Document * fetchDocument(const std::string &uri) +{ + cout << "Fetching " << uri << "..." << endl; + curlpp::Cleanup myCleanup; + std::ostringstream os; + os << curlpp::options::Url(uri); + rapidjson::Document *fetchedRoot = new rapidjson::Document(); + fetchedRoot->template Parse<0>(os.str().c_str()); + return fetchedRoot; +} + +void freeDocument(const rapidjson::Document *adapter) +{ + delete adapter; +} + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + cerr << "Usage: " << argv[0] << " " << endl; + return 1; + } + + // Load the document containing the schema + rapidjson::Document schemaDocument; + if (!valijson::utils::loadDocument(argv[1], schemaDocument)) { + cerr << "Failed to load schema document." << endl; + return 1; + } + + // Load the document that is to be validated + rapidjson::Document targetDocument; + if (!valijson::utils::loadDocument(argv[2], targetDocument)) { + cerr << "Failed to load target document." << endl; + return 1; + } + + // Parse the json schema into an internal schema format + Schema schema; + SchemaParser parser; + RapidJsonAdapter schemaDocumentAdapter(schemaDocument); + try { + parser.populateSchema(schemaDocumentAdapter, schema, fetchDocument, freeDocument); + } catch (std::exception &e) { + cerr << "Failed to parse schema: " << e.what() << endl; + return 1; + } + + // Perform validation + Validator validator(Validator::kWeakTypes); + ValidationResults results; + RapidJsonAdapter targetDocumentAdapter(targetDocument); + if (!validator.validate(schema, targetDocumentAdapter, &results)) { + std::cerr << "Validation failed." << endl; + ValidationResults::Error error; + unsigned int errorNum = 1; + while (results.popError(error)) { + + std::string context; + std::vector::iterator itr = error.context.begin(); + for (; itr != error.context.end(); itr++) { + context += *itr; + } + + cerr << "Error #" << errorNum << std::endl + << " context: " << context << endl + << " desc: " << error.description << endl; + ++errorNum; + } + return 1; + } + + return 0; +}