mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-08 11:13:50 +00:00
Compare commits
5 Commits
copilot/ad
...
json11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ac97adf6b | ||
|
|
06f93c859d | ||
|
|
9e4a4d5b94 | ||
|
|
4a35464d9b | ||
|
|
e316127243 |
@@ -10,10 +10,8 @@ ADD_EXECUTABLE ( loader_example
|
||||
|
||||
ADD_SUBDIRECTORY ( examples/gltfutil )
|
||||
ADD_SUBDIRECTORY ( examples/glview )
|
||||
ADD_SUBDIRECTORY ( examples/validator )
|
||||
|
||||
INSTALL ( FILES
|
||||
json.hpp
|
||||
stb_image.h
|
||||
stb_image_write.h
|
||||
tiny_gltf.h
|
||||
|
||||
19
README.md
19
README.md
@@ -2,11 +2,12 @@
|
||||
|
||||
`TinyGLTF` is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library.
|
||||
|
||||
`TinyGLTF` uses Niels Lohmann's json library(https://github.com/nlohmann/json), so now it requires C++11 compiler.
|
||||
`TinyGLTF` uses dropbox/json11 JSON library(https://github.com/dropbox/json11), so now it requires C++11 compiler.
|
||||
If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
|
||||
## Status
|
||||
|
||||
v2.2.0 Use dropbox/json11 instead of nlohmann's json.hpp
|
||||
v2.0.0 release(22 Aug, 2018)!
|
||||
|
||||
## Builds
|
||||
@@ -23,10 +24,11 @@ v2.0.0 release(22 Aug, 2018)!
|
||||
* [x] Linux + gcc/clang
|
||||
* [x] Windows + MinGW
|
||||
* [x] Windows + Visual Studio 2015 Update 3 or later.
|
||||
* Visual Studio 2013 is not supported since they have limited C++11 support and failed to compile `json.hpp`.
|
||||
* Visual Studio 2013 is not supported since they have limited C++11 support.
|
||||
* [x] Android NDK
|
||||
* [x] Android + CrystaX(NDK drop-in replacement) GCC
|
||||
* [x] Web using Emscripten(LLVM)
|
||||
* [x] Can be compiled with no RTTI, noexception.
|
||||
* Moderate parsing time and memory consumption.
|
||||
* glTF specification v2.0.0
|
||||
* [x] ASCII glTF
|
||||
@@ -82,7 +84,7 @@ TinyGLTF is licensed under MIT license.
|
||||
|
||||
TinyGLTF uses the following third party libraries.
|
||||
|
||||
* json.hpp : Copyright (c) 2013-2017 Niels Lohmann. MIT license.
|
||||
* json11 : Copyright (c) 2013 Dropbox, Inc. MIT license.
|
||||
* base64 : Copyright (C) 2004-2008 René Nyffenegger
|
||||
* stb_image.h : v2.08 - public domain image loader - [Github link](https://github.com/nothings/stb/blob/master/stb_image.h)
|
||||
* stb_image_write.h : v1.09 - public domain image writer - [Github link](https://github.com/nothings/stb/blob/master/stb_image_write.h)
|
||||
@@ -90,7 +92,8 @@ TinyGLTF uses the following third party libraries.
|
||||
|
||||
## Build and example
|
||||
|
||||
Copy `stb_image.h`, `stb_image_write.h`, `json.hpp` and `tiny_gltf.h` to your project.
|
||||
Copy `stb_image.h`, `stb_image_write.h`, and `tiny_gltf.h` to your project.
|
||||
json11 code is embeded into `tiny_gltf.h`
|
||||
|
||||
### Loading glTF 2.0 model
|
||||
|
||||
@@ -99,7 +102,9 @@ Copy `stb_image.h`, `stb_image_write.h`, `json.hpp` and `tiny_gltf.h` to your pr
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
// #define TINYGLTF_NOEXCEPTION // optional. disable exception handling.
|
||||
// Optional. Uncomment these if you want external json11.cpp instead of embedded json11.cpp code
|
||||
//#define TINYGLTF_USE_EXTERNAL_JSON11_CPP
|
||||
//#include "json11.hpp"
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
using namespace tinygltf;
|
||||
@@ -128,12 +133,12 @@ if (!ret) {
|
||||
|
||||
## Compile options
|
||||
|
||||
* `TINYGLTF_NOEXCEPTION` : Disable C++ exception in JSON parsing. You can use `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION` and `TINYGLTF_NOEXCEPTION` to fully remove C++ exception codes when compiling TinyGLTF.
|
||||
* `TINYGLTF_NO_STB_IMAGE` : Do not load images with stb_image. Instead use `TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)` to set a callback for loading images.
|
||||
* `TINYGLTF_NO_STB_IMAGE_WRITE` : Do not write images with stb_image_write. Instead use `TinyGLTF::SetImageWriter(WriteimageDataFunction WriteImageData, void *user_data)` to set a callback for writing images.
|
||||
* `TINYGLTF_NO_EXTERNAL_IMAGE` : Do not try to load external image file. This option woulde be helpful if you do not want load image file during glTF parsing.
|
||||
* `TINYGLTF_ANDROID_LOAD_FROM_ASSETS`: Load all files from packaged app assets instead of the regular file system. **Note:** You must pass a valid asset manager from your android app to `tinygltf::asset_manager` beforehand.
|
||||
* `TINYGLTF_ENABLE_DRACO`: Enable Draco compression. User must provide include path and link correspnding libraries in your project file.
|
||||
* `TINYGLTF_USE_EXTERNAL_JSON11_CPP` : Use externally supplied json11.cpp/json11.hpp. This option may be helpful if you use json11.cpp in other source codes of your project.
|
||||
|
||||
### Saving gltTF 2.0 model
|
||||
* [ ] Buffers.
|
||||
@@ -173,6 +178,6 @@ $ ./tester_noexcept
|
||||
|
||||
## Third party licenses
|
||||
|
||||
* json.hpp : Licensed under the MIT License <http://opensource.org/licenses/MIT>. Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
* json11 : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
* stb_image : Public domain.
|
||||
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
|
||||
|
||||
@@ -312,7 +312,11 @@ int main(int argc, char **argv) {
|
||||
glewExperimental = GL_TRUE;
|
||||
#endif
|
||||
|
||||
glewInit();
|
||||
if (glewInit() != GLEW_OK) {
|
||||
std::cerr << "glew init error." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << glGetString(GL_RENDERER) << ", " << glGetString(GL_VERSION)
|
||||
<< std::endl;
|
||||
|
||||
|
||||
@@ -678,13 +678,13 @@ static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
|
||||
// FIXME(syoyo): Refactor.
|
||||
// DrawCurves(scene, it->second);
|
||||
if (node.mesh > -1) {
|
||||
assert(node.mesh < model.meshes.size());
|
||||
assert(node.mesh < int(model.meshes.size()));
|
||||
DrawMesh(model, model.meshes[node.mesh]);
|
||||
}
|
||||
|
||||
// Draw child nodes.
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
assert(node.children[i] < model.nodes.size());
|
||||
assert(node.children[i] < int(model.nodes.size()));
|
||||
DrawNode(model, model.nodes[node.children[i]]);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ solution "glview"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
cppdialect "C++11"
|
||||
files { "glview.cc", "../common/trackball.cc" }
|
||||
files { "glview.cc", "../../json11.cpp", "../common/trackball.cc" }
|
||||
includedirs { "./" }
|
||||
includedirs { "../../" }
|
||||
includedirs { "../common/" }
|
||||
|
||||
@@ -2,21 +2,20 @@ project(tinygltf-validator CXX)
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
# Use C++11
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
# exe
|
||||
add_executable(tinygltf-validator
|
||||
app/tinygltf-validate.cc
|
||||
src/json-schema.hpp
|
||||
src/json-schema-draft4.json.cpp
|
||||
src/json-uri.cpp
|
||||
src/json-validator.cpp)
|
||||
tinygltf-validate.cc
|
||||
json11.cpp)
|
||||
|
||||
target_include_directories(tinygltf-validator
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/valijson/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/
|
||||
)
|
||||
|
||||
target_compile_features(tinygltf-validator
|
||||
PUBLIC
|
||||
cxx_range_for) # for C++11 - flags
|
||||
# Enable more compiler warnings, except when using Visual Studio compiler
|
||||
if(NOT MSVC)
|
||||
target_compile_options(tinygltf-validator
|
||||
@@ -51,3 +50,4 @@ install ( TARGETS
|
||||
DESTINATION
|
||||
bin
|
||||
)
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
Modern C++ JSON schema validator is licensed under the MIT License
|
||||
<http://opensource.org/licenses/MIT>:
|
||||
|
||||
Copyright (c) 2016 Patrick Boettcher
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
12
examples/validator/LICENSE.jsonhpp.MIT → examples/validator/LICENSE.json11.txt
Normal file → Executable file
12
examples/validator/LICENSE.jsonhpp.MIT → examples/validator/LICENSE.json11.txt
Normal file → Executable file
@@ -1,6 +1,4 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-2017 Niels Lohmann
|
||||
Copyright (c) 2013 Dropbox, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -9,13 +7,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
4
examples/validator/Makefile
Normal file
4
examples/validator/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
EXTRA_CXXFLAGS=-fsanitize=address
|
||||
|
||||
all:
|
||||
clang++ $(EXTRA_CXXFLAGS) -g -O1 -I. -Ivalijson/include tinygltf-validate.cc json11.cpp -o tinygltf-validate
|
||||
@@ -1,32 +1,15 @@
|
||||
# tinygltf-validator
|
||||
|
||||
TinyGLTF validator based on Modern C++ JSON schema validator https://github.com/pboettch/json-schema-validator
|
||||
|
||||
## Status
|
||||
|
||||
Experimental. W.I.P.
|
||||
|
||||
## Requirements
|
||||
|
||||
* C++11 compiler
|
||||
* CMake
|
||||
|
||||
## How to build
|
||||
Currently it causes stack-overflow in runtime.
|
||||
|
||||
```
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
$ ./tinygltf-validate schema_dir ../../models/Cube/Cube.gltf
|
||||
|
||||
...
|
||||
|
||||
==17033==ERROR: AddressSanitizer: stack-overflow on address 0x7fff55415d68 (pc 0x0000004c62de bp 0x7fff554165c0 sp 0x7fff55415d70 T0)
|
||||
#0 0x4c62dd in __asan_memset /tmp/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:27:3
|
||||
#1 0x53a7a7 in std::_Vector_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::_Vector_impl::_Vector_impl(std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&) /usr/lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:91:37
|
||||
#2 0x53a6f1 in std::_Vector_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::_Vector_base(unsigned long, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&) /usr/lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:135:9
|
||||
#3 0x53a9e2 in std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::vector(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&) /usr/lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:319:9
|
||||
#4 0x584313 in valijson::ValidationVisitor<valijson::adapters::Json11Adapter>::ValidationVisitor(valijson::ValidationVisitor<valijson::adapters::Json11Adapter> const&) /home/syoyo/work/tinygltf/examples/validator/valijson/include/valijson/validation_visitor.hpp:26:7
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
```
|
||||
$ gltf-validator /path/to/file.gltf /path/to/gltf-schema
|
||||
```
|
||||
|
||||
## Third party licenses
|
||||
|
||||
* json.hpp https://github.com/nlohmann/json : MIT
|
||||
* json-schema-validator https://github.com/pboettch/json-schema-validator : MIT
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Modern C++ JSON schema validator
|
||||
*
|
||||
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*
|
||||
* Copyright (c) 2016 Patrick Boettcher <patrick.boettcher@posteo.de>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <json-schema.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_uri;
|
||||
using nlohmann::json_schema_draft4::json_validator;
|
||||
|
||||
static void usage(const char *name)
|
||||
{
|
||||
std::cerr << "Usage: " << name << " <gltf file> <gltf schema dir>\n";
|
||||
std::cerr << " schema dir : $glTF/specification/2.0/schema\n";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
resolver r(nlohmann::json_schema_draft4::root_schema,
|
||||
nlohmann::json_schema_draft4::root_schema["id"]);
|
||||
schema_refs_.insert(r.schema_refs.begin(), r.schema_refs.end());
|
||||
assert(r.undefined_refs.size() == 0);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
static void loader(const json_uri &uri, json &schema)
|
||||
{
|
||||
std::fstream lf("." + uri.path());
|
||||
if (!lf.good())
|
||||
throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path());
|
||||
|
||||
try {
|
||||
lf >> schema;
|
||||
} catch (std::exception &e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool validate(const std::string &schema_dir, const std::string &filename)
|
||||
{
|
||||
std::string gltf_schema = schema_dir + "/glTF.schema.json";
|
||||
|
||||
std::fstream f(gltf_schema);
|
||||
if (!f.good()) {
|
||||
std::cerr << "could not open " << gltf_schema << " for reading\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1) Read the schema for the document you want to validate
|
||||
json schema;
|
||||
try {
|
||||
f >> schema;
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << e.what() << " at " << f.tellp() << " - while parsing the schema\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) create the validator and
|
||||
json_validator validator([&schema_dir](const json_uri &uri, json &schema) {
|
||||
std::cout << "uri.url : " << uri.url() << std::endl;
|
||||
std::cout << "uri.path : " << uri.path() << std::endl;
|
||||
|
||||
std::fstream lf(schema_dir + "/" + uri.path());
|
||||
if (!lf.good())
|
||||
throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path());
|
||||
|
||||
try {
|
||||
lf >> schema;
|
||||
} catch (std::exception &e) {
|
||||
throw e;
|
||||
}
|
||||
}, [](const std::string &, const std::string &) {});
|
||||
|
||||
try {
|
||||
// insert this schema as the root to the validator
|
||||
// this resolves remote-schemas, sub-schemas and references via the given loader-function
|
||||
validator.set_root_schema(schema);
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "setting root schema failed\n";
|
||||
std::cerr << e.what() << "\n";
|
||||
}
|
||||
|
||||
// 3) do the actual validation of the document
|
||||
json document;
|
||||
|
||||
std::fstream d(filename);
|
||||
if (!d.good()) {
|
||||
std::cerr << "could not open " << filename << " for reading\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
d >> document;
|
||||
validator.validate(document);
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "schema validation failed\n";
|
||||
std::cerr << e.what() << " at offset: " << d.tellg() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cerr << "document is valid\n";
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 3)
|
||||
usage(argv[0]);
|
||||
|
||||
bool ret = validate(argv[1], argv[2]);
|
||||
|
||||
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
788
examples/validator/json11.cpp
Normal file
788
examples/validator/json11.cpp
Normal file
@@ -0,0 +1,788 @@
|
||||
/* Copyright (c) 2013 Dropbox, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "json11.hpp"
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
namespace json11 {
|
||||
|
||||
static const int max_depth = 200;
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::map;
|
||||
using std::make_shared;
|
||||
using std::initializer_list;
|
||||
using std::move;
|
||||
|
||||
/* Helper for representing null - just a do-nothing struct, plus comparison
|
||||
* operators so the helpers in JsonValue work. We can't use nullptr_t because
|
||||
* it may not be orderable.
|
||||
*/
|
||||
struct NullStruct {
|
||||
bool operator==(NullStruct) const { return true; }
|
||||
bool operator<(NullStruct) const { return false; }
|
||||
};
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Serialization
|
||||
*/
|
||||
|
||||
static void dump(NullStruct, string &out) {
|
||||
out += "null";
|
||||
}
|
||||
|
||||
static void dump(double value, string &out) {
|
||||
if (std::isfinite(value)) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%.17g", value);
|
||||
out += buf;
|
||||
} else {
|
||||
out += "null";
|
||||
}
|
||||
}
|
||||
|
||||
static void dump(int value, string &out) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%d", value);
|
||||
out += buf;
|
||||
}
|
||||
|
||||
static void dump(bool value, string &out) {
|
||||
out += value ? "true" : "false";
|
||||
}
|
||||
|
||||
static void dump(const string &value, string &out) {
|
||||
out += '"';
|
||||
for (size_t i = 0; i < value.length(); i++) {
|
||||
const char ch = value[i];
|
||||
if (ch == '\\') {
|
||||
out += "\\\\";
|
||||
} else if (ch == '"') {
|
||||
out += "\\\"";
|
||||
} else if (ch == '\b') {
|
||||
out += "\\b";
|
||||
} else if (ch == '\f') {
|
||||
out += "\\f";
|
||||
} else if (ch == '\n') {
|
||||
out += "\\n";
|
||||
} else if (ch == '\r') {
|
||||
out += "\\r";
|
||||
} else if (ch == '\t') {
|
||||
out += "\\t";
|
||||
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof buf, "\\u%04x", ch);
|
||||
out += buf;
|
||||
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
|
||||
out += "\\u2028";
|
||||
i += 2;
|
||||
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
|
||||
out += "\\u2029";
|
||||
i += 2;
|
||||
} else {
|
||||
out += ch;
|
||||
}
|
||||
}
|
||||
out += '"';
|
||||
}
|
||||
|
||||
static void dump(const Json::array &values, string &out) {
|
||||
bool first = true;
|
||||
out += "[";
|
||||
for (const auto &value : values) {
|
||||
if (!first)
|
||||
out += ", ";
|
||||
value.dump(out);
|
||||
first = false;
|
||||
}
|
||||
out += "]";
|
||||
}
|
||||
|
||||
static void dump(const Json::object &values, string &out) {
|
||||
bool first = true;
|
||||
out += "{";
|
||||
for (const auto &kv : values) {
|
||||
if (!first)
|
||||
out += ", ";
|
||||
dump(kv.first, out);
|
||||
out += ": ";
|
||||
kv.second.dump(out);
|
||||
first = false;
|
||||
}
|
||||
out += "}";
|
||||
}
|
||||
|
||||
void Json::dump(string &out) const {
|
||||
m_ptr->dump(out);
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Value wrappers
|
||||
*/
|
||||
|
||||
template <Json::Type tag, typename T>
|
||||
class Value : public JsonValue {
|
||||
protected:
|
||||
|
||||
// Constructors
|
||||
explicit Value(const T &value) : m_value(value) {}
|
||||
explicit Value(T &&value) : m_value(move(value)) {}
|
||||
|
||||
// Get type tag
|
||||
Json::Type type() const override {
|
||||
return tag;
|
||||
}
|
||||
|
||||
// Comparisons
|
||||
bool equals(const JsonValue * other) const override {
|
||||
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
|
||||
}
|
||||
bool less(const JsonValue * other) const override {
|
||||
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
|
||||
}
|
||||
|
||||
const T m_value;
|
||||
void dump(string &out) const override { json11::dump(m_value, out); }
|
||||
};
|
||||
|
||||
class JsonDouble final : public Value<Json::NUMBER, double> {
|
||||
double number_value() const override { return m_value; }
|
||||
int int_value() const override { return static_cast<int>(m_value); }
|
||||
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||
public:
|
||||
explicit JsonDouble(double value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonInt final : public Value<Json::NUMBER, int> {
|
||||
double number_value() const override { return m_value; }
|
||||
int int_value() const override { return m_value; }
|
||||
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||
public:
|
||||
explicit JsonInt(int value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonBoolean final : public Value<Json::BOOL, bool> {
|
||||
bool bool_value() const override { return m_value; }
|
||||
public:
|
||||
explicit JsonBoolean(bool value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonString final : public Value<Json::STRING, string> {
|
||||
const string &string_value() const override { return m_value; }
|
||||
public:
|
||||
explicit JsonString(const string &value) : Value(value) {}
|
||||
explicit JsonString(string &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonArray final : public Value<Json::ARRAY, Json::array> {
|
||||
const Json::array &array_items() const override { return m_value; }
|
||||
const Json & operator[](size_t i) const override;
|
||||
public:
|
||||
explicit JsonArray(const Json::array &value) : Value(value) {}
|
||||
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonObject final : public Value<Json::OBJECT, Json::object> {
|
||||
const Json::object &object_items() const override { return m_value; }
|
||||
const Json & operator[](const string &key) const override;
|
||||
public:
|
||||
explicit JsonObject(const Json::object &value) : Value(value) {}
|
||||
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonNull final : public Value<Json::NUL, NullStruct> {
|
||||
public:
|
||||
JsonNull() : Value({}) {}
|
||||
};
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Static globals - static-init-safe
|
||||
*/
|
||||
struct Statics {
|
||||
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
|
||||
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
|
||||
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
|
||||
const string empty_string;
|
||||
const vector<Json> empty_vector;
|
||||
const map<string, Json> empty_map;
|
||||
Statics() {}
|
||||
};
|
||||
|
||||
static const Statics & statics() {
|
||||
static const Statics s {};
|
||||
return s;
|
||||
}
|
||||
|
||||
static const Json & static_null() {
|
||||
// This has to be separate, not in Statics, because Json() accesses statics().null.
|
||||
static const Json json_null;
|
||||
return json_null;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Constructors
|
||||
*/
|
||||
|
||||
Json::Json() noexcept : m_ptr(statics().null) {}
|
||||
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
|
||||
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
|
||||
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
|
||||
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
|
||||
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
|
||||
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
|
||||
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
|
||||
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
|
||||
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Accessors
|
||||
*/
|
||||
|
||||
Json::Type Json::type() const { return m_ptr->type(); }
|
||||
double Json::number_value() const { return m_ptr->number_value(); }
|
||||
int Json::int_value() const { return m_ptr->int_value(); }
|
||||
bool Json::bool_value() const { return m_ptr->bool_value(); }
|
||||
const string & Json::string_value() const { return m_ptr->string_value(); }
|
||||
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
|
||||
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
|
||||
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
|
||||
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
|
||||
|
||||
double JsonValue::number_value() const { return 0; }
|
||||
int JsonValue::int_value() const { return 0; }
|
||||
bool JsonValue::bool_value() const { return false; }
|
||||
const string & JsonValue::string_value() const { return statics().empty_string; }
|
||||
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
|
||||
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
|
||||
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
|
||||
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
|
||||
|
||||
const Json & JsonObject::operator[] (const string &key) const {
|
||||
auto iter = m_value.find(key);
|
||||
return (iter == m_value.end()) ? static_null() : iter->second;
|
||||
}
|
||||
const Json & JsonArray::operator[] (size_t i) const {
|
||||
if (i >= m_value.size()) return static_null();
|
||||
else return m_value[i];
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Comparison
|
||||
*/
|
||||
|
||||
bool Json::operator== (const Json &other) const {
|
||||
if (m_ptr == other.m_ptr)
|
||||
return true;
|
||||
if (m_ptr->type() != other.m_ptr->type())
|
||||
return false;
|
||||
|
||||
return m_ptr->equals(other.m_ptr.get());
|
||||
}
|
||||
|
||||
bool Json::operator< (const Json &other) const {
|
||||
if (m_ptr == other.m_ptr)
|
||||
return false;
|
||||
if (m_ptr->type() != other.m_ptr->type())
|
||||
return m_ptr->type() < other.m_ptr->type();
|
||||
|
||||
return m_ptr->less(other.m_ptr.get());
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Parsing
|
||||
*/
|
||||
|
||||
/* esc(c)
|
||||
*
|
||||
* Format char c suitable for printing in an error message.
|
||||
*/
|
||||
static inline string esc(char c) {
|
||||
char buf[12];
|
||||
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
|
||||
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
|
||||
} else {
|
||||
snprintf(buf, sizeof buf, "(%d)", c);
|
||||
}
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
static inline bool in_range(long x, long lower, long upper) {
|
||||
return (x >= lower && x <= upper);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/* JsonParser
|
||||
*
|
||||
* Object that tracks all state of an in-progress parse.
|
||||
*/
|
||||
struct JsonParser final {
|
||||
|
||||
/* State
|
||||
*/
|
||||
const string &str;
|
||||
size_t i;
|
||||
string &err;
|
||||
bool failed;
|
||||
const JsonParse strategy;
|
||||
|
||||
/* fail(msg, err_ret = Json())
|
||||
*
|
||||
* Mark this parse as failed.
|
||||
*/
|
||||
Json fail(string &&msg) {
|
||||
return fail(move(msg), Json());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T fail(string &&msg, const T err_ret) {
|
||||
if (!failed)
|
||||
err = std::move(msg);
|
||||
failed = true;
|
||||
return err_ret;
|
||||
}
|
||||
|
||||
/* consume_whitespace()
|
||||
*
|
||||
* Advance until the current character is non-whitespace.
|
||||
*/
|
||||
void consume_whitespace() {
|
||||
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
|
||||
i++;
|
||||
}
|
||||
|
||||
/* consume_comment()
|
||||
*
|
||||
* Advance comments (c-style inline and multiline).
|
||||
*/
|
||||
bool consume_comment() {
|
||||
bool comment_found = false;
|
||||
if (str[i] == '/') {
|
||||
i++;
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input after start of comment", false);
|
||||
if (str[i] == '/') { // inline comment
|
||||
i++;
|
||||
// advance until next line, or end of input
|
||||
while (i < str.size() && str[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
comment_found = true;
|
||||
}
|
||||
else if (str[i] == '*') { // multiline comment
|
||||
i++;
|
||||
if (i > str.size()-2)
|
||||
return fail("unexpected end of input inside multi-line comment", false);
|
||||
// advance until closing tokens
|
||||
while (!(str[i] == '*' && str[i+1] == '/')) {
|
||||
i++;
|
||||
if (i > str.size()-2)
|
||||
return fail(
|
||||
"unexpected end of input inside multi-line comment", false);
|
||||
}
|
||||
i += 2;
|
||||
comment_found = true;
|
||||
}
|
||||
else
|
||||
return fail("malformed comment", false);
|
||||
}
|
||||
return comment_found;
|
||||
}
|
||||
|
||||
/* consume_garbage()
|
||||
*
|
||||
* Advance until the current character is non-whitespace and non-comment.
|
||||
*/
|
||||
void consume_garbage() {
|
||||
consume_whitespace();
|
||||
if(strategy == JsonParse::COMMENTS) {
|
||||
bool comment_found = false;
|
||||
do {
|
||||
comment_found = consume_comment();
|
||||
if (failed) return;
|
||||
consume_whitespace();
|
||||
}
|
||||
while(comment_found);
|
||||
}
|
||||
}
|
||||
|
||||
/* get_next_token()
|
||||
*
|
||||
* Return the next non-whitespace character. If the end of the input is reached,
|
||||
* flag an error and return 0.
|
||||
*/
|
||||
char get_next_token() {
|
||||
consume_garbage();
|
||||
if (failed) return (char)0;
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input", (char)0);
|
||||
|
||||
return str[i++];
|
||||
}
|
||||
|
||||
/* encode_utf8(pt, out)
|
||||
*
|
||||
* Encode pt as UTF-8 and add it to out.
|
||||
*/
|
||||
void encode_utf8(long pt, string & out) {
|
||||
if (pt < 0)
|
||||
return;
|
||||
|
||||
if (pt < 0x80) {
|
||||
out += static_cast<char>(pt);
|
||||
} else if (pt < 0x800) {
|
||||
out += static_cast<char>((pt >> 6) | 0xC0);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
} else if (pt < 0x10000) {
|
||||
out += static_cast<char>((pt >> 12) | 0xE0);
|
||||
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
} else {
|
||||
out += static_cast<char>((pt >> 18) | 0xF0);
|
||||
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
|
||||
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_string()
|
||||
*
|
||||
* Parse a string, starting at the current position.
|
||||
*/
|
||||
string parse_string() {
|
||||
string out;
|
||||
long last_escaped_codepoint = -1;
|
||||
while (true) {
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input in string", "");
|
||||
|
||||
char ch = str[i++];
|
||||
|
||||
if (ch == '"') {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (in_range(ch, 0, 0x1f))
|
||||
return fail("unescaped " + esc(ch) + " in string", "");
|
||||
|
||||
// The usual case: non-escaped characters
|
||||
if (ch != '\\') {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = -1;
|
||||
out += ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle escapes
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input in string", "");
|
||||
|
||||
ch = str[i++];
|
||||
|
||||
if (ch == 'u') {
|
||||
// Extract 4-byte escape sequence
|
||||
string esc = str.substr(i, 4);
|
||||
// Explicitly check length of the substring. The following loop
|
||||
// relies on std::string returning the terminating NUL when
|
||||
// accessing str[length]. Checking here reduces brittleness.
|
||||
if (esc.length() < 4) {
|
||||
return fail("bad \\u escape: " + esc, "");
|
||||
}
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
|
||||
&& !in_range(esc[j], '0', '9'))
|
||||
return fail("bad \\u escape: " + esc, "");
|
||||
}
|
||||
|
||||
long codepoint = strtol(esc.data(), nullptr, 16);
|
||||
|
||||
// JSON specifies that characters outside the BMP shall be encoded as a pair
|
||||
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
|
||||
// whether we're in the middle of such a beast: the previous codepoint was an
|
||||
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
|
||||
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
|
||||
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
|
||||
// Reassemble the two surrogate pairs into one astral-plane character, per
|
||||
// the UTF-16 algorithm.
|
||||
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
|
||||
| (codepoint - 0xDC00)) + 0x10000, out);
|
||||
last_escaped_codepoint = -1;
|
||||
} else {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = codepoint;
|
||||
}
|
||||
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = -1;
|
||||
|
||||
if (ch == 'b') {
|
||||
out += '\b';
|
||||
} else if (ch == 'f') {
|
||||
out += '\f';
|
||||
} else if (ch == 'n') {
|
||||
out += '\n';
|
||||
} else if (ch == 'r') {
|
||||
out += '\r';
|
||||
} else if (ch == 't') {
|
||||
out += '\t';
|
||||
} else if (ch == '"' || ch == '\\' || ch == '/') {
|
||||
out += ch;
|
||||
} else {
|
||||
return fail("invalid escape character " + esc(ch), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_number()
|
||||
*
|
||||
* Parse a double.
|
||||
*/
|
||||
Json parse_number() {
|
||||
size_t start_pos = i;
|
||||
|
||||
if (str[i] == '-')
|
||||
i++;
|
||||
|
||||
// Integer part
|
||||
if (str[i] == '0') {
|
||||
i++;
|
||||
if (in_range(str[i], '0', '9'))
|
||||
return fail("leading 0s not permitted in numbers");
|
||||
} else if (in_range(str[i], '1', '9')) {
|
||||
i++;
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
} else {
|
||||
return fail("invalid " + esc(str[i]) + " in number");
|
||||
}
|
||||
|
||||
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
|
||||
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
|
||||
return std::atoi(str.c_str() + start_pos);
|
||||
}
|
||||
|
||||
// Decimal part
|
||||
if (str[i] == '.') {
|
||||
i++;
|
||||
if (!in_range(str[i], '0', '9'))
|
||||
return fail("at least one digit required in fractional part");
|
||||
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
}
|
||||
|
||||
// Exponent part
|
||||
if (str[i] == 'e' || str[i] == 'E') {
|
||||
i++;
|
||||
|
||||
if (str[i] == '+' || str[i] == '-')
|
||||
i++;
|
||||
|
||||
if (!in_range(str[i], '0', '9'))
|
||||
return fail("at least one digit required in exponent");
|
||||
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
}
|
||||
|
||||
return std::strtod(str.c_str() + start_pos, nullptr);
|
||||
}
|
||||
|
||||
/* expect(str, res)
|
||||
*
|
||||
* Expect that 'str' starts at the character that was just read. If it does, advance
|
||||
* the input and return res. If not, flag an error.
|
||||
*/
|
||||
Json expect(const string &expected, Json res) {
|
||||
assert(i != 0);
|
||||
i--;
|
||||
if (str.compare(i, expected.length(), expected) == 0) {
|
||||
i += expected.length();
|
||||
return res;
|
||||
} else {
|
||||
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_json()
|
||||
*
|
||||
* Parse a JSON object.
|
||||
*/
|
||||
Json parse_json(int depth) {
|
||||
if (depth > max_depth) {
|
||||
return fail("exceeded maximum nesting depth");
|
||||
}
|
||||
|
||||
char ch = get_next_token();
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
if (ch == '-' || (ch >= '0' && ch <= '9')) {
|
||||
i--;
|
||||
return parse_number();
|
||||
}
|
||||
|
||||
if (ch == 't')
|
||||
return expect("true", true);
|
||||
|
||||
if (ch == 'f')
|
||||
return expect("false", false);
|
||||
|
||||
if (ch == 'n')
|
||||
return expect("null", Json());
|
||||
|
||||
if (ch == '"')
|
||||
return parse_string();
|
||||
|
||||
if (ch == '{') {
|
||||
map<string, Json> data;
|
||||
ch = get_next_token();
|
||||
if (ch == '}')
|
||||
return data;
|
||||
|
||||
while (1) {
|
||||
if (ch != '"')
|
||||
return fail("expected '\"' in object, got " + esc(ch));
|
||||
|
||||
string key = parse_string();
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch != ':')
|
||||
return fail("expected ':' in object, got " + esc(ch));
|
||||
|
||||
data[std::move(key)] = parse_json(depth + 1);
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch == '}')
|
||||
break;
|
||||
if (ch != ',')
|
||||
return fail("expected ',' in object, got " + esc(ch));
|
||||
|
||||
ch = get_next_token();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
if (ch == '[') {
|
||||
vector<Json> data;
|
||||
ch = get_next_token();
|
||||
if (ch == ']')
|
||||
return data;
|
||||
|
||||
while (1) {
|
||||
i--;
|
||||
data.push_back(parse_json(depth + 1));
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch == ']')
|
||||
break;
|
||||
if (ch != ',')
|
||||
return fail("expected ',' in list, got " + esc(ch));
|
||||
|
||||
ch = get_next_token();
|
||||
(void)ch;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
return fail("expected value, got " + esc(ch));
|
||||
}
|
||||
};
|
||||
}//namespace {
|
||||
|
||||
Json Json::parse(const string &in, string &err, JsonParse strategy) {
|
||||
JsonParser parser { in, 0, err, false, strategy };
|
||||
Json result = parser.parse_json(0);
|
||||
|
||||
// Check for any trailing garbage
|
||||
parser.consume_garbage();
|
||||
if (parser.failed)
|
||||
return Json();
|
||||
if (parser.i != in.size())
|
||||
return parser.fail("unexpected trailing " + esc(in[parser.i]));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Documented in json11.hpp
|
||||
vector<Json> Json::parse_multi(const string &in,
|
||||
std::string::size_type &parser_stop_pos,
|
||||
string &err,
|
||||
JsonParse strategy) {
|
||||
JsonParser parser { in, 0, err, false, strategy };
|
||||
parser_stop_pos = 0;
|
||||
vector<Json> json_vec;
|
||||
while (parser.i != in.size() && !parser.failed) {
|
||||
json_vec.push_back(parser.parse_json(0));
|
||||
if (parser.failed)
|
||||
break;
|
||||
|
||||
// Check for another object
|
||||
parser.consume_garbage();
|
||||
if (parser.failed)
|
||||
break;
|
||||
parser_stop_pos = parser.i;
|
||||
}
|
||||
return json_vec;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Shape-checking
|
||||
*/
|
||||
|
||||
bool Json::has_shape(const shape & types, string & err) const {
|
||||
if (!is_object()) {
|
||||
err = "expected JSON object, got " + dump();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto & item : types) {
|
||||
if ((*this)[item.first].type() != item.second) {
|
||||
err = "bad type for " + item.first + " in " + dump();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace json11
|
||||
232
examples/validator/json11.hpp
Normal file
232
examples/validator/json11.hpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/* json11
|
||||
*
|
||||
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
|
||||
*
|
||||
* The core object provided by the library is json11::Json. A Json object represents any JSON
|
||||
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
|
||||
* object (std::map).
|
||||
*
|
||||
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
|
||||
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
|
||||
* Json::parse (static) to parse a std::string as a Json object.
|
||||
*
|
||||
* Internally, the various types of Json object are represented by the JsonValue class
|
||||
* hierarchy.
|
||||
*
|
||||
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
|
||||
* so some JSON implementations distinguish between integers and floating-point numbers, while
|
||||
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
|
||||
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
|
||||
* to JSON that will be *silently* changed by a round-trip through those implementations.
|
||||
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
|
||||
* provides integer helpers.
|
||||
*
|
||||
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
|
||||
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
|
||||
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
|
||||
* will be exact for +/- 275 years.)
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2013 Dropbox, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <initializer_list>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER <= 1800 // VS 2013
|
||||
#ifndef noexcept
|
||||
#define noexcept throw()
|
||||
#endif
|
||||
|
||||
#ifndef snprintf
|
||||
#define snprintf _snprintf_s
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace json11 {
|
||||
|
||||
enum JsonParse {
|
||||
STANDARD, COMMENTS
|
||||
};
|
||||
|
||||
class JsonValue;
|
||||
|
||||
class Json final {
|
||||
public:
|
||||
// Types
|
||||
enum Type {
|
||||
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
|
||||
};
|
||||
|
||||
// Array and object typedefs
|
||||
typedef std::vector<Json> array;
|
||||
typedef std::map<std::string, Json> object;
|
||||
|
||||
// Constructors for the various types of JSON value.
|
||||
Json() noexcept; // NUL
|
||||
Json(std::nullptr_t) noexcept; // NUL
|
||||
Json(double value); // NUMBER
|
||||
Json(int value); // NUMBER
|
||||
Json(bool value); // BOOL
|
||||
Json(const std::string &value); // STRING
|
||||
Json(std::string &&value); // STRING
|
||||
Json(const char * value); // STRING
|
||||
Json(const array &values); // ARRAY
|
||||
Json(array &&values); // ARRAY
|
||||
Json(const object &values); // OBJECT
|
||||
Json(object &&values); // OBJECT
|
||||
|
||||
// Implicit constructor: anything with a to_json() function.
|
||||
template <class T, class = decltype(&T::to_json)>
|
||||
Json(const T & t) : Json(t.to_json()) {}
|
||||
|
||||
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
|
||||
template <class M, typename std::enable_if<
|
||||
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
|
||||
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
|
||||
int>::type = 0>
|
||||
Json(const M & m) : Json(object(m.begin(), m.end())) {}
|
||||
|
||||
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
|
||||
template <class V, typename std::enable_if<
|
||||
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
|
||||
int>::type = 0>
|
||||
Json(const V & v) : Json(array(v.begin(), v.end())) {}
|
||||
|
||||
// This prevents Json(some_pointer) from accidentally producing a bool. Use
|
||||
// Json(bool(some_pointer)) if that behavior is desired.
|
||||
Json(void *) = delete;
|
||||
|
||||
// Accessors
|
||||
Type type() const;
|
||||
|
||||
bool is_null() const { return type() == NUL; }
|
||||
bool is_number() const { return type() == NUMBER; }
|
||||
bool is_bool() const { return type() == BOOL; }
|
||||
bool is_string() const { return type() == STRING; }
|
||||
bool is_array() const { return type() == ARRAY; }
|
||||
bool is_object() const { return type() == OBJECT; }
|
||||
|
||||
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
|
||||
// distinguish between integer and non-integer numbers - number_value() and int_value()
|
||||
// can both be applied to a NUMBER-typed object.
|
||||
double number_value() const;
|
||||
int int_value() const;
|
||||
|
||||
// Return the enclosed value if this is a boolean, false otherwise.
|
||||
bool bool_value() const;
|
||||
// Return the enclosed string if this is a string, "" otherwise.
|
||||
const std::string &string_value() const;
|
||||
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
|
||||
const array &array_items() const;
|
||||
// Return the enclosed std::map if this is an object, or an empty map otherwise.
|
||||
const object &object_items() const;
|
||||
|
||||
// Return a reference to arr[i] if this is an array, Json() otherwise.
|
||||
const Json & operator[](size_t i) const;
|
||||
// Return a reference to obj[key] if this is an object, Json() otherwise.
|
||||
const Json & operator[](const std::string &key) const;
|
||||
|
||||
// Serialize.
|
||||
void dump(std::string &out) const;
|
||||
std::string dump() const {
|
||||
std::string out;
|
||||
dump(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Parse. If parse fails, return Json() and assign an error message to err.
|
||||
static Json parse(const std::string & in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD);
|
||||
static Json parse(const char * in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD) {
|
||||
if (in) {
|
||||
return parse(std::string(in), err, strategy);
|
||||
} else {
|
||||
err = "null input";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
// Parse multiple objects, concatenated or separated by whitespace
|
||||
static std::vector<Json> parse_multi(
|
||||
const std::string & in,
|
||||
std::string::size_type & parser_stop_pos,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD);
|
||||
|
||||
static inline std::vector<Json> parse_multi(
|
||||
const std::string & in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD) {
|
||||
std::string::size_type parser_stop_pos;
|
||||
return parse_multi(in, parser_stop_pos, err, strategy);
|
||||
}
|
||||
|
||||
bool operator== (const Json &rhs) const;
|
||||
bool operator< (const Json &rhs) const;
|
||||
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
|
||||
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
|
||||
bool operator> (const Json &rhs) const { return (rhs < *this); }
|
||||
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
|
||||
|
||||
/* has_shape(types, err)
|
||||
*
|
||||
* Return true if this is a JSON object and, for each item in types, has a field of
|
||||
* the given type. If not, return false and set err to a descriptive message.
|
||||
*/
|
||||
typedef std::initializer_list<std::pair<std::string, Type>> shape;
|
||||
bool has_shape(const shape & types, std::string & err) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<JsonValue> m_ptr;
|
||||
};
|
||||
|
||||
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
|
||||
class JsonValue {
|
||||
protected:
|
||||
friend class Json;
|
||||
friend class JsonInt;
|
||||
friend class JsonDouble;
|
||||
virtual Json::Type type() const = 0;
|
||||
virtual bool equals(const JsonValue * other) const = 0;
|
||||
virtual bool less(const JsonValue * other) const = 0;
|
||||
virtual void dump(std::string &out) const = 0;
|
||||
virtual double number_value() const;
|
||||
virtual int int_value() const;
|
||||
virtual bool bool_value() const;
|
||||
virtual const std::string &string_value() const;
|
||||
virtual const Json::array &array_items() const;
|
||||
virtual const Json &operator[](size_t i) const;
|
||||
virtual const Json::object &object_items() const;
|
||||
virtual const Json &operator[](const std::string &key) const;
|
||||
virtual ~JsonValue() {}
|
||||
};
|
||||
|
||||
} // namespace json11
|
||||
@@ -1,160 +0,0 @@
|
||||
#include <json-schema.hpp>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
namespace json_schema_draft4
|
||||
{
|
||||
|
||||
json draft4_schema_builtin = R"( {
|
||||
"id": "http://json-schema.org/draft-04/schema#",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"description": "Core schema meta-schema",
|
||||
"definitions": {
|
||||
"schemaArray": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#" }
|
||||
},
|
||||
"positiveInteger": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"positiveIntegerDefault0": {
|
||||
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
|
||||
},
|
||||
"simpleTypes": {
|
||||
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
|
||||
},
|
||||
"stringArray": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {},
|
||||
"multipleOf": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"exclusiveMinimum": true
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number"
|
||||
},
|
||||
"exclusiveMaximum": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"minimum": {
|
||||
"type": "number"
|
||||
},
|
||||
"exclusiveMinimum": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"maxLength": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"pattern": {
|
||||
"type": "string",
|
||||
"format": "regex"
|
||||
},
|
||||
"additionalItems": {
|
||||
"anyOf": [
|
||||
{ "type": "boolean" },
|
||||
{ "$ref": "#" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#" },
|
||||
{ "$ref": "#/definitions/schemaArray" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"maxItems": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"uniqueItems": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"maxProperties": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"required": { "$ref": "#/definitions/stringArray" },
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{ "type": "boolean" },
|
||||
{ "$ref": "#" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"definitions": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"patternProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#" },
|
||||
{ "$ref": "#/definitions/stringArray" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"enum": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"type": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#/definitions/simpleTypes" },
|
||||
{
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/simpleTypes" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"allOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"anyOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"oneOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"not": { "$ref": "#" }
|
||||
},
|
||||
"dependencies": {
|
||||
"exclusiveMaximum": [ "maximum" ],
|
||||
"exclusiveMinimum": [ "minimum" ]
|
||||
},
|
||||
"default": {}
|
||||
} )"_json;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
* Modern C++ JSON schema validator
|
||||
*
|
||||
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*
|
||||
* Copyright (c) 2016 Patrick Boettcher <patrick.boettcher@posteo.de>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef NLOHMANN_JSON_SCHEMA_HPP__
|
||||
#define NLOHMANN_JSON_SCHEMA_HPP__
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef JSON_SCHEMA_VALIDATOR_EXPORTS
|
||||
# define JSON_SCHEMA_VALIDATOR_API __declspec(dllexport)
|
||||
# else
|
||||
# define JSON_SCHEMA_VALIDATOR_API __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define JSON_SCHEMA_VALIDATOR_API
|
||||
#endif
|
||||
|
||||
#include <json.hpp>
|
||||
|
||||
// make yourself a home - welcome to nlohmann's namespace
|
||||
namespace nlohmann
|
||||
{
|
||||
|
||||
// a class representing a JSON-pointer RFC6901
|
||||
//
|
||||
// examples of JSON pointers
|
||||
//
|
||||
// # - root of the current document
|
||||
// #item - refers to the object which is identified ("id") by `item`
|
||||
// in the current document
|
||||
// #/path/to/element
|
||||
// - refers to the element in /path/to from the root-document
|
||||
//
|
||||
//
|
||||
// The json_pointer-class stores everything in a string, which might seem bizarre
|
||||
// as parsing is done from a string to a string, but from_string() is also
|
||||
// doing some formatting.
|
||||
//
|
||||
// TODO
|
||||
// ~ and % - codec
|
||||
// needs testing and clarification regarding the '#' at the beginning
|
||||
|
||||
class json_pointer
|
||||
{
|
||||
std::string str_;
|
||||
|
||||
void from_string(const std::string &r);
|
||||
|
||||
public:
|
||||
json_pointer(const std::string &s = "")
|
||||
{
|
||||
from_string(s);
|
||||
}
|
||||
|
||||
void append(const std::string &elem)
|
||||
{
|
||||
str_.append(elem);
|
||||
}
|
||||
|
||||
const std::string &to_string() const
|
||||
{
|
||||
return str_;
|
||||
}
|
||||
};
|
||||
|
||||
// A class representing a JSON-URI for schemas derived from
|
||||
// section 8 of JSON Schema: A Media Type for Describing JSON Documents
|
||||
// draft-wright-json-schema-00
|
||||
//
|
||||
// New URIs can be derived from it using the derive()-method.
|
||||
// This is useful for resolving refs or subschema-IDs in json-schemas.
|
||||
//
|
||||
// This is done implement the requirements described in section 8.2.
|
||||
//
|
||||
class JSON_SCHEMA_VALIDATOR_API json_uri
|
||||
{
|
||||
std::string urn_;
|
||||
|
||||
std::string proto_;
|
||||
std::string hostname_;
|
||||
std::string path_;
|
||||
json_pointer pointer_;
|
||||
|
||||
protected:
|
||||
// decodes a JSON uri and replaces all or part of the currently stored values
|
||||
void from_string(const std::string &uri);
|
||||
|
||||
std::tuple<std::string, std::string, std::string, std::string, std::string> tie() const
|
||||
{
|
||||
return std::tie(urn_, proto_, hostname_, path_, pointer_.to_string());
|
||||
}
|
||||
|
||||
public:
|
||||
json_uri(const std::string &uri)
|
||||
{
|
||||
from_string(uri);
|
||||
}
|
||||
|
||||
const std::string protocol() const { return proto_; }
|
||||
const std::string hostname() const { return hostname_; }
|
||||
const std::string path() const { return path_; }
|
||||
const json_pointer pointer() const { return pointer_; }
|
||||
|
||||
const std::string url() const;
|
||||
|
||||
// decode and encode strings for ~ and % escape sequences
|
||||
static std::string unescape(const std::string &);
|
||||
static std::string escape(const std::string &);
|
||||
|
||||
// create a new json_uri based in this one and the given uri
|
||||
// resolves relative changes (pathes or pointers) and resets part if proto or hostname changes
|
||||
json_uri derive(const std::string &uri) const
|
||||
{
|
||||
json_uri u = *this;
|
||||
u.from_string(uri);
|
||||
return u;
|
||||
}
|
||||
|
||||
// append a pointer-field to the pointer-part of this uri
|
||||
json_uri append(const std::string &field) const
|
||||
{
|
||||
json_uri u = *this;
|
||||
u.pointer_.append("/" + field);
|
||||
return u;
|
||||
}
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
friend bool operator<(const json_uri &l, const json_uri &r)
|
||||
{
|
||||
return l.tie() < r.tie();
|
||||
}
|
||||
|
||||
friend bool operator==(const json_uri &l, const json_uri &r)
|
||||
{
|
||||
return l.tie() == r.tie();
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const json_uri &u);
|
||||
};
|
||||
|
||||
namespace json_schema_draft4
|
||||
{
|
||||
|
||||
extern json draft4_schema_builtin;
|
||||
|
||||
class JSON_SCHEMA_VALIDATOR_API json_validator
|
||||
{
|
||||
std::vector<std::shared_ptr<json>> schema_store_;
|
||||
std::shared_ptr<json> root_schema_;
|
||||
std::function<void(const json_uri &, json &)> schema_loader_ = nullptr;
|
||||
std::function<void(const std::string &, const std::string &)> format_check_ = nullptr;
|
||||
|
||||
std::map<json_uri, const json *> schema_refs_;
|
||||
|
||||
void validate(const json &instance, const json &schema_, const std::string &name);
|
||||
void validate_array(const json &instance, const json &schema_, const std::string &name);
|
||||
void validate_object(const json &instance, const json &schema_, const std::string &name);
|
||||
void validate_string(const json &instance, const json &schema, const std::string &name);
|
||||
|
||||
void insert_schema(const json &input, const json_uri &id);
|
||||
|
||||
public:
|
||||
json_validator(std::function<void(const json_uri &, json &)> loader = nullptr,
|
||||
std::function<void(const std::string &, const std::string &)> format = nullptr)
|
||||
: schema_loader_(loader), format_check_(format)
|
||||
{
|
||||
}
|
||||
|
||||
// insert and set a root-schema
|
||||
void set_root_schema(const json &);
|
||||
|
||||
// validate a json-document based on the root-schema
|
||||
void validate(const json &instance);
|
||||
};
|
||||
|
||||
} // json_schema_draft4
|
||||
} // nlohmann
|
||||
|
||||
#endif /* NLOHMANN_JSON_SCHEMA_HPP__ */
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Modern C++ JSON schema validator
|
||||
*
|
||||
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*
|
||||
* Copyright (c) 2016 Patrick Boettcher <patrick.boettcher@posteo.de>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "json-schema.hpp"
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
|
||||
void json_pointer::from_string(const std::string &r)
|
||||
{
|
||||
str_ = "#";
|
||||
|
||||
if (r.size() == 0)
|
||||
return;
|
||||
|
||||
if (r[0] != '#')
|
||||
throw std::invalid_argument("not a valid JSON pointer - missing # at the beginning");
|
||||
|
||||
if (r.size() == 1)
|
||||
return;
|
||||
|
||||
std::size_t pos = 1;
|
||||
|
||||
do {
|
||||
std::size_t next = r.find('/', pos + 1);
|
||||
str_.append(r.substr(pos, next - pos));
|
||||
pos = next;
|
||||
} while (pos != std::string::npos);
|
||||
}
|
||||
|
||||
void json_uri::from_string(const std::string &uri)
|
||||
{
|
||||
// if it is an urn take it as it is - maybe there is more to be done
|
||||
if (uri.find("urn:") == 0) {
|
||||
urn_ = uri;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string pointer = "#"; // default pointer is the root
|
||||
|
||||
// first split the URI into URL and JSON-pointer
|
||||
auto pointer_separator = uri.find('#');
|
||||
if (pointer_separator != std::string::npos) // and extract the JSON-pointer-string if found
|
||||
pointer = uri.substr(pointer_separator);
|
||||
|
||||
// the rest is an URL
|
||||
std::string url = uri.substr(0, pointer_separator);
|
||||
if (url.size()) { // if an URL is part of the URI
|
||||
|
||||
std::size_t pos = 0;
|
||||
auto proto = url.find("://", pos);
|
||||
if (proto != std::string::npos) { // extract the protocol
|
||||
proto_ = url.substr(pos, proto - pos);
|
||||
pos = 3 + proto; // 3 == "://"
|
||||
|
||||
auto hostname = url.find("/", pos);
|
||||
if (hostname != std::string::npos) { // and the hostname (no proto without hostname)
|
||||
hostname_ = url.substr(pos, hostname - pos);
|
||||
pos = hostname;
|
||||
}
|
||||
}
|
||||
|
||||
// the rest is the path
|
||||
auto path = url.substr(pos);
|
||||
if (path[0] == '/') // if it starts with a / it is root-path
|
||||
path_ = path;
|
||||
else { // otherwise it is a subfolder
|
||||
// HACK(syoyo): Force append '/' for glTF json schemas
|
||||
path_ = path;
|
||||
//path_.append(path);
|
||||
}
|
||||
|
||||
pointer_ = json_pointer("");
|
||||
}
|
||||
|
||||
if (pointer.size() > 0)
|
||||
pointer_ = pointer;
|
||||
}
|
||||
|
||||
const std::string json_uri::url() const
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
if (proto_.size() > 0)
|
||||
s << proto_ << "://";
|
||||
|
||||
s << hostname_
|
||||
<< path_;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::string json_uri::to_string() const
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
s << urn_
|
||||
<< url()
|
||||
<< pointer_.to_string();
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const json_uri &u)
|
||||
{
|
||||
return os << u.to_string();
|
||||
}
|
||||
|
||||
std::string json_uri::unescape(const std::string &src)
|
||||
{
|
||||
std::string l = src;
|
||||
std::size_t pos = src.size() - 1;
|
||||
|
||||
do {
|
||||
pos = l.rfind('~', pos);
|
||||
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
if (pos < l.size() - 1) {
|
||||
switch (l[pos + 1]) {
|
||||
case '0':
|
||||
l.replace(pos, 2, "~");
|
||||
break;
|
||||
|
||||
case '1':
|
||||
l.replace(pos, 2, "/");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == 0)
|
||||
break;
|
||||
pos--;
|
||||
} while (pos != std::string::npos);
|
||||
|
||||
// TODO - percent handling
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
std::string json_uri::escape(const std::string &src)
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> chars = {
|
||||
{"~", "~0"},
|
||||
{"/", "~1"},
|
||||
{"%", "%25"}};
|
||||
|
||||
std::string l = src;
|
||||
|
||||
for (const auto &c : chars) {
|
||||
std::size_t pos = 0;
|
||||
do {
|
||||
pos = l.find(c.first, pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
l.replace(pos, 1, c.second);
|
||||
pos += c.second.size();
|
||||
} while (1);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
} // nlohmann
|
||||
@@ -1,738 +0,0 @@
|
||||
/*
|
||||
* Modern C++ JSON schema validator
|
||||
*
|
||||
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*
|
||||
* Copyright (c) 2016 Patrick Boettcher <patrick.boettcher@posteo.de>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <json-schema.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_uri;
|
||||
|
||||
#ifdef JSON_SCHEMA_BOOST_REGEX
|
||||
#include <boost/regex.hpp>
|
||||
#define REGEX_NAMESPACE boost
|
||||
#elif defined(JSON_SCHEMA_NO_REGEX)
|
||||
#define NO_STD_REGEX
|
||||
#else
|
||||
#include <regex>
|
||||
#define REGEX_NAMESPACE std
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class resolver
|
||||
{
|
||||
void resolve(json &schema, json_uri id)
|
||||
{
|
||||
// look for the id-field in this schema
|
||||
auto fid = schema.find("id");
|
||||
|
||||
// found?
|
||||
if (fid != schema.end() &&
|
||||
fid.value().type() == json::value_t::string)
|
||||
id = id.derive(fid.value()); // resolve to a full id with URL + path based on the parent
|
||||
|
||||
// already existing - error
|
||||
if (schema_refs.find(id) != schema_refs.end())
|
||||
throw std::invalid_argument("schema " + id.to_string() + " already present in local resolver");
|
||||
|
||||
// store a raw pointer to this (sub-)schema referenced by its absolute json_uri
|
||||
// this (sub-)schema is part of a schema stored inside schema_store_ so we can the a raw-pointer-ref
|
||||
schema_refs[id] = &schema;
|
||||
|
||||
for (auto i = schema.begin(), end = schema.end(); i != end; ++i) {
|
||||
// FIXME: this inhibits the user adding properties with the key "default"
|
||||
if (i.key() == "default") /* default value can be objects, but are not schemas */
|
||||
continue;
|
||||
|
||||
switch (i.value().type()) {
|
||||
|
||||
case json::value_t::object: // child is object, it is a schema
|
||||
resolve(i.value(), id.append(json_uri::escape(i.key())));
|
||||
break;
|
||||
|
||||
case json::value_t::array: {
|
||||
std::size_t index = 0;
|
||||
auto child_id = id.append(json_uri::escape(i.key()));
|
||||
for (auto &v : i.value()) {
|
||||
if (v.type() == json::value_t::object) // array element is object
|
||||
resolve(v, child_id.append(std::to_string(index)));
|
||||
index++;
|
||||
}
|
||||
} break;
|
||||
|
||||
case json::value_t::string:
|
||||
if (i.key() == "$ref") {
|
||||
json_uri ref = id.derive(i.value());
|
||||
i.value() = ref.to_string();
|
||||
refs.insert(ref);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::set<json_uri> refs;
|
||||
|
||||
public:
|
||||
std::set<json_uri> undefined_refs;
|
||||
|
||||
std::map<json_uri, const json *> schema_refs;
|
||||
|
||||
resolver(json &schema, json_uri id)
|
||||
{
|
||||
// if schema has an id use it as name and to retrieve the namespace (URL)
|
||||
auto fid = schema.find("id");
|
||||
if (fid != schema.end())
|
||||
id = id.derive(fid.value());
|
||||
|
||||
resolve(schema, id);
|
||||
|
||||
// refs now contains all references
|
||||
//
|
||||
// local references should be resolvable inside the same URL
|
||||
//
|
||||
// undefined_refs will only contain external references
|
||||
for (auto r : refs) {
|
||||
if (schema_refs.find(r) == schema_refs.end()) {
|
||||
if (r.url() == id.url()) // same url means referencing a sub-schema
|
||||
// of the same document, which has not been found
|
||||
throw std::invalid_argument("sub-schema " + r.pointer().to_string() +
|
||||
" in schema " + id.to_string() + " not found");
|
||||
undefined_refs.insert(r.url());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void validate_type(const json &schema, const std::string &expected_type, const std::string &name)
|
||||
{
|
||||
const auto &type_it = schema.find("type");
|
||||
if (type_it == schema.end())
|
||||
/* TODO something needs to be done here, I think */
|
||||
return;
|
||||
|
||||
const auto &type_instance = type_it.value();
|
||||
|
||||
// any of the types in this array
|
||||
if (type_instance.type() == json::value_t::array) {
|
||||
if ((std::find(type_instance.begin(),
|
||||
type_instance.end(),
|
||||
expected_type) != type_instance.end()) ||
|
||||
(expected_type == "integer" &&
|
||||
std::find(type_instance.begin(),
|
||||
type_instance.end(),
|
||||
"number") != type_instance.end()))
|
||||
return;
|
||||
|
||||
std::ostringstream s;
|
||||
s << expected_type << " is not any of " << type_instance << " for " << name;
|
||||
throw std::invalid_argument(s.str());
|
||||
|
||||
} else { // type_instance is a string
|
||||
if (type_instance == expected_type ||
|
||||
(type_instance == "number" && expected_type == "integer"))
|
||||
return;
|
||||
|
||||
throw std::invalid_argument(name + " is " + expected_type +
|
||||
", but required type is " + type_instance.get<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
void validate_enum(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
const auto &enum_value = schema.find("enum");
|
||||
if (enum_value == schema.end())
|
||||
return;
|
||||
|
||||
if (std::find(enum_value.value().begin(), enum_value.value().end(), instance) != enum_value.value().end())
|
||||
return;
|
||||
|
||||
std::ostringstream s;
|
||||
s << "invalid enum-value '" << instance << "' "
|
||||
<< "for instance '" << name << "'. Candidates are " << enum_value.value() << ".";
|
||||
|
||||
throw std::invalid_argument(s.str());
|
||||
}
|
||||
|
||||
void validate_boolean(const json & /*instance*/, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "boolean", name);
|
||||
}
|
||||
|
||||
void validate_numeric(const json &schema, const std::string &name, double value)
|
||||
{
|
||||
// multipleOf - if the rest of the division is 0 -> OK
|
||||
const auto &multipleOf = schema.find("multipleOf");
|
||||
if (multipleOf != schema.end()) {
|
||||
if (multipleOf.value().get<double>() != 0.0) {
|
||||
|
||||
double v = value;
|
||||
v /= multipleOf.value().get<double>();
|
||||
|
||||
if (v != (double) (long) v)
|
||||
throw std::out_of_range(name + " is not a multiple ...");
|
||||
}
|
||||
}
|
||||
|
||||
const auto &maximum = schema.find("maximum");
|
||||
if (maximum != schema.end()) {
|
||||
double maxi = maximum.value();
|
||||
auto ex = std::out_of_range(name + " exceeds maximum of " + std::to_string(maxi));
|
||||
if (schema.find("exclusiveMaximum") != schema.end()) {
|
||||
if (value >= maxi)
|
||||
throw ex;
|
||||
} else {
|
||||
if (value > maxi)
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
const auto &minimum = schema.find("minimum");
|
||||
if (minimum != schema.end()) {
|
||||
double mini = minimum.value();
|
||||
auto ex = std::out_of_range(name + " exceeds minimum of " + std::to_string(mini));
|
||||
if (schema.find("exclusiveMinimum") != schema.end()) {
|
||||
if (value <= mini)
|
||||
throw ex;
|
||||
} else {
|
||||
if (value < mini)
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_integer(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "integer", name);
|
||||
validate_numeric(schema, name, instance.get<int>());
|
||||
}
|
||||
|
||||
void validate_unsigned(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "integer", name);
|
||||
validate_numeric(schema, name, instance.get<unsigned>());
|
||||
}
|
||||
|
||||
void validate_float(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "number", name);
|
||||
validate_numeric(schema, name, instance.get<double>());
|
||||
}
|
||||
|
||||
void validate_null(const json & /*instance*/, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "null", name);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
namespace json_schema_draft4
|
||||
{
|
||||
|
||||
void json_validator::insert_schema(const json &input, const json_uri &id)
|
||||
{
|
||||
// allocate create a copy for later storage - if resolving reference works
|
||||
std::shared_ptr<json> schema = std::make_shared<json>(input);
|
||||
|
||||
do {
|
||||
// resolve all local schemas and references
|
||||
resolver r(*schema, id);
|
||||
|
||||
// check whether all undefined schema references can be resolved with existing ones
|
||||
std::set<json_uri> undefined;
|
||||
for (auto &ref : r.undefined_refs)
|
||||
if (schema_refs_.find(ref) == schema_refs_.end()) // exact schema reference not found
|
||||
undefined.insert(ref);
|
||||
|
||||
if (undefined.size() == 0) { // no undefined references
|
||||
// now insert all schema-references
|
||||
// check whether all schema-references are new
|
||||
for (auto &sref : r.schema_refs) {
|
||||
if (schema_refs_.find(sref.first) != schema_refs_.end())
|
||||
// HACK(syoyo): Skip duplicated schema.
|
||||
break;
|
||||
//throw std::invalid_argument("schema " + sref.first.to_string() + " already present in validator.");
|
||||
}
|
||||
// no undefined references and no duplicated schema - store the schema
|
||||
schema_store_.push_back(schema);
|
||||
|
||||
// and insert all references
|
||||
schema_refs_.insert(r.schema_refs.begin(), r.schema_refs.end());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (schema_loader_ == nullptr)
|
||||
throw std::invalid_argument("schema contains undefined references to other schemas, needed schema-loader.");
|
||||
|
||||
for (auto undef : undefined) {
|
||||
json ext;
|
||||
|
||||
schema_loader_(undef, ext);
|
||||
insert_schema(ext, undef.url());
|
||||
}
|
||||
} while (1);
|
||||
|
||||
// store the document root-schema
|
||||
if (id == json_uri("#"))
|
||||
root_schema_ = schema;
|
||||
}
|
||||
|
||||
void json_validator::validate(const json &instance)
|
||||
{
|
||||
if (root_schema_ == nullptr)
|
||||
throw std::invalid_argument("no root-schema has been inserted. Cannot validate an instance without it.");
|
||||
|
||||
validate(instance, *root_schema_, "root");
|
||||
}
|
||||
|
||||
void json_validator::set_root_schema(const json &schema)
|
||||
{
|
||||
insert_schema(schema, json_uri("#"));
|
||||
}
|
||||
|
||||
void json_validator::validate(const json &instance, const json &schema_, const std::string &name)
|
||||
{
|
||||
const json *schema = &schema_;
|
||||
|
||||
// $ref resolution
|
||||
do {
|
||||
const auto &ref = schema->find("$ref");
|
||||
if (ref == schema->end())
|
||||
break;
|
||||
|
||||
auto it = schema_refs_.find(ref.value().get<std::string>());
|
||||
|
||||
if (it == schema_refs_.end())
|
||||
throw std::invalid_argument("schema reference " + ref.value().get<std::string>() + " not found. Make sure all schemas have been inserted before validation.");
|
||||
|
||||
schema = it->second;
|
||||
} while (1); // loop in case of nested refs
|
||||
|
||||
// not
|
||||
const auto attr = schema->find("not");
|
||||
if (attr != schema->end()) {
|
||||
bool ok;
|
||||
|
||||
try {
|
||||
validate(instance, attr.value(), name);
|
||||
ok = false;
|
||||
} catch (std::exception &) {
|
||||
ok = true;
|
||||
}
|
||||
if (!ok)
|
||||
throw std::invalid_argument("schema match for " + name + " but a not-match is defined by schema.");
|
||||
return; // return here - not cannot be mixed with based-schemas?
|
||||
}
|
||||
|
||||
// allOf, anyOf, oneOf
|
||||
const json *combined_schemas = nullptr;
|
||||
enum {
|
||||
none,
|
||||
allOf,
|
||||
anyOf,
|
||||
oneOf
|
||||
} combine_logic = none;
|
||||
|
||||
{
|
||||
const auto &attr = schema->find("allOf");
|
||||
if (attr != schema->end()) {
|
||||
combine_logic = allOf;
|
||||
combined_schemas = &attr.value();
|
||||
}
|
||||
}
|
||||
{
|
||||
const auto &attr = schema->find("anyOf");
|
||||
if (attr != schema->end()) {
|
||||
combine_logic = anyOf;
|
||||
combined_schemas = &attr.value();
|
||||
}
|
||||
}
|
||||
{
|
||||
const auto &attr = schema->find("oneOf");
|
||||
if (attr != schema->end()) {
|
||||
combine_logic = oneOf;
|
||||
combined_schemas = &attr.value();
|
||||
}
|
||||
}
|
||||
|
||||
if (combine_logic != none) {
|
||||
std::size_t count = 0;
|
||||
std::ostringstream sub_schema_err;
|
||||
|
||||
for (const auto &s : *combined_schemas) {
|
||||
try {
|
||||
validate(instance, s, name);
|
||||
count++;
|
||||
} catch (std::exception &e) {
|
||||
sub_schema_err << " one schema failed because: " << e.what() << "\n";
|
||||
|
||||
if (combine_logic == allOf)
|
||||
throw std::out_of_range("At least one schema has failed for " + name + " where allOf them were requested.\n" + sub_schema_err.str());
|
||||
}
|
||||
if (combine_logic == oneOf && count > 1)
|
||||
throw std::out_of_range("More than one schema has succeeded for " + name + " where only oneOf them was requested.\n" + sub_schema_err.str());
|
||||
}
|
||||
if ((combine_logic == anyOf || combine_logic == oneOf) && count == 0)
|
||||
throw std::out_of_range("No schema has succeeded for " + name + " but anyOf/oneOf them should have worked.\n" + sub_schema_err.str());
|
||||
}
|
||||
|
||||
// check (base) schema
|
||||
validate_enum(instance, *schema, name);
|
||||
|
||||
switch (instance.type()) {
|
||||
case json::value_t::object:
|
||||
validate_object(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::array:
|
||||
validate_array(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::string:
|
||||
validate_string(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::number_unsigned:
|
||||
validate_unsigned(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::number_integer:
|
||||
validate_integer(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::number_float:
|
||||
validate_float(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::boolean:
|
||||
validate_boolean(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::null:
|
||||
validate_null(instance, *schema, name);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0 && "unexpected instance type for validation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void json_validator::validate_array(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "array", name);
|
||||
|
||||
// maxItems
|
||||
const auto &maxItems = schema.find("maxItems");
|
||||
if (maxItems != schema.end())
|
||||
if (instance.size() > maxItems.value().get<size_t>())
|
||||
throw std::out_of_range(name + " has too many items.");
|
||||
|
||||
// minItems
|
||||
const auto &minItems = schema.find("minItems");
|
||||
if (minItems != schema.end())
|
||||
if (instance.size() < minItems.value().get<size_t>())
|
||||
throw std::out_of_range(name + " has too few items.");
|
||||
|
||||
// uniqueItems
|
||||
const auto &uniqueItems = schema.find("uniqueItems");
|
||||
if (uniqueItems != schema.end())
|
||||
if (uniqueItems.value().get<bool>() == true) {
|
||||
std::set<json> array_to_set;
|
||||
for (auto v : instance) {
|
||||
auto ret = array_to_set.insert(v);
|
||||
if (ret.second == false)
|
||||
throw std::out_of_range(name + " should have only unique items.");
|
||||
}
|
||||
}
|
||||
|
||||
// items and additionalItems
|
||||
// default to empty schemas
|
||||
auto items_iter = schema.find("items");
|
||||
json items = {};
|
||||
if (items_iter != schema.end())
|
||||
items = items_iter.value();
|
||||
|
||||
auto additionalItems_iter = schema.find("additionalItems");
|
||||
json additionalItems = {};
|
||||
if (additionalItems_iter != schema.end())
|
||||
additionalItems = additionalItems_iter.value();
|
||||
|
||||
size_t i = 0;
|
||||
bool validation_done = false;
|
||||
|
||||
for (auto &value : instance) {
|
||||
std::string sub_name = name + "[" + std::to_string(i) + "]";
|
||||
|
||||
switch (items.type()) {
|
||||
|
||||
case json::value_t::array:
|
||||
|
||||
if (i < items.size())
|
||||
validate(value, items[i], sub_name);
|
||||
else {
|
||||
switch (additionalItems.type()) { // items is an array
|
||||
// we need to take into consideration additionalItems
|
||||
case json::value_t::object:
|
||||
validate(value, additionalItems, sub_name);
|
||||
break;
|
||||
|
||||
case json::value_t::boolean:
|
||||
if (additionalItems.get<bool>() == false)
|
||||
throw std::out_of_range("additional values in array are not allowed for " + sub_name);
|
||||
else
|
||||
validation_done = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case json::value_t::object: // items is a schema
|
||||
validate(value, items, sub_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (validation_done)
|
||||
break;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void json_validator::validate_object(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "object", name);
|
||||
|
||||
json properties = {};
|
||||
if (schema.find("properties") != schema.end())
|
||||
properties = schema["properties"];
|
||||
|
||||
#if 0
|
||||
// check for default values of properties
|
||||
// and insert them into this object, if they don't exists
|
||||
// works only for object properties for the moment
|
||||
if (default_value_insertion)
|
||||
for (auto it = properties.begin(); it != properties.end(); ++it) {
|
||||
|
||||
const auto &default_value = it.value().find("default");
|
||||
if (default_value == it.value().end())
|
||||
continue; /* no default value -> continue */
|
||||
|
||||
if (instance.find(it.key()) != instance.end())
|
||||
continue; /* value is present */
|
||||
|
||||
/* create element from default value */
|
||||
instance[it.key()] = default_value.value();
|
||||
}
|
||||
#endif
|
||||
// maxProperties
|
||||
const auto &maxProperties = schema.find("maxProperties");
|
||||
if (maxProperties != schema.end())
|
||||
if (instance.size() > maxProperties.value().get<size_t>())
|
||||
throw std::out_of_range(name + " has too many properties.");
|
||||
|
||||
// minProperties
|
||||
const auto &minProperties = schema.find("minProperties");
|
||||
if (minProperties != schema.end())
|
||||
if (instance.size() < minProperties.value().get<size_t>())
|
||||
throw std::out_of_range(name + " has too few properties.");
|
||||
|
||||
// additionalProperties
|
||||
enum {
|
||||
True,
|
||||
False,
|
||||
Object
|
||||
} additionalProperties = True;
|
||||
|
||||
const auto &additionalPropertiesVal = schema.find("additionalProperties");
|
||||
if (additionalPropertiesVal != schema.end()) {
|
||||
if (additionalPropertiesVal.value().type() == json::value_t::boolean)
|
||||
additionalProperties = additionalPropertiesVal.value().get<bool>() == true ? True : False;
|
||||
else
|
||||
additionalProperties = Object;
|
||||
}
|
||||
|
||||
// patternProperties
|
||||
json patternProperties = {};
|
||||
if (schema.find("patternProperties") != schema.end())
|
||||
patternProperties = schema["patternProperties"];
|
||||
|
||||
// check all elements in object
|
||||
for (auto child = instance.begin(); child != instance.end(); ++child) {
|
||||
std::string child_name = name + "." + child.key();
|
||||
|
||||
bool property_or_patternProperties_has_validated = false;
|
||||
// is this a property which is described in the schema
|
||||
const auto &object_prop = properties.find(child.key());
|
||||
if (object_prop != properties.end()) {
|
||||
// validate the element with its schema
|
||||
validate(child.value(), object_prop.value(), child_name);
|
||||
property_or_patternProperties_has_validated = true;
|
||||
}
|
||||
|
||||
for (auto pp = patternProperties.begin();
|
||||
pp != patternProperties.end(); ++pp) {
|
||||
#ifndef NO_STD_REGEX
|
||||
REGEX_NAMESPACE::regex re(pp.key(), REGEX_NAMESPACE::regex::ECMAScript);
|
||||
|
||||
if (REGEX_NAMESPACE::regex_search(child.key(), re)) {
|
||||
validate(child.value(), pp.value(), child_name);
|
||||
property_or_patternProperties_has_validated = true;
|
||||
}
|
||||
#else
|
||||
// accept everything in case of a patternProperty
|
||||
property_or_patternProperties_has_validated = true;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (property_or_patternProperties_has_validated)
|
||||
continue;
|
||||
|
||||
switch (additionalProperties) {
|
||||
case True:
|
||||
break;
|
||||
|
||||
case Object:
|
||||
validate(child.value(), additionalPropertiesVal.value(), child_name);
|
||||
break;
|
||||
|
||||
case False:
|
||||
throw std::invalid_argument("unknown property '" + child.key() + "' in object '" + name + "'");
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// required
|
||||
const auto &required = schema.find("required");
|
||||
if (required != schema.end())
|
||||
for (const auto &element : required.value()) {
|
||||
if (instance.find(element) == instance.end()) {
|
||||
throw std::invalid_argument("required element '" + element.get<std::string>() +
|
||||
"' not found in object '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// dependencies
|
||||
const auto &dependencies = schema.find("dependencies");
|
||||
if (dependencies == schema.end())
|
||||
return;
|
||||
|
||||
for (auto dep = dependencies.value().cbegin();
|
||||
dep != dependencies.value().cend();
|
||||
++dep) {
|
||||
|
||||
// property not present in this instance - next
|
||||
if (instance.find(dep.key()) == instance.end())
|
||||
continue;
|
||||
|
||||
std::string sub_name = name + ".dependency-of-" + dep.key();
|
||||
|
||||
switch (dep.value().type()) {
|
||||
|
||||
case json::value_t::object:
|
||||
validate(instance, dep.value(), sub_name);
|
||||
break;
|
||||
|
||||
case json::value_t::array:
|
||||
for (const auto &prop : dep.value())
|
||||
if (instance.find(prop) == instance.end())
|
||||
throw std::invalid_argument("failed dependency for " + sub_name + ". Need property " + prop.get<std::string>());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::size_t utf8_length(const std::string &s)
|
||||
{
|
||||
size_t len = 0;
|
||||
for (const unsigned char &c : s)
|
||||
if ((c & 0xc0) != 0x80)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
void json_validator::validate_string(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "string", name);
|
||||
|
||||
// minLength
|
||||
auto attr = schema.find("minLength");
|
||||
if (attr != schema.end())
|
||||
if (utf8_length(instance) < attr.value().get<size_t>()) {
|
||||
std::ostringstream s;
|
||||
s << "'" << name << "' of value '" << instance << "' is too short as per minLength ("
|
||||
<< attr.value() << ")";
|
||||
throw std::out_of_range(s.str());
|
||||
}
|
||||
|
||||
// maxLength
|
||||
attr = schema.find("maxLength");
|
||||
if (attr != schema.end())
|
||||
if (utf8_length(instance) > attr.value().get<size_t>()) {
|
||||
std::ostringstream s;
|
||||
s << "'" << name << "' of value '" << instance << "' is too long as per maxLength ("
|
||||
<< attr.value() << ")";
|
||||
throw std::out_of_range(s.str());
|
||||
}
|
||||
|
||||
#ifndef NO_STD_REGEX
|
||||
// pattern
|
||||
attr = schema.find("pattern");
|
||||
if (attr != schema.end()) {
|
||||
REGEX_NAMESPACE::regex re(attr.value().get<std::string>(), REGEX_NAMESPACE::regex::ECMAScript);
|
||||
if (!REGEX_NAMESPACE::regex_search(instance.get<std::string>(), re))
|
||||
throw std::invalid_argument(instance.get<std::string>() + " does not match regex pattern: " + attr.value().get<std::string>() + " for " + name);
|
||||
}
|
||||
#endif
|
||||
|
||||
// format
|
||||
attr = schema.find("format");
|
||||
if (attr != schema.end()) {
|
||||
if (format_check_ == nullptr)
|
||||
throw std::logic_error("A format checker was not provided but a format-attribute for this string is present. " +
|
||||
name + " cannot be validated for " + attr.value().get<std::string>());
|
||||
format_check_(attr.value(), instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
170
examples/validator/tinygltf-validate.cc
Normal file
170
examples/validator/tinygltf-validate.cc
Normal file
@@ -0,0 +1,170 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "json11.hpp"
|
||||
|
||||
#include "valijson/adapters/json11_adapter.hpp"
|
||||
#include "valijson/utils/json11_utils.hpp"
|
||||
|
||||
#include "valijson/schema.hpp"
|
||||
#include "valijson/schema_parser.hpp"
|
||||
#include "valijson/validator.hpp"
|
||||
|
||||
static void usage(const char *name) {
|
||||
std::cerr << "Usage: " << name << " <gltf schema dir> <gltf file>\n";
|
||||
std::cerr << " schema dir is usually : $glTF/specification/2.0/schema\n";
|
||||
std::cerr << " where $glTF is a directory of https://github.com/KhronosGroup/glTF\n";
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
resolver r(nlohmann::json_schema_draft4::root_schema,
|
||||
nlohmann::json_schema_draft4::root_schema["id"]);
|
||||
schema_refs_.insert(r.schema_refs.begin(), r.schema_refs.end());
|
||||
assert(r.undefined_refs.size() == 0);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
static void loader(const json_uri &uri, json &schema)
|
||||
{
|
||||
std::fstream lf("." + uri.path());
|
||||
if (!lf.good())
|
||||
throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path());
|
||||
|
||||
try {
|
||||
lf >> schema;
|
||||
} catch (std::exception &e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
bool validate(const std::string &schema_dir, const std::string &filename)
|
||||
{
|
||||
std::string gltf_schema = schema_dir + "/glTF.schema.json";
|
||||
|
||||
std::fstream f(gltf_schema);
|
||||
if (!f.good()) {
|
||||
std::cerr << "could not open " << gltf_schema << " for reading\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1) Read the schema for the document you want to validate
|
||||
json schema;
|
||||
try {
|
||||
f >> schema;
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << e.what() << " at " << f.tellp() << " - while parsing the schema\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) create the validator and
|
||||
json_validator validator([&schema_dir](const json_uri &uri, json &schema) {
|
||||
std::cout << "uri.url : " << uri.url() << std::endl;
|
||||
std::cout << "uri.path : " << uri.path() << std::endl;
|
||||
|
||||
std::fstream lf(schema_dir + "/" + uri.path());
|
||||
if (!lf.good())
|
||||
throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path());
|
||||
|
||||
try {
|
||||
lf >> schema;
|
||||
} catch (std::exception &e) {
|
||||
throw e;
|
||||
}
|
||||
}, [](const std::string &, const std::string &) {});
|
||||
|
||||
try {
|
||||
// insert this schema as the root to the validator
|
||||
// this resolves remote-schemas, sub-schemas and references via the given loader-function
|
||||
validator.set_root_schema(schema);
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "setting root schema failed\n";
|
||||
std::cerr << e.what() << "\n";
|
||||
}
|
||||
|
||||
// 3) do the actual validation of the document
|
||||
json document;
|
||||
|
||||
std::fstream d(filename);
|
||||
if (!d.good()) {
|
||||
std::cerr << "could not open " << filename << " for reading\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
d >> document;
|
||||
validator.validate(document);
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "schema validation failed\n";
|
||||
std::cerr << e.what() << " at offset: " << d.tellg() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cerr << "document is valid\n";
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
bool validate(const std::string &schema_dir, const std::string &filename) {
|
||||
std::string gltf_schema = schema_dir + "/glTF.schema.json";
|
||||
|
||||
// 1) Read the schema for the document you want to validate
|
||||
json11::Json schema_doc;
|
||||
bool ret = valijson::utils::loadDocument(gltf_schema, schema_doc);
|
||||
if (!ret) {
|
||||
std::cerr << "Failed to load schema file : " << gltf_schema << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) Parse JSON schema content using valijson
|
||||
valijson::Schema mySchema;
|
||||
valijson::SchemaParser parser;
|
||||
valijson::adapters::Json11Adapter mySchemaAdapter(schema_doc);
|
||||
parser.populateSchema(mySchemaAdapter, mySchema);
|
||||
|
||||
// 3) Load a document to validate
|
||||
json11::Json target_doc;
|
||||
if (!valijson::utils::loadDocument(filename, target_doc)) {
|
||||
std::cerr << "Failed to load JSON file to validate : " << filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
valijson::Validator validator;
|
||||
valijson::ValidationResults results;
|
||||
valijson::adapters::Json11Adapter myTargetAdapter(target_doc);
|
||||
if (!validator.validate(mySchema, myTargetAdapter, &results)) {
|
||||
std::cerr << "Validation failed.\n";
|
||||
|
||||
valijson::ValidationResults::Error error;
|
||||
unsigned int errorNum = 1;
|
||||
while (results.popError(error)) {
|
||||
std::cerr << "Error #" << errorNum << std::endl;
|
||||
std::cerr << " ";
|
||||
for (const std::string &contextElement : error.context) {
|
||||
std::cerr << contextElement << " ";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
std::cerr << " - " << error.description << std::endl;
|
||||
++errorNum;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Valid glTF file!\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 3) usage(argv[0]);
|
||||
|
||||
bool ret = validate(argv[1], argv[2]);
|
||||
|
||||
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
23
examples/validator/valijson/LICENSE
Normal file
23
examples/validator/valijson/LICENSE
Normal file
@@ -0,0 +1,23 @@
|
||||
Copyright (c) 2016, Tristan Penman
|
||||
Copyright (c) 2016, Akamai Technolgies, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
1042
examples/validator/valijson/include/compat/optional.hpp
Normal file
1042
examples/validator/valijson/include/compat/optional.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,476 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_ADAPTER_HPP
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class FrozenValue;
|
||||
|
||||
/**
|
||||
* @brief An interface that encapsulates access to the JSON values provided
|
||||
* by a JSON parser implementation.
|
||||
*
|
||||
* This interface allows JSON processing code to be parser-agnostic. It provides
|
||||
* functions to access the plain old datatypes (PODs) that are described in the
|
||||
* JSON specification, and callback-based access to the contents of arrays and
|
||||
* objects.
|
||||
*
|
||||
* The interface also defines a set of functions that allow for type-casting and
|
||||
* type-comparison based on value rather than on type.
|
||||
*/
|
||||
class Adapter
|
||||
{
|
||||
public:
|
||||
|
||||
/// Typedef for callback function supplied to applyToArray.
|
||||
typedef std::function<bool (const Adapter &)>
|
||||
ArrayValueCallback;
|
||||
|
||||
/// Typedef for callback function supplied to applyToObject.
|
||||
typedef std::function<bool (const std::string &, const Adapter &)>
|
||||
ObjectMemberCallback;
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor defined to ensure deletion via base-class
|
||||
* pointers is safe.
|
||||
*/
|
||||
virtual ~Adapter() { };
|
||||
|
||||
/**
|
||||
* @brief Apply a callback function to each value in an array.
|
||||
*
|
||||
* The callback function is invoked for each element in the array, until
|
||||
* it has been applied to all values, or it returns false.
|
||||
*
|
||||
* @param fn Callback function to invoke
|
||||
*
|
||||
* @returns true if Adapter contains an array and all values are equal,
|
||||
* false otherwise.
|
||||
*/
|
||||
virtual bool applyToArray(ArrayValueCallback fn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Apply a callback function to each member in an object.
|
||||
*
|
||||
* The callback function shall be invoked for each member in the object,
|
||||
* until it has been applied to all values, or it returns false.
|
||||
*
|
||||
* @param fn Callback function to invoke
|
||||
*
|
||||
* @returns true if Adapter contains an object, and callback function
|
||||
* returns true for each member in the object, false otherwise.
|
||||
*/
|
||||
virtual bool applyToObject(ObjectMemberCallback fn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the boolean representation of the contained value.
|
||||
*
|
||||
* This function shall return a boolean value if the Adapter contains either
|
||||
* an actual boolean value, or one of the strings 'true' or 'false'.
|
||||
* The string comparison is case sensitive.
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a boolean.
|
||||
*
|
||||
* @returns Boolean representation of contained value.
|
||||
*/
|
||||
virtual bool asBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the boolean representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a boolean value if the Adapter contains
|
||||
* either an actual boolean value, or one of the strings 'true' or 'false'.
|
||||
* The string comparison is case sensitive.
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a bool to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asBool(bool &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the double representation of the contained value.
|
||||
*
|
||||
* This function shall return a double value if the Adapter contains either
|
||||
* an actual double, an integer, or a string that contains a valid
|
||||
* representation of a numeric value (according to the C++ Std Library).
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a double.
|
||||
*
|
||||
* @returns Double representation of contained value.
|
||||
*/
|
||||
virtual double asDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the double representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a double value if the Adapter contains either
|
||||
* an actual double, an integer, or a string that contains a valid
|
||||
* representation of a numeric value (according to the C++ Std Library).
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a double to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asDouble(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the int64_t representation of the contained value.
|
||||
*
|
||||
* This function shall return an int64_t value if the Adapter contains either
|
||||
* an actual integer, or a string that contains a valid representation of an
|
||||
* integer value (according to the C++ Std Library).
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to an int64_t.
|
||||
*
|
||||
* @returns int64_t representation of contained value.
|
||||
*/
|
||||
virtual int64_t asInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the int64_t representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve an int64_t value if the Adapter contains
|
||||
* either an actual integer, or a string that contains a valid
|
||||
* representation of an integer value (according to the C++ Std Library).
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a int64_t to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asInteger(int64_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the string representation of the contained value.
|
||||
*
|
||||
* This function shall return a string value if the Adapter contains either
|
||||
* an actual string, a literal value of another POD type, an empty array,
|
||||
* an empty object, or null.
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a string.
|
||||
*
|
||||
* @returns string representation of contained value.
|
||||
*/
|
||||
virtual std::string asString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the string representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a string value if the Adapter contains either
|
||||
* an actual string, a literal value of another POD type, an empty array,
|
||||
* an empty object, or null.
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a string to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asString(std::string &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Compare the value held by this Adapter instance with the value
|
||||
* held by another Adapter instance.
|
||||
*
|
||||
* @param other the other adapter instance
|
||||
* @param strict flag to use strict type comparison
|
||||
*
|
||||
* @returns true if values are equal, false otherwise
|
||||
*/
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Create a new FrozenValue instance that is equivalent to the
|
||||
* value contained by the Adapter.
|
||||
*
|
||||
* @returns pointer to a new FrozenValue instance, belonging to the caller.
|
||||
*/
|
||||
virtual FrozenValue* freeze() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the number of elements in the array.
|
||||
*
|
||||
* Throws an exception if the value is not an array.
|
||||
*
|
||||
* @return number of elements if value is an array
|
||||
*/
|
||||
virtual size_t getArraySize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array.
|
||||
*
|
||||
* This function shall return true or false to indicate whether or not the
|
||||
* result value was set. If the contained value is not an array, the
|
||||
* result value shall not be set. This applies even if the value could be
|
||||
* cast to an empty array. The calling code is expected to handles those
|
||||
* cases manually.
|
||||
*
|
||||
* @param result reference to size_t variable to set with result.
|
||||
*
|
||||
* @return true if value retrieved successfully, false otherwise.
|
||||
*/
|
||||
virtual bool getArraySize(size_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained boolean value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* boolean.
|
||||
*
|
||||
* @returns contained boolean value.
|
||||
*/
|
||||
virtual bool getBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained boolean value.
|
||||
*
|
||||
* This function shall retrieve the boolean value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to boolean variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getBool(bool &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained double value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* double.
|
||||
*
|
||||
* @returns contained double value.
|
||||
*/
|
||||
virtual double getDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained double value.
|
||||
*
|
||||
* This function shall retrieve the double value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to double variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getDouble(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained integer value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* integer.
|
||||
*
|
||||
* @returns contained integer value.
|
||||
*/
|
||||
virtual int64_t getInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained integer value.
|
||||
*
|
||||
* This function shall retrieve the integer value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to integer variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getInteger(int64_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained numeric value as a double.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* integer or a double.
|
||||
*
|
||||
* @returns contained double or integral value.
|
||||
*/
|
||||
virtual double getNumber() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained numeric value as a double.
|
||||
*
|
||||
* This function shall retrieve the double or integral value contained by
|
||||
* this Adapter, and store it in the result variable that was passed by
|
||||
* reference.
|
||||
*
|
||||
* @param result reference to double variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getNumber(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the number of members in the object.
|
||||
*
|
||||
* Throws an exception if the value is not an object.
|
||||
*
|
||||
* @return number of members if value is an object
|
||||
*/
|
||||
virtual size_t getObjectSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object.
|
||||
*
|
||||
* This function shall return true or false to indicate whether or not the
|
||||
* result value was set. If the contained value is not an object, the
|
||||
* result value shall not be set. This applies even if the value could be
|
||||
* cast to an empty object. The calling code is expected to handles those
|
||||
* cases manually.
|
||||
*
|
||||
* @param result reference to size_t variable to set with result.
|
||||
*
|
||||
* @return true if value retrieved successfully, false otherwise.
|
||||
*/
|
||||
virtual bool getObjectSize(size_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained string value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* string - even if the value could be cast to a string. The asString()
|
||||
* function should be used when casting is allowed.
|
||||
*
|
||||
* @returns string contained by this Adapter
|
||||
*/
|
||||
virtual std::string getString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained string value.
|
||||
*
|
||||
* This function shall retrieve the string value contained by this Adapter,
|
||||
* and store it in result variable that is passed by reference.
|
||||
*
|
||||
* @param result reference to string to set with result
|
||||
*
|
||||
* @returns true if string was retrieved, false otherwise
|
||||
*/
|
||||
virtual bool getString(std::string &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not this Adapter supports strict types.
|
||||
*
|
||||
* This function shall return true if the Adapter implementation supports
|
||||
* strict types, or false if the Adapter fails to store any part of the
|
||||
* type information supported by the Adapter interface.
|
||||
*
|
||||
* For example, the PropertyTreeAdapter implementation stores POD values as
|
||||
* strings, effectively discarding any other type information. If you were
|
||||
* to call isDouble() on a double stored by this Adapter, the result would
|
||||
* be false. The maybeDouble(), asDouble() and various related functions
|
||||
* are provided to perform type checking based on value rather than on type.
|
||||
*
|
||||
* The BasicAdapter template class provides implementations for the type-
|
||||
* casting functions so that Adapter implementations are semantically
|
||||
* equivalent in their type-casting behaviour.
|
||||
*
|
||||
* @returns true if Adapter supports strict types, false otherwise
|
||||
*/
|
||||
virtual bool hasStrictTypes() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an array.
|
||||
virtual bool isArray() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a boolean.
|
||||
virtual bool isBool() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a double.
|
||||
virtual bool isDouble() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an integer.
|
||||
virtual bool isInteger() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a null.
|
||||
virtual bool isNull() const = 0;
|
||||
|
||||
/// Returns true if the contained value is either a double or an integer.
|
||||
virtual bool isNumber() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an object.
|
||||
virtual bool isObject() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a string.
|
||||
virtual bool isString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an array.
|
||||
*
|
||||
* @returns true if the contained value is an array, an empty string, or an
|
||||
* empty object.
|
||||
*/
|
||||
virtual bool maybeArray() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a boolean.
|
||||
*
|
||||
* @returns true if the contained value is a boolean, or one of the strings
|
||||
* 'true' or 'false'. Note that numeric values are not to be cast
|
||||
* to boolean values.
|
||||
*/
|
||||
virtual bool maybeBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a double.
|
||||
*
|
||||
* @returns true if the contained value is a double, an integer, or a string
|
||||
* containing a double or integral value.
|
||||
*/
|
||||
virtual bool maybeDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an integer.
|
||||
*
|
||||
* @returns true if the contained value is an integer, or a string
|
||||
* containing an integral value.
|
||||
*/
|
||||
virtual bool maybeInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a null.
|
||||
*
|
||||
* @returns true if the contained value is null or an empty string.
|
||||
*/
|
||||
virtual bool maybeNull() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an object.
|
||||
*
|
||||
* @returns true if the contained value is an object, an empty array or
|
||||
* an empty string.
|
||||
*/
|
||||
virtual bool maybeObject() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a string.
|
||||
*
|
||||
* @returns true if the contained value is a non-null POD type, an empty
|
||||
* array, or an empty object.
|
||||
*/
|
||||
virtual bool maybeString() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Template struct that should be specialised for each concrete Adapter
|
||||
* class.
|
||||
*
|
||||
* @deprecated This is a bit of a hack, and I'd like to remove it.
|
||||
*/
|
||||
template<typename T>
|
||||
struct AdapterTraits
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,868 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_BASIC_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_BASIC_ADAPTER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sstream>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/internal/optional.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
/**
|
||||
* @brief A helper for the array and object member iterators.
|
||||
*
|
||||
* See http://www.stlsoft.org/doc-1.9/group__group____pattern____dereference__proxy.html
|
||||
* for motivation
|
||||
*
|
||||
* @tparam Value Name of the value type
|
||||
*/
|
||||
template<class Value>
|
||||
struct DerefProxy
|
||||
{
|
||||
explicit DerefProxy(const Value& x)
|
||||
: m_ref(x) { }
|
||||
|
||||
Value* operator->()
|
||||
{
|
||||
return std::addressof(m_ref);
|
||||
}
|
||||
|
||||
operator Value*()
|
||||
{
|
||||
return std::addressof(m_ref);
|
||||
}
|
||||
|
||||
private:
|
||||
Value m_ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Template class that implements the expected semantics of an Adapter.
|
||||
*
|
||||
* Implementing all of the type-casting functionality for each Adapter is error
|
||||
* prone and tedious, so this template class aims to minimise the duplication
|
||||
* of code between various Adapter implementations. This template doesn't quite
|
||||
* succeed in removing all duplication, but it has greatly simplified the
|
||||
* implementation of a new Adapter by encapsulating the type-casting semantics
|
||||
* and a lot of the trivial functionality associated with the Adapter interface.
|
||||
*
|
||||
* By inheriting from this template class, Adapter implementations will inherit
|
||||
* the exception throwing behaviour that is expected by other parts of the
|
||||
* Valijson library.
|
||||
*
|
||||
* @tparam AdapterType Self-referential name of the Adapter being
|
||||
* specialised.
|
||||
* @tparam ArrayType Name of the type that will be returned by the
|
||||
* getArray() function. Instances of this type should
|
||||
* provide begin(), end() and size() functions so
|
||||
* that it is possible to iterate over the values in
|
||||
* the array.
|
||||
* @tparam ObjectMemberType Name of the type exposed when iterating over the
|
||||
* contents of an object returned by getObject().
|
||||
* @tparam ObjectType Name of the type that will be returned by the
|
||||
* getObject() function. Instances of this type
|
||||
* should provide begin(), end(), find() and size()
|
||||
* functions so that it is possible to iterate over
|
||||
* the members of the object.
|
||||
* @tparam ValueType Name of the type that provides a consistent
|
||||
* interface to a JSON value for a parser. For
|
||||
* example, this type should provide the getDouble()
|
||||
* and isDouble() functions. But it does not need to
|
||||
* know how to cast values from one type to another -
|
||||
* that functionality is provided by this template
|
||||
* class.
|
||||
*/
|
||||
template<
|
||||
typename AdapterType,
|
||||
typename ArrayType,
|
||||
typename ObjectMemberType,
|
||||
typename ObjectType,
|
||||
typename ValueType>
|
||||
class BasicAdapter: public Adapter
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief Functor for comparing two arrays.
|
||||
*
|
||||
* This functor is used to compare the elements in an array of the type
|
||||
* ArrayType with individual values provided as generic Adapter objects.
|
||||
* Comparison is performed by the () operator.
|
||||
*
|
||||
* The functor works by maintaining an iterator for the current position
|
||||
* in an array. Each time the () operator is called, the value at this
|
||||
* position is compared with the value passed as an argument to ().
|
||||
* Immediately after the comparison, the iterator will be incremented.
|
||||
*
|
||||
* This functor is designed to be passed to the applyToArray() function
|
||||
* of an Adapter object.
|
||||
*/
|
||||
class ArrayComparisonFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an ArrayComparisonFunctor for an array.
|
||||
*
|
||||
* @param array Array to compare values against
|
||||
* @param strict Flag to use strict type comparison
|
||||
*/
|
||||
ArrayComparisonFunctor(const ArrayType &array, bool strict)
|
||||
: itr(array.begin()),
|
||||
end(array.end()),
|
||||
strict(strict) { }
|
||||
|
||||
/**
|
||||
* @brief Compare a value against the current element in the array.
|
||||
*
|
||||
* @param adapter Value to be compared with current element
|
||||
*
|
||||
* @returns true if values are equal, false otherwise.
|
||||
*/
|
||||
bool operator()(const Adapter &adapter)
|
||||
{
|
||||
if (itr == end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return AdapterType(*itr++).equalTo(adapter, strict);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iterator for current element in the array
|
||||
typename ArrayType::const_iterator itr;
|
||||
|
||||
/// Iterator for one-past the last element of the array
|
||||
typename ArrayType::const_iterator end;
|
||||
|
||||
/// Flag to use strict type comparison
|
||||
const bool strict;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Functor for comparing two objects
|
||||
*
|
||||
* This functor is used to compare the members of an object of the type
|
||||
* ObjectType with key-value pairs belonging to another object.
|
||||
*
|
||||
* The functor works by maintaining a reference to an object provided via
|
||||
* the constructor. When time the () operator is called with a key-value
|
||||
* pair as arguments, the function will attempt to find the key in the
|
||||
* base object. If found, the associated value will be compared with the
|
||||
* value provided to the () operator.
|
||||
*
|
||||
* This functor is designed to be passed to the applyToObject() function
|
||||
* of an Adapter object.
|
||||
*/
|
||||
class ObjectComparisonFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new ObjectComparisonFunctor for an object.
|
||||
*
|
||||
* @param object object to use as comparison baseline
|
||||
* @param strict flag to use strict type-checking
|
||||
*/
|
||||
ObjectComparisonFunctor(
|
||||
const ObjectType &object, bool strict)
|
||||
: object(object),
|
||||
strict(strict) { }
|
||||
|
||||
/**
|
||||
* @brief Find a key in the object and compare its value.
|
||||
*
|
||||
* @param key Key to find
|
||||
* @param value Value to be compared against
|
||||
*
|
||||
* @returns true if key is found and values are equal, false otherwise.
|
||||
*/
|
||||
bool operator()(const std::string &key, const Adapter &value)
|
||||
{
|
||||
const typename ObjectType::const_iterator itr = object.find(key);
|
||||
if (itr == object.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (*itr).second.equalTo(value, strict);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Object to be used as a comparison baseline
|
||||
const ObjectType &object;
|
||||
|
||||
/// Flag to use strict type-checking
|
||||
bool strict;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// Alias for ArrayType template parameter
|
||||
typedef ArrayType Array;
|
||||
|
||||
/// Alias for ObjectMemberType template parameter
|
||||
typedef ObjectMemberType ObjectMember;
|
||||
|
||||
/// Alias for ObjectType template parameter
|
||||
typedef ObjectType Object;
|
||||
|
||||
/**
|
||||
* @brief Construct an Adapter using the default value.
|
||||
*
|
||||
* This constructor relies on the default constructor of the ValueType
|
||||
* class provided as a template argument.
|
||||
*/
|
||||
BasicAdapter() { }
|
||||
|
||||
/**
|
||||
* @brief Construct an Adapter using a specified ValueType object.
|
||||
*
|
||||
* This constructor relies on the copy constructor of the ValueType
|
||||
* class provided as template argument.
|
||||
*/
|
||||
BasicAdapter(const ValueType &value)
|
||||
: value(value) { }
|
||||
|
||||
virtual bool applyToArray(ArrayValueCallback fn) const
|
||||
{
|
||||
if (!maybeArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Due to the fact that the only way a value can be 'maybe an array' is
|
||||
// if it is an empty string or empty object, we only need to go to
|
||||
// effort of constructing an ArrayType instance if the value is
|
||||
// definitely an array.
|
||||
if (value.isArray()) {
|
||||
const opt::optional<Array> array = value.getArrayOptional();
|
||||
for (const AdapterType element : *array) {
|
||||
if (!fn(element)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool applyToObject(ObjectMemberCallback fn) const
|
||||
{
|
||||
if (!maybeObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.isObject()) {
|
||||
const opt::optional<Object> object = value.getObjectOptional();
|
||||
for (const ObjectMemberType member : *object) {
|
||||
if (!fn(member.first, AdapterType(member.second))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ArrayType instance containing an array representation
|
||||
* of the value held by this Adapter.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the elements in an array. The ArrayType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained value is either an empty object, or an empty string,
|
||||
* then this function will cast the value to an empty array.
|
||||
*
|
||||
* @returns ArrayType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ArrayType asArray() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return *value.getArrayOptional();
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return ArrayType();
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue) && stringValue.empty()) {
|
||||
return ArrayType();
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to an array.");
|
||||
}
|
||||
|
||||
virtual bool asBool() const
|
||||
{
|
||||
bool result;
|
||||
if (asBool(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a boolean.");
|
||||
}
|
||||
|
||||
virtual bool asBool(bool &result) const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
return value.getBool(result);
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
if (s.compare("true") == 0) {
|
||||
result = true;
|
||||
return true;
|
||||
} else if (s.compare("false") == 0) {
|
||||
result = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual double asDouble() const
|
||||
{
|
||||
double result;
|
||||
if (asDouble(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a double.");
|
||||
}
|
||||
|
||||
virtual bool asDouble(double &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
return value.getDouble(result);
|
||||
} else if (value.isInteger()) {
|
||||
int64_t i;
|
||||
if (value.getInteger(i)) {
|
||||
result = double(i);
|
||||
return true;
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
const char *b = s.c_str();
|
||||
char *e = NULL;
|
||||
double x = strtod(b, &e);
|
||||
if (e == b || e != b + s.length()) {
|
||||
return false;
|
||||
}
|
||||
result = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual int64_t asInteger() const
|
||||
{
|
||||
int64_t result;
|
||||
if (asInteger(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast as an integer.");
|
||||
}
|
||||
|
||||
virtual bool asInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isInteger()) {
|
||||
return value.getInteger(result);
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
std::istringstream i(s);
|
||||
int64_t x;
|
||||
char c;
|
||||
if (!(!(i >> x) || i.get(c))) {
|
||||
result = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ObjectType instance containing an array representation
|
||||
* of the value held by this Adapter.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the members of the object. The ObjectType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* @returns ObjectType instance containing an object representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ObjectType asObject() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return *value.getObjectOptional();
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return ObjectType();
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue) && stringValue.empty()) {
|
||||
return ObjectType();
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to an object.");
|
||||
}
|
||||
|
||||
virtual std::string asString() const
|
||||
{
|
||||
std::string result;
|
||||
if (asString(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a string.");
|
||||
}
|
||||
|
||||
virtual bool asString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
return value.getString(result);
|
||||
} else if (value.isNull()) {
|
||||
result.clear();
|
||||
return true;
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
result.clear();
|
||||
return true;
|
||||
}
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
result.clear();
|
||||
return true;
|
||||
}
|
||||
} else if (value.isBool()) {
|
||||
bool boolValue;
|
||||
if (value.getBool(boolValue)) {
|
||||
result = boolValue ? "true" : "false";
|
||||
return true;
|
||||
}
|
||||
} else if (value.isInteger()) {
|
||||
int64_t integerValue;
|
||||
if (value.getInteger(integerValue)) {
|
||||
result = std::to_string(integerValue);
|
||||
return true;
|
||||
}
|
||||
} else if (value.isDouble()) {
|
||||
double doubleValue;
|
||||
if (value.getDouble(doubleValue)) {
|
||||
result = std::to_string(doubleValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
if (isNull() || (!strict && maybeNull())) {
|
||||
return other.isNull() || (!strict && other.maybeNull());
|
||||
} else if (isBool() || (!strict && maybeBool())) {
|
||||
return (other.isBool() || (!strict && other.maybeBool())) &&
|
||||
other.asBool() == asBool();
|
||||
} else if (isNumber() && strict) {
|
||||
return other.isNumber() && other.getNumber() == getNumber();
|
||||
} else if (!strict && maybeDouble()) {
|
||||
return (other.maybeDouble() &&
|
||||
other.asDouble() == asDouble());
|
||||
} else if (!strict && maybeInteger()) {
|
||||
return (other.maybeInteger() &&
|
||||
other.asInteger() == asInteger());
|
||||
} else if (isString() || (!strict && maybeString())) {
|
||||
return (other.isString() || (!strict && other.maybeString())) &&
|
||||
other.asString() == asString();
|
||||
} else if (isArray()) {
|
||||
if (other.isArray() && getArraySize() == other.getArraySize()) {
|
||||
const opt::optional<ArrayType> array = value.getArrayOptional();
|
||||
if (array) {
|
||||
ArrayComparisonFunctor fn(*array, strict);
|
||||
return other.applyToArray(fn);
|
||||
}
|
||||
} else if (!strict && other.maybeArray() && getArraySize() == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (isObject()) {
|
||||
if (other.isObject() && other.getObjectSize() == getObjectSize()) {
|
||||
const opt::optional<ObjectType> object = value.getObjectOptional();
|
||||
if (object) {
|
||||
ObjectComparisonFunctor fn(*object, strict);
|
||||
return other.applyToObject(fn);
|
||||
}
|
||||
} else if (!strict && other.maybeObject() && getObjectSize() == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ArrayType instance representing the array contained
|
||||
* by this Adapter instance.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the elements in an array. The ArrayType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained is not an array, this function will throw an exception.
|
||||
*
|
||||
* @returns ArrayType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ArrayType getArray() const
|
||||
{
|
||||
opt::optional<ArrayType> arrayValue = value.getArrayOptional();
|
||||
if (arrayValue) {
|
||||
return *arrayValue;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an array.");
|
||||
}
|
||||
|
||||
virtual size_t getArraySize() const
|
||||
{
|
||||
size_t result;
|
||||
if (value.getArraySize(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an array.");
|
||||
}
|
||||
|
||||
virtual bool getArraySize(size_t &result) const
|
||||
{
|
||||
return value.getArraySize(result);
|
||||
}
|
||||
|
||||
virtual bool getBool() const
|
||||
{
|
||||
bool result;
|
||||
if (getBool(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a boolean.");
|
||||
}
|
||||
|
||||
virtual bool getBool(bool &result) const
|
||||
{
|
||||
return value.getBool(result);
|
||||
}
|
||||
|
||||
virtual double getDouble() const
|
||||
{
|
||||
double result;
|
||||
if (getDouble(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a double.");
|
||||
}
|
||||
|
||||
virtual bool getDouble(double &result) const
|
||||
{
|
||||
return value.getDouble(result);
|
||||
}
|
||||
|
||||
virtual int64_t getInteger() const
|
||||
{
|
||||
int64_t result;
|
||||
if (getInteger(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an integer.");
|
||||
}
|
||||
|
||||
virtual bool getInteger(int64_t &result) const
|
||||
{
|
||||
return value.getInteger(result);
|
||||
}
|
||||
|
||||
virtual double getNumber() const
|
||||
{
|
||||
double result;
|
||||
if (getNumber(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a number.");
|
||||
}
|
||||
|
||||
virtual bool getNumber(double &result) const
|
||||
{
|
||||
if (isDouble()) {
|
||||
return getDouble(result);
|
||||
} else if (isInteger()) {
|
||||
int64_t integerResult;
|
||||
if (getInteger(integerResult)) {
|
||||
result = static_cast<double>(integerResult);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ObjectType instance representing the object contained
|
||||
* by this Adapter instance.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the members of an object. The ObjectType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained is not an object, this function will throw an exception.
|
||||
*
|
||||
* @returns ObjectType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ObjectType getObject() const
|
||||
{
|
||||
opt::optional<ObjectType> objectValue = value.getObjectOptional();
|
||||
if (objectValue) {
|
||||
return *objectValue;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an object.");
|
||||
}
|
||||
|
||||
virtual size_t getObjectSize() const
|
||||
{
|
||||
size_t result;
|
||||
if (getObjectSize(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an object.");
|
||||
}
|
||||
|
||||
virtual bool getObjectSize(size_t &result) const
|
||||
{
|
||||
return value.getObjectSize(result);
|
||||
}
|
||||
|
||||
virtual std::string getString() const
|
||||
{
|
||||
std::string result;
|
||||
if (getString(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a string.");
|
||||
}
|
||||
|
||||
virtual bool getString(std::string &result) const
|
||||
{
|
||||
return value.getString(result);
|
||||
}
|
||||
|
||||
virtual FrozenValue * freeze() const
|
||||
{
|
||||
return value.freeze();
|
||||
}
|
||||
|
||||
virtual bool hasStrictTypes() const
|
||||
{
|
||||
return ValueType::hasStrictTypes();
|
||||
}
|
||||
|
||||
virtual bool isArray() const
|
||||
{
|
||||
return value.isArray();
|
||||
}
|
||||
|
||||
virtual bool isBool() const
|
||||
{
|
||||
return value.isBool();
|
||||
}
|
||||
|
||||
virtual bool isDouble() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
virtual bool isInteger() const
|
||||
{
|
||||
return value.isInteger();
|
||||
}
|
||||
|
||||
virtual bool isNull() const
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
|
||||
virtual bool isNumber() const
|
||||
{
|
||||
return value.isInteger() || value.isDouble();
|
||||
}
|
||||
|
||||
virtual bool isObject() const
|
||||
{
|
||||
return value.isObject();
|
||||
}
|
||||
|
||||
virtual bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
virtual bool maybeArray() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return true;
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeBool() const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue)) {
|
||||
if (stringValue.compare("true") == 0 || stringValue.compare("false") == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeDouble() const
|
||||
{
|
||||
if (value.isNumber()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
const char *b = s.c_str();
|
||||
char *e = NULL;
|
||||
strtod(b, &e);
|
||||
return e != b && e == b + s.length();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeInteger() const
|
||||
{
|
||||
if (value.isInteger()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
std::istringstream i(s);
|
||||
int64_t x;
|
||||
char c;
|
||||
if (!(i >> x) || i.get(c)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeNull() const
|
||||
{
|
||||
if (value.isNull()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue)) {
|
||||
if (stringValue.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeObject() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return true;
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeString() const
|
||||
{
|
||||
if (value.isString() || value.isBool() || value.isInteger() ||
|
||||
value.isDouble()) {
|
||||
return true;
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const ValueType value;
|
||||
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_FROZEN_VALUE_HPP
|
||||
#define __VALIJSON_ADAPTERS_FROZEN_VALUE_HPP
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
/**
|
||||
* @brief An interface that provides minimal access to a stored JSON value.
|
||||
*
|
||||
* The main reason that this interface exists is to support the 'enum'
|
||||
* constraint. Each Adapter type is expected to provide an implementation of
|
||||
* this interface. That class should be able to maintain its own copy of a
|
||||
* JSON value, independent of the original document.
|
||||
*
|
||||
* This interface currently provides just the clone and equalTo functions, but
|
||||
* could be expanded to include other functions declared in the Adapter
|
||||
* interface.
|
||||
*
|
||||
* @todo it would be nice to better integrate this with the Adapter interface
|
||||
*/
|
||||
class FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor defined to ensure deletion via base-class
|
||||
* pointers is safe.
|
||||
*/
|
||||
virtual ~FrozenValue() { }
|
||||
|
||||
/**
|
||||
* @brief Clone the stored value and return a pointer to a new FrozenValue
|
||||
* object containing the value.
|
||||
*/
|
||||
virtual FrozenValue *clone() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return true if the stored value is equal to the value contained
|
||||
* by an Adapter instance.
|
||||
*
|
||||
* @param adapter Adapter to compare value against
|
||||
* @param strict Flag to use strict type comparison
|
||||
*
|
||||
* @returns true if values are equal, false otherwise
|
||||
*/
|
||||
virtual bool equalTo(const Adapter &adapter, bool strict) const = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,713 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the json11 parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for json11.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - Json11Adapter
|
||||
* - Json11Array
|
||||
* - Json11ArrayValueIterator
|
||||
* - Json11FrozenValue
|
||||
* - Json11Object
|
||||
* - Json11ObjectMember
|
||||
* - Json11ObjectMemberIterator
|
||||
* - Json11Value
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is Json11Adapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_JSON11_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_JSON11_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <json11.hpp>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class Json11Adapter;
|
||||
class Json11ArrayValueIterator;
|
||||
class Json11ObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, Json11Adapter> Json11ObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Json11 array value.
|
||||
*
|
||||
* This class is light weight wrapper for a Json11 array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* Json11 value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class Json11Array
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Json11ArrayValueIterator const_iterator;
|
||||
typedef Json11ArrayValueIterator iterator;
|
||||
|
||||
/// Construct a Json11Array referencing an empty array.
|
||||
Json11Array()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a Json11Array referencing a specific Json11
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a Json11 value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
Json11Array(const json11::Json &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is_array()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying Json11 implementation.
|
||||
*/
|
||||
Json11ArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying Json11 implementation.
|
||||
*/
|
||||
Json11ArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.array_items().size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a Json11 value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const json11::Json & emptyArray()
|
||||
{
|
||||
static const json11::Json array((json11::Json::array()));
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const json11::Json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Json11 object.
|
||||
*
|
||||
* This class is light weight wrapper for a Json11 object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* Json11 value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class Json11Object
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Json11ObjectMemberIterator const_iterator;
|
||||
typedef Json11ObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a Json11Object referencing an empty object singleton.
|
||||
Json11Object()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a Json11Object referencing a specific Json11
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a Json11 value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
Json11Object(const json11::Json &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is_object()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying Json11 implementation.
|
||||
*/
|
||||
Json11ObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying Json11 implementation.
|
||||
*/
|
||||
Json11ObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
Json11ObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.object_items().size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a Json11 value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const json11::Json & emptyObject()
|
||||
{
|
||||
static const json11::Json object((json11::Json::object()));
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const json11::Json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a Json11 value.
|
||||
*
|
||||
* This class allows a Json11 value to be stored independent of its original
|
||||
* document. Json11 makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class Json11FrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a Json11 value
|
||||
*
|
||||
* @param source the Json11 value to be copied
|
||||
*/
|
||||
explicit Json11FrozenValue(const json11::Json &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new Json11FrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored Json11 value
|
||||
json11::Json value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Json11 value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a Json11 value. This class is responsible
|
||||
* for the mechanics of actually reading a Json11 value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class Json11Value
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
Json11Value()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific Json11 value
|
||||
Json11Value(const json11::Json &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new Json11FrozenValue instance that contains the
|
||||
* value referenced by this Json11Value instance.
|
||||
*
|
||||
* @returns pointer to a new Json11FrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new Json11FrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a Json11Array instance.
|
||||
*
|
||||
* If the referenced Json11 value is an array, this function will return
|
||||
* a std::optional containing a Json11Array instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<Json11Array> getArrayOptional() const
|
||||
{
|
||||
if (value.is_array()) {
|
||||
return opt::make_optional(Json11Array(value));
|
||||
}
|
||||
|
||||
return opt::optional<Json11Array>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced Json11 value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.is_array()) {
|
||||
result = value.array_items().size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.is_bool()) {
|
||||
result = value.bool_value();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.is_number()) {
|
||||
result = value.number_value();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if(isInteger()) {
|
||||
result = value.int_value();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a Json11Object instance.
|
||||
*
|
||||
* If the referenced Json11 value is an object, this function will return a
|
||||
* std::optional containing a Json11Object instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<Json11Object> getObjectOptional() const
|
||||
{
|
||||
if (value.is_object()) {
|
||||
return opt::make_optional(Json11Object(value));
|
||||
}
|
||||
|
||||
return opt::optional<Json11Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced Json11 value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.is_object()) {
|
||||
result = value.object_items().size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.is_string()) {
|
||||
result = value.string_value();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.is_array();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.is_bool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.is_number();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.is_number()
|
||||
&& value.int_value() == value.number_value();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.is_null();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.is_number();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.is_object();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.is_string();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const json11::Json & emptyObject()
|
||||
{
|
||||
static const json11::Json object((json11::Json::object()));
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained Json11 value.
|
||||
const json11::Json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting Json11.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class Json11Adapter:
|
||||
public BasicAdapter<Json11Adapter,
|
||||
Json11Array,
|
||||
Json11ObjectMember,
|
||||
Json11Object,
|
||||
Json11Value>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a Json11Adapter that contains an empty object
|
||||
Json11Adapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a Json11Adapter containing a specific Json11 value
|
||||
Json11Adapter(const json11::Json &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* Json11Adapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see Json11Array
|
||||
*/
|
||||
class Json11ArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
Json11Adapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new Json11ArrayValueIterator using an existing
|
||||
* Json11 iterator.
|
||||
*
|
||||
* @param itr Json11 iterator to store
|
||||
*/
|
||||
Json11ArrayValueIterator(
|
||||
const json11::Json::array::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a Json11Adapter that contains the value of the current
|
||||
/// element.
|
||||
Json11Adapter operator*() const
|
||||
{
|
||||
return Json11Adapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<Json11Adapter> operator->() const
|
||||
{
|
||||
return DerefProxy<Json11Adapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const Json11ArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const Json11ArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const Json11ArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Json11ArrayValueIterator operator++(int)
|
||||
{
|
||||
Json11ArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const Json11ArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
json11::Json::array::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of Json11ObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see Json11Object
|
||||
* @see Json11ObjectMember
|
||||
*/
|
||||
class Json11ObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
Json11ObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a Json11 iterator.
|
||||
*
|
||||
* @param itr Json11 iterator to store
|
||||
*/
|
||||
Json11ObjectMemberIterator(
|
||||
const json11::Json::object::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a Json11ObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
Json11ObjectMember operator*() const
|
||||
{
|
||||
return Json11ObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<Json11ObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<Json11ObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const Json11ObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const Json11ObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const Json11ObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Json11ObjectMemberIterator operator++(int)
|
||||
{
|
||||
Json11ObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const Json11ObjectMemberIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original Json11 iterator
|
||||
json11::Json::object::const_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for Json11Adapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::Json11Adapter>
|
||||
{
|
||||
typedef json11::Json DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "Json11Adapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool Json11FrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return Json11Adapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline Json11ArrayValueIterator Json11Array::begin() const
|
||||
{
|
||||
return value.array_items().begin();
|
||||
}
|
||||
|
||||
inline Json11ArrayValueIterator Json11Array::end() const
|
||||
{
|
||||
return value.array_items().end();
|
||||
}
|
||||
|
||||
inline Json11ObjectMemberIterator Json11Object::begin() const
|
||||
{
|
||||
return value.object_items().begin();
|
||||
}
|
||||
|
||||
inline Json11ObjectMemberIterator Json11Object::end() const
|
||||
{
|
||||
return value.object_items().end();
|
||||
}
|
||||
|
||||
inline Json11ObjectMemberIterator Json11Object::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
return value.object_items().find(propertyName);
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,724 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the JsonCpp parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for JsonCpp.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - JsonCppAdapter
|
||||
* - JsonCppArray
|
||||
* - JsonCppArrayValueIterator
|
||||
* - JsonCppFrozenValue
|
||||
* - JsonCppObject
|
||||
* - JsonCppObjectMember
|
||||
* - JsonCppObjectMemberIterator
|
||||
* - JsonCppValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is JsonCppAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_JSONCPP_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_JSONCPP_ADAPTER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class JsonCppAdapter;
|
||||
class JsonCppArrayValueIterator;
|
||||
class JsonCppObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, JsonCppAdapter> JsonCppObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp array value.
|
||||
*
|
||||
* This class is light weight wrapper for a JsonCpp array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* JsonCpp value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class JsonCppArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef JsonCppArrayValueIterator const_iterator;
|
||||
typedef JsonCppArrayValueIterator iterator;
|
||||
|
||||
/// Construct a JsonCppArray referencing an empty array.
|
||||
JsonCppArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a JsonCppArray referencing a specific JsonCpp value.
|
||||
*
|
||||
* @param value reference to a JsonCpp value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
JsonCppArray(const Json::Value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.isArray()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array.
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a JsonCpp value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const Json::Value & emptyArray()
|
||||
{
|
||||
static const Json::Value array(Json::arrayValue);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained array
|
||||
const Json::Value &value;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp object.
|
||||
*
|
||||
* This class is light weight wrapper for a JsonCpp object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* JsonCpp object, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class JsonCppObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef JsonCppObjectMemberIterator const_iterator;
|
||||
typedef JsonCppObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a JsonCppObject referencing an empty object singleton.
|
||||
JsonCppObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a JsonCppObject referencing a specific JsonCpp value.
|
||||
*
|
||||
* @param value reference to a JsonCpp value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
JsonCppObject(const Json::Value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.isObject()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for a member/property with the given name
|
||||
*
|
||||
* @param propertyName Property name
|
||||
*
|
||||
* @returns a valid iterator if found, or an invalid iterator if not found
|
||||
*/
|
||||
JsonCppObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Return the number of members in the object
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty JsonCpp object
|
||||
static const Json::Value & emptyObject()
|
||||
{
|
||||
static const Json::Value object(Json::objectValue);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const Json::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a JsonCpp value.
|
||||
*
|
||||
* This class allows a JsonCpp value to be stored independent of its original
|
||||
* document. JsonCpp makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class JsonCppFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a JsonCpp value
|
||||
*
|
||||
* @param source the JsonCpp value to be copied
|
||||
*/
|
||||
explicit JsonCppFrozenValue(const Json::Value &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new JsonCppFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored JsonCpp value
|
||||
Json::Value value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a JsonCpp value. This class is responsible
|
||||
* for the mechanics of actually reading a JsonCpp value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class JsonCppValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
JsonCppValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific JsonCpp value
|
||||
JsonCppValue(const Json::Value &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new JsonCppFrozenValue instance that contains the
|
||||
* value referenced by this JsonCppValue instance.
|
||||
*
|
||||
* @returns pointer to a new JsonCppFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new JsonCppFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a JsonCppArray instance.
|
||||
*
|
||||
* If the referenced JsonCpp value is an array, this function will return a
|
||||
* std::optional containing a JsonCppArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<JsonCppArray> getArrayOptional() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return opt::make_optional(JsonCppArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<JsonCppArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced JsonCpp value is an array, this function will retrieve
|
||||
* the number of elements in the array and store it in the output variable
|
||||
* provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
result = value.asBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
result = value.asDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isIntegral()) {
|
||||
result = static_cast<int64_t>(value.asInt());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a JsonCppObject instance.
|
||||
*
|
||||
* If the referenced JsonCpp value is an object, this function will return a
|
||||
* std::optional containing a JsonCppObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<JsonCppObject> getObjectOptional() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return opt::make_optional(JsonCppObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<JsonCppObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced JsonCpp value is an object, this function will retrieve
|
||||
* the number of members in the object and store it in the output variable
|
||||
* provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
result = value.asString();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.isArray() && !value.isNull();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.isBool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.isIntegral() && !value.isBool();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.isNumeric() && !value.isBool();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.isObject() && !value.isNull();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton.
|
||||
static const Json::Value &emptyObject()
|
||||
{
|
||||
static Json::Value object(Json::objectValue);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained JsonCpp value
|
||||
const Json::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting JsonCpp.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class JsonCppAdapter:
|
||||
public BasicAdapter<JsonCppAdapter,
|
||||
JsonCppArray,
|
||||
JsonCppObjectMember,
|
||||
JsonCppObject,
|
||||
JsonCppValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a JsonCppAdapter that contains an empty object
|
||||
JsonCppAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a JsonCppAdapter containing a specific JsonCpp value
|
||||
JsonCppAdapter(const Json::Value &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* JsonCppAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see JsonCppArray
|
||||
*/
|
||||
class JsonCppArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
JsonCppAdapter> // value type
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new JsonCppArrayValueIterator using an existing
|
||||
* JsonCpp iterator.
|
||||
*
|
||||
* @param itr JsonCpp iterator to store
|
||||
*/
|
||||
JsonCppArrayValueIterator(const Json::Value::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a JsonCppAdapter that contains the value of the current element.
|
||||
JsonCppAdapter operator*() const
|
||||
{
|
||||
return JsonCppAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<JsonCppAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<JsonCppAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const JsonCppArrayValueIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const JsonCppArrayValueIterator &rhs) const
|
||||
{
|
||||
return !(itr == rhs.itr);
|
||||
}
|
||||
|
||||
JsonCppArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonCppArrayValueIterator operator++(int)
|
||||
{
|
||||
JsonCppArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
JsonCppArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
if (n > 0) {
|
||||
while (n-- > 0) {
|
||||
itr++;
|
||||
}
|
||||
} else {
|
||||
while (n++ < 0) {
|
||||
itr--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Json::Value::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of JsonCppObjectMember representing one of the members of the object. It has
|
||||
* been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see JsonCppObject
|
||||
* @see JsonCppObjectMember
|
||||
*/
|
||||
class JsonCppObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
JsonCppObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a JsonCpp iterator.
|
||||
*
|
||||
* @param itr JsonCpp iterator to store
|
||||
*/
|
||||
JsonCppObjectMemberIterator(const Json::ValueConstIterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a JsonCppObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
JsonCppObjectMember operator*() const
|
||||
{
|
||||
return JsonCppObjectMember(itr.key().asString(), *itr);
|
||||
}
|
||||
|
||||
DerefProxy<JsonCppObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<JsonCppObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const JsonCppObjectMemberIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const JsonCppObjectMemberIterator &rhs) const
|
||||
{
|
||||
return !(itr == rhs.itr);
|
||||
}
|
||||
|
||||
const JsonCppObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonCppObjectMemberIterator operator++(int)
|
||||
{
|
||||
JsonCppObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
JsonCppObjectMemberIterator operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original JsonCpp iterator
|
||||
Json::ValueConstIterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for JsonCppAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::JsonCppAdapter>
|
||||
{
|
||||
typedef Json::Value DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "JsonCppAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool JsonCppFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return JsonCppAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline JsonCppArrayValueIterator JsonCppArray::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline JsonCppArrayValueIterator JsonCppArray::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
if (value.isMember(propertyName)) {
|
||||
Json::ValueConstIterator itr;
|
||||
for ( itr = value.begin(); itr != value.end(); ++itr) {
|
||||
if (itr.key() == propertyName) {
|
||||
return itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value.end();
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,712 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the nlohmann json parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for nlohmann json.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - NlohmannJsonAdapter
|
||||
* - NlohmannJsonArray
|
||||
* - NlohmannJsonValueIterator
|
||||
* - NlohmannJsonFrozenValue
|
||||
* - NlohmannJsonObject
|
||||
* - NlohmannJsonObjectMember
|
||||
* - NlohmannJsonObjectMemberIterator
|
||||
* - NlohmannJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is NlohmannJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_NLOHMANN_JSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_NLOHMANN_JSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <json.hpp>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class NlohmannJsonAdapter;
|
||||
class NlohmannJsonArrayValueIterator;
|
||||
class NlohmannJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, NlohmannJsonAdapter> NlohmannJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a NlohmannJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a NlohmannJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* NlohmannJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class NlohmannJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef NlohmannJsonArrayValueIterator const_iterator;
|
||||
typedef NlohmannJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a NlohmannJsonArray referencing an empty array.
|
||||
NlohmannJsonArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a NlohmannJsonArray referencing a specific NlohmannJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a NlohmannJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
NlohmannJsonArray(const nlohmann::json &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is_array()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying NlohmannJson implementation.
|
||||
*/
|
||||
NlohmannJsonArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying NlohmannJson implementation.
|
||||
*/
|
||||
NlohmannJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a NlohmannJson value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const nlohmann::json & emptyArray()
|
||||
{
|
||||
static const nlohmann::json array = nlohmann::json::array();
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const nlohmann::json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a NlohmannJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a NlohmannJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* NlohmannJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class NlohmannJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef NlohmannJsonObjectMemberIterator const_iterator;
|
||||
typedef NlohmannJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a NlohmannJsonObject referencing an empty object singleton.
|
||||
NlohmannJsonObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a NlohmannJsonObject referencing a specific NlohmannJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a NlohmannJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
NlohmannJsonObject(const nlohmann::json &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is_object()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying NlohmannJson implementation.
|
||||
*/
|
||||
NlohmannJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying NlohmannJson implementation.
|
||||
*/
|
||||
NlohmannJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
NlohmannJsonObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a NlohmannJson value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const nlohmann::json & emptyObject()
|
||||
{
|
||||
static const nlohmann::json object = nlohmann::json::object();
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const nlohmann::json &value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a NlohmannJson value.
|
||||
*
|
||||
* This class allows a NlohmannJson value to be stored independent of its original
|
||||
* document. NlohmannJson makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class NlohmannJsonFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a NlohmannJson value
|
||||
*
|
||||
* @param source the NlohmannJson value to be copied
|
||||
*/
|
||||
explicit NlohmannJsonFrozenValue(const nlohmann::json &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new NlohmannJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored NlohmannJson value
|
||||
nlohmann::json value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a NlohmannJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a NlohmannJson value. This class is responsible
|
||||
* for the mechanics of actually reading a NlohmannJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class NlohmannJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
NlohmannJsonValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific NlohmannJson value
|
||||
NlohmannJsonValue(const nlohmann::json &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new NlohmannJsonFrozenValue instance that contains the
|
||||
* value referenced by this NlohmannJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new NlohmannJsonFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new NlohmannJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a NlohmannJsonArray instance.
|
||||
*
|
||||
* If the referenced NlohmannJson value is an array, this function will return
|
||||
* a std::optional containing a NlohmannJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<NlohmannJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.is_array()) {
|
||||
return opt::make_optional(NlohmannJsonArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<NlohmannJsonArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced NlohmannJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.is_array()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.is_boolean()) {
|
||||
result = value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.is_number_float()) {
|
||||
result = value.get<double>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if(value.is_number_integer()) {
|
||||
result = value.get<int64_t>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a NlohmannJsonObject instance.
|
||||
*
|
||||
* If the referenced NlohmannJson value is an object, this function will return a
|
||||
* std::optional containing a NlohmannJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<NlohmannJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.is_object()) {
|
||||
return opt::make_optional(NlohmannJsonObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<NlohmannJsonObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced NlohmannJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.is_object()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.is_string()) {
|
||||
result = value.get<std::string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.is_array();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.is_boolean();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.is_number_float();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.is_number_integer();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.is_null();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.is_number();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.is_object();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.is_string();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const nlohmann::json & emptyObject()
|
||||
{
|
||||
static const nlohmann::json object = nlohmann::json::object();
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained NlohmannJson value.
|
||||
const nlohmann::json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting NlohmannJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class NlohmannJsonAdapter:
|
||||
public BasicAdapter<NlohmannJsonAdapter,
|
||||
NlohmannJsonArray,
|
||||
NlohmannJsonObjectMember,
|
||||
NlohmannJsonObject,
|
||||
NlohmannJsonValue>
|
||||
{
|
||||
public:
|
||||
/// Construct a NlohmannJsonAdapter that contains an empty object
|
||||
NlohmannJsonAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a NlohmannJsonAdapter containing a specific Nlohmann Json object
|
||||
NlohmannJsonAdapter(const nlohmann::json &value)
|
||||
: BasicAdapter(NlohmannJsonValue{value}) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* NlohmannJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see NlohmannJsonArray
|
||||
*/
|
||||
class NlohmannJsonArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
NlohmannJsonAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new NlohmannJsonArrayValueIterator using an existing
|
||||
* NlohmannJson iterator.
|
||||
*
|
||||
* @param itr NlohmannJson iterator to store
|
||||
*/
|
||||
NlohmannJsonArrayValueIterator(const nlohmann::json::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a NlohmannJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
NlohmannJsonAdapter operator*() const
|
||||
{
|
||||
return NlohmannJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<NlohmannJsonAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<NlohmannJsonAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const NlohmannJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const NlohmannJsonArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const NlohmannJsonArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NlohmannJsonArrayValueIterator operator++(int)
|
||||
{
|
||||
NlohmannJsonArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const NlohmannJsonArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
nlohmann::json::const_iterator itr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of NlohmannJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see NlohmannJsonObject
|
||||
* @see NlohmannJsonObjectMember
|
||||
*/
|
||||
class NlohmannJsonObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
NlohmannJsonObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a NlohmannJson iterator.
|
||||
*
|
||||
* @param itr NlohmannJson iterator to store
|
||||
*/
|
||||
NlohmannJsonObjectMemberIterator(const nlohmann::json::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a NlohmannJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
NlohmannJsonObjectMember operator*() const
|
||||
{
|
||||
return NlohmannJsonObjectMember(itr.key(), itr.value());
|
||||
}
|
||||
|
||||
DerefProxy<NlohmannJsonObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<NlohmannJsonObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const NlohmannJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const NlohmannJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const NlohmannJsonObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NlohmannJsonObjectMemberIterator operator++(int)
|
||||
{
|
||||
NlohmannJsonObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const NlohmannJsonObjectMemberIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original NlohmannJson iterator
|
||||
nlohmann::json::const_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for NlohmannJsonAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::NlohmannJsonAdapter>
|
||||
{
|
||||
typedef nlohmann::json DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "NlohmannJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool NlohmannJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return NlohmannJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline NlohmannJsonArrayValueIterator NlohmannJsonArray::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline NlohmannJsonArrayValueIterator NlohmannJsonArray::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
return value.find(propertyName);
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,727 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the PicoJson parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for PicoJson.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - PicoJsonAdapter
|
||||
* - PicoJsonArray
|
||||
* - PicoJsonArrayValueIterator
|
||||
* - PicoJsonFrozenValue
|
||||
* - PicoJsonObject
|
||||
* - PicoJsonObjectMember
|
||||
* - PicoJsonObjectMemberIterator
|
||||
* - PicoJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is PicoJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_PICOJSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_PICOJSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class PicoJsonAdapter;
|
||||
class PicoJsonArrayValueIterator;
|
||||
class PicoJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, PicoJsonAdapter> PicoJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PicoJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a PicoJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* PicoJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PicoJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PicoJsonArrayValueIterator const_iterator;
|
||||
typedef PicoJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a PicoJsonArray referencing an empty array.
|
||||
PicoJsonArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PicoJsonArray referencing a specific PicoJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a PicoJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
explicit PicoJsonArray(const picojson::value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is<picojson::array>()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying PicoJson implementation.
|
||||
*/
|
||||
PicoJsonArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying PicoJson implementation.
|
||||
*/
|
||||
PicoJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
const picojson::array &array = value.get<picojson::array>();
|
||||
return array.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a PicoJson value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const picojson::value & emptyArray()
|
||||
{
|
||||
static const picojson::value array(picojson::array_type, false);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const picojson::value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PicoJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a PicoJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* PicoJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PicoJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PicoJsonObjectMemberIterator const_iterator;
|
||||
typedef PicoJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a PicoJsonObject referencing an empty object singleton.
|
||||
PicoJsonObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PicoJsonObject referencing a specific PicoJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a PicoJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
PicoJsonObject(const picojson::value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is<picojson::object>()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying PicoJson implementation.
|
||||
*/
|
||||
PicoJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying PicoJson implementation.
|
||||
*/
|
||||
PicoJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
PicoJsonObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
return object.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a PicoJson value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const picojson::value & emptyObject()
|
||||
{
|
||||
static const picojson::value object(picojson::object_type, false);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const picojson::value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a PicoJson value.
|
||||
*
|
||||
* This class allows a PicoJson value to be stored independent of its original
|
||||
* document. PicoJson makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class PicoJsonFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a PicoJson value
|
||||
*
|
||||
* @param source the PicoJson value to be copied
|
||||
*/
|
||||
explicit PicoJsonFrozenValue(const picojson::value &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new PicoJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored PicoJson value
|
||||
picojson::value value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PicoJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a PicoJson value. This class is responsible
|
||||
* for the mechanics of actually reading a PicoJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PicoJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
PicoJsonValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific PicoJson value
|
||||
PicoJsonValue(const picojson::value &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new PicoJsonFrozenValue instance that contains the
|
||||
* value referenced by this PicoJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new PicoJsonFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new PicoJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PicoJsonArray instance.
|
||||
*
|
||||
* If the referenced PicoJson value is an array, this function will return
|
||||
* a std::optional containing a PicoJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PicoJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.is<picojson::array>()) {
|
||||
return opt::make_optional(PicoJsonArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<PicoJsonArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced PicoJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.is<picojson::array>()) {
|
||||
const picojson::array& array = value.get<picojson::array>();
|
||||
result = array.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.is<bool>()) {
|
||||
result = value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.is<double>()) {
|
||||
result = value.get<double>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.is<int64_t>()) {
|
||||
result = value.get<int64_t>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PicoJsonObject instance.
|
||||
*
|
||||
* If the referenced PicoJson value is an object, this function will return a
|
||||
* std::optional containing a PicoJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PicoJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.is<picojson::object>()) {
|
||||
return opt::make_optional(PicoJsonObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<PicoJsonObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced PicoJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.is<picojson::object>()) {
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
result = object.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.is<std::string>()) {
|
||||
result = value.get<std::string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.is<picojson::array>();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.is<bool>();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
if (value.is<int64_t>()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return value.is<double>();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.is<int64_t>();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.is<picojson::null>();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.is<double>();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.is<picojson::object>();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.is<std::string>();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const picojson::value & emptyObject()
|
||||
{
|
||||
static const picojson::value object(picojson::object_type, false);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained PicoJson value.
|
||||
const picojson::value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting PicoJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PicoJsonAdapter:
|
||||
public BasicAdapter<PicoJsonAdapter,
|
||||
PicoJsonArray,
|
||||
PicoJsonObjectMember,
|
||||
PicoJsonObject,
|
||||
PicoJsonValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a PicoJsonAdapter that contains an empty object
|
||||
PicoJsonAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a PicoJsonAdapter containing a specific PicoJson value
|
||||
PicoJsonAdapter(const picojson::value &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* PicoJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the std::iterator template.
|
||||
*
|
||||
* @see PicoJsonArray
|
||||
*/
|
||||
class PicoJsonArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PicoJsonAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new PicoJsonArrayValueIterator using an existing
|
||||
* PicoJson iterator.
|
||||
*
|
||||
* @param itr PicoJson iterator to store
|
||||
*/
|
||||
PicoJsonArrayValueIterator(
|
||||
const picojson::array::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a PicoJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
PicoJsonAdapter operator*() const
|
||||
{
|
||||
return PicoJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<PicoJsonAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<PicoJsonAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const PicoJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PicoJsonArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const PicoJsonArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PicoJsonArrayValueIterator operator++(int)
|
||||
{
|
||||
PicoJsonArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PicoJsonArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
picojson::array::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of PicoJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PicoJsonObject
|
||||
* @see PicoJsonObjectMember
|
||||
*/
|
||||
class PicoJsonObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PicoJsonObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a PicoJson iterator.
|
||||
*
|
||||
* @param itr PicoJson iterator to store
|
||||
*/
|
||||
PicoJsonObjectMemberIterator(
|
||||
const picojson::object::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a PicoJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
PicoJsonObjectMember operator*() const
|
||||
{
|
||||
return PicoJsonObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<PicoJsonObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<PicoJsonObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const PicoJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PicoJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const PicoJsonObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PicoJsonObjectMemberIterator operator++(int)
|
||||
{
|
||||
PicoJsonObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PicoJsonObjectMemberIterator& operator--(int)
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original PicoJson iterator
|
||||
picojson::object::const_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for PicoJsonAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::PicoJsonAdapter>
|
||||
{
|
||||
typedef picojson::value DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "PicoJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool PicoJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return PicoJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline PicoJsonArrayValueIterator PicoJsonArray::begin() const
|
||||
{
|
||||
const picojson::array &array = value.get<picojson::array>();
|
||||
return array.begin();
|
||||
}
|
||||
|
||||
inline PicoJsonArrayValueIterator PicoJsonArray::end() const
|
||||
{
|
||||
const picojson::array &array = value.get<picojson::array>();
|
||||
return array.end();
|
||||
}
|
||||
|
||||
inline PicoJsonObjectMemberIterator PicoJsonObject::begin() const
|
||||
{
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
return object.begin();
|
||||
}
|
||||
|
||||
inline PicoJsonObjectMemberIterator PicoJsonObject::end() const
|
||||
{
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
return object.end();
|
||||
}
|
||||
|
||||
inline PicoJsonObjectMemberIterator PicoJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
return object.find(propertyName);
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,728 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the Poco json parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for Poco json.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - PocoJsonAdapter
|
||||
* - PocoJsonArray
|
||||
* - PocoJsonValueIterator
|
||||
* - PocoJsonFrozenValue
|
||||
* - PocoJsonObject
|
||||
* - PocoJsonObjectMember
|
||||
* - PocoJsonObjectMemberIterator
|
||||
* - PocoJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is PocoJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_POCO_JSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_POCO_JSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <Poco/JSON/Object.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson
|
||||
{
|
||||
namespace adapters
|
||||
{
|
||||
|
||||
class PocoJsonAdapter;
|
||||
class PocoJsonArrayValueIterator;
|
||||
class PocoJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, PocoJsonAdapter> PocoJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PocoJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a PocoJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* PocoJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PocoJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PocoJsonArrayValueIterator const_iterator;
|
||||
typedef PocoJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a PocoJsonArray referencing an empty array.
|
||||
PocoJsonArray()
|
||||
: value(emptyArray())
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Construct a PocoJsonArray referencing a specific PocoJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a PocoJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
PocoJsonArray(const Poco::Dynamic::Var &value)
|
||||
: value(value)
|
||||
{
|
||||
if (value.type() != typeid(Poco::JSON::Array::Ptr)) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying PocoJson implementation.
|
||||
*/
|
||||
PocoJsonArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying PocoJson implementation.
|
||||
*/
|
||||
PocoJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Array::Ptr>()->size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a PocoJson value that is an empty array.
|
||||
*/
|
||||
static Poco::Dynamic::Var emptyArray()
|
||||
{
|
||||
Poco::Dynamic::Var array = Poco::JSON::Array::Ptr();
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Contained value
|
||||
Poco::Dynamic::Var value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PocoJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a PocoJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* PocoJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PocoJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PocoJsonObjectMemberIterator const_iterator;
|
||||
typedef PocoJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a PocoJsonObject an empty object.
|
||||
PocoJsonObject()
|
||||
: value(emptyObject())
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Construct a PocoJsonObject referencing a specific PocoJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a PocoJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
PocoJsonObject(const Poco::Dynamic::Var &value)
|
||||
: value(value)
|
||||
{
|
||||
if (value.type() != typeid(Poco::JSON::Object::Ptr)) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying PocoJson implementation.
|
||||
*/
|
||||
PocoJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying PocoJson implementation.
|
||||
*/
|
||||
PocoJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
PocoJsonObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Object::Ptr>()->size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a PocoJson value that is empty object.
|
||||
*/
|
||||
static Poco::Dynamic::Var emptyObject()
|
||||
{
|
||||
Poco::Dynamic::Var object = Poco::JSON::Object::Ptr();
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Contained value
|
||||
Poco::Dynamic::Var value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a PocoJson value.
|
||||
*
|
||||
* This class allows a PocoJson value to be stored independent of its original
|
||||
* document. PocoJson makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class PocoJsonFrozenValue : public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a PocoJson value
|
||||
*
|
||||
* @param source the PocoJson value to be copied
|
||||
*/
|
||||
explicit PocoJsonFrozenValue(const Poco::Dynamic::Var &source)
|
||||
: value(source)
|
||||
{ }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new PocoJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored PocoJson value
|
||||
Poco::Dynamic::Var value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PocoJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a PocoJson value. This class is responsible
|
||||
* for the mechanics of actually reading a PocoJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PocoJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object
|
||||
PocoJsonValue()
|
||||
: value(emptyObject())
|
||||
{ }
|
||||
|
||||
/// Construct a wrapper for a specific PocoJson value
|
||||
PocoJsonValue(const Poco::Dynamic::Var& value)
|
||||
: value(value)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Create a new PocoJsonFrozenValue instance that contains the
|
||||
* value referenced by this PocoJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new PocoJsonFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new PocoJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PocoJsonArray instance.
|
||||
*
|
||||
* If the referenced PocoJson value is an array, this function will return
|
||||
* a std::optional containing a PocoJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PocoJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.type() == typeid(Poco::JSON::Array::Ptr)) {
|
||||
return opt::make_optional(PocoJsonArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<PocoJsonArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced PocoJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.type() == typeid(Poco::JSON::Array::Ptr)) {
|
||||
result = value.extract<Poco::JSON::Array::Ptr>()->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.isBoolean()) {
|
||||
result = value.convert<bool>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.isNumeric() && !value.isInteger()) {
|
||||
result = value.convert<double>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isInteger()) {
|
||||
result = value.convert<int>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PocoJsonObject instance.
|
||||
*
|
||||
* If the referenced PocoJson value is an object, this function will return a
|
||||
* std::optional containing a PocoJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PocoJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.type() == typeid(Poco::JSON::Object::Ptr)) {
|
||||
return opt::make_optional(PocoJsonObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<PocoJsonObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced PocoJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.type() == typeid(Poco::JSON::Object::Ptr)) {
|
||||
result = value.extract<Poco::JSON::Object::Ptr>()->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
result = value.convert<std::string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.type() == typeid(Poco::JSON::Array::Ptr);
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.isBoolean();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.isNumeric() && !value.isInteger();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return !isBool() && value.isInteger();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.isEmpty();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.isNumeric();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.type() == typeid(Poco::JSON::Object::Ptr);
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return an empty object
|
||||
static Poco::Dynamic::Var emptyObject()
|
||||
{
|
||||
Poco::Dynamic::Var object = Poco::JSON::Object::Ptr();
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Contained value
|
||||
Poco::Dynamic::Var value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting PocoJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PocoJsonAdapter :
|
||||
public BasicAdapter<PocoJsonAdapter,
|
||||
PocoJsonArray,
|
||||
PocoJsonObjectMember,
|
||||
PocoJsonObject,
|
||||
PocoJsonValue>
|
||||
{
|
||||
public:
|
||||
/// Construct a PocoJsonAdapter that contains an empty object
|
||||
PocoJsonAdapter()
|
||||
: BasicAdapter()
|
||||
{ }
|
||||
|
||||
/// Construct a PocoJsonAdapter containing a specific Poco Json object
|
||||
PocoJsonAdapter(const Poco::Dynamic::Var &value)
|
||||
: BasicAdapter(PocoJsonValue {value})
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* PocoJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PocoJsonArray
|
||||
*/
|
||||
class PocoJsonArrayValueIterator :
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PocoJsonAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new PocoJsonArrayValueIterator using an existing
|
||||
* PocoJson iterator.
|
||||
*
|
||||
* @param itr PocoJson iterator to store
|
||||
*/
|
||||
PocoJsonArrayValueIterator(const Poco::JSON::Array::ConstIterator &itr)
|
||||
: itr(itr)
|
||||
{ }
|
||||
|
||||
/// Returns a PocoJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
PocoJsonAdapter operator*() const
|
||||
{
|
||||
return PocoJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<PocoJsonAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<PocoJsonAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const PocoJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PocoJsonArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const PocoJsonArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PocoJsonArrayValueIterator operator++(int)
|
||||
{
|
||||
PocoJsonArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PocoJsonArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
Poco::JSON::Array::ConstIterator itr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of PocoJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PocoJsonObject
|
||||
* @see PocoJsonObjectMember
|
||||
*/
|
||||
class PocoJsonObjectMemberIterator :
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PocoJsonObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a PocoJson iterator.
|
||||
*
|
||||
* @param itr PocoJson iterator to store
|
||||
*/
|
||||
PocoJsonObjectMemberIterator(const Poco::JSON::Object::ConstIterator &itr)
|
||||
: itr(itr)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Returns a PocoJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
PocoJsonObjectMember operator*() const
|
||||
{
|
||||
return PocoJsonObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<PocoJsonObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<PocoJsonObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const PocoJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PocoJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const PocoJsonObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PocoJsonObjectMemberIterator operator++(int)
|
||||
{
|
||||
PocoJsonObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PocoJsonObjectMemberIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original PocoJson iterator
|
||||
Poco::JSON::Object::ConstIterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for PocoJsonAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::PocoJsonAdapter>
|
||||
{
|
||||
typedef Poco::Dynamic::Var DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "PocoJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline PocoJsonArrayValueIterator PocoJsonArray::begin() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Array::Ptr>()->begin();
|
||||
}
|
||||
|
||||
inline PocoJsonArrayValueIterator PocoJsonArray::end() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Array::Ptr>()->end();
|
||||
}
|
||||
|
||||
inline PocoJsonObjectMemberIterator PocoJsonObject::begin() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Object::Ptr>()->begin();
|
||||
}
|
||||
|
||||
inline PocoJsonObjectMemberIterator PocoJsonObject::end() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Object::Ptr>()->end();
|
||||
}
|
||||
|
||||
inline PocoJsonObjectMemberIterator PocoJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
auto& ptr = value.extract<Poco::JSON::Object::Ptr>();
|
||||
|
||||
auto it = std::find_if(ptr->begin(), ptr->end(),
|
||||
[&propertyName](const Poco::JSON::Object::ValueType& p)
|
||||
{
|
||||
return p.first == propertyName;
|
||||
});
|
||||
return it;
|
||||
}
|
||||
|
||||
inline bool PocoJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return PocoJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif // __VALIJSON_ADAPTERS_POCO_JSON_ADAPTER_HPP
|
||||
|
||||
@@ -0,0 +1,757 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the Boost property tree library.
|
||||
*
|
||||
* Include this file in your program to enable support for boost property trees.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - PropertyTreeAdapter
|
||||
* - PropertyTreeArray
|
||||
* - PropertyTreeArrayValueIterator
|
||||
* - PropertyTreeFrozenValue
|
||||
* - PropertyTreeObject
|
||||
* - PropertyTreeObjectMember
|
||||
* - PropertyTreeObjectMemberIterator
|
||||
* - PropertyTreeValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is PropertyTreeAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_PROPERTY_TREE_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_PROPERTY_TREE_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class PropertyTreeAdapter;
|
||||
class PropertyTreeArrayValueIterator;
|
||||
class PropertyTreeObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, PropertyTreeAdapter> PropertyTreeObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree that contains
|
||||
* array-like data.
|
||||
*
|
||||
* This class is light weight wrapper for a Boost property tree. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to a Boost property
|
||||
* tree that is assumed to contain unnamed key-value pairs. There is very little
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PropertyTreeArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PropertyTreeArrayValueIterator const_iterator;
|
||||
typedef PropertyTreeArrayValueIterator iterator;
|
||||
|
||||
/// Construct a PropertyTreeArra7 referencing an empty property tree
|
||||
/// singleton.
|
||||
PropertyTreeArray()
|
||||
: array(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct PropertyTreeArray referencing a specific Boost
|
||||
* property tree.
|
||||
*
|
||||
* @param array reference to a property tree containing an array
|
||||
*
|
||||
* It is assumed that this value contains array-like data, but this is not
|
||||
* checked due to runtime cost.
|
||||
*/
|
||||
explicit PropertyTreeArray(const boost::property_tree::ptree &array)
|
||||
: array(array) { }
|
||||
|
||||
/// Return an iterator for the first element in the array.
|
||||
PropertyTreeArrayValueIterator begin() const;
|
||||
|
||||
/// Return an iterator for one-past the last element of the array.
|
||||
PropertyTreeArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return array.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a property tree that looks like an
|
||||
* empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const boost::property_tree::ptree &array;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree that contains
|
||||
* object-like data.
|
||||
*
|
||||
* This class is light weight wrapper for a Boost property tree. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* property tree value, assumed to be object-like, so there is very little
|
||||
* overhead associated with copy construction and passing by value.
|
||||
*/
|
||||
class PropertyTreeObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PropertyTreeObjectMemberIterator const_iterator;
|
||||
typedef PropertyTreeObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a PropertyTreeObject referencing an empty property tree.
|
||||
PropertyTreeObject()
|
||||
: object(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PropertyTreeObject referencing a specific property
|
||||
* tree.
|
||||
*
|
||||
* @param object reference to a property tree containing an object
|
||||
*
|
||||
* Note that the value of the property tree is not checked, due to the
|
||||
* runtime cost of doing so.
|
||||
*/
|
||||
PropertyTreeObject(const boost::property_tree::ptree &object)
|
||||
: object(object) { }
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying property tree
|
||||
* implementation.
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the pointer value returned by the underlying property tree
|
||||
* implementation.
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param property property name to search for
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator find(const std::string &property) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return object.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to an empty property tree.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const boost::property_tree::ptree &object;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a Boost property tree.
|
||||
*
|
||||
* This class allows a property tree value to be stored independent of its
|
||||
* original 'document'. Boost property trees make this easy to do, as they do
|
||||
* not perform any custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class PropertyTreeFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a Boost property tree POD value
|
||||
*
|
||||
* @param source string containing the POD vlaue
|
||||
*/
|
||||
explicit PropertyTreeFrozenValue(
|
||||
const boost::property_tree::ptree::data_type &source)
|
||||
: value(source) { }
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a Boost property tree object or array value
|
||||
*
|
||||
* @param source the property tree to be copied
|
||||
*/
|
||||
explicit PropertyTreeFrozenValue(
|
||||
const boost::property_tree::ptree &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new PropertyTreeFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored value
|
||||
boost::property_tree::ptree value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a Boost property tree value. This class
|
||||
* is responsible for the mechanics of actually reading a property tree, whereas
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PropertyTreeValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for an empty property tree
|
||||
PropertyTreeValue()
|
||||
: object(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PropertyTreeValue from a tree object
|
||||
*
|
||||
* This function will determine whether the tree object represents an array
|
||||
* or an object by scanning the key names for any non-empty strings. In the
|
||||
* case of an empty tree object, it is not possible to determine whether it
|
||||
* is an array or an object, so it will be treated as an array by default.
|
||||
* Empty arrays are considered equal to empty objects when compared using
|
||||
* non-strict type comparison. Empty strings will also be stored as empty
|
||||
* arrays.
|
||||
*
|
||||
* @param tree Tree object to be wrapped
|
||||
*/
|
||||
PropertyTreeValue(const boost::property_tree::ptree &tree)
|
||||
{
|
||||
if (tree.data().empty()) { // No string content
|
||||
if (tree.size() == 0) { // No children
|
||||
array.emplace(tree); // Treat as empty array
|
||||
} else {
|
||||
bool isArray = true;
|
||||
boost::property_tree::ptree::const_iterator itr;
|
||||
for (itr = tree.begin(); itr != tree.end(); itr++) {
|
||||
if (!itr->first.empty()) {
|
||||
isArray = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isArray) {
|
||||
array.emplace(tree);
|
||||
} else {
|
||||
object.emplace(tree);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = tree.data();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new PropertyTreeFrozenValue instance that contains the
|
||||
* value referenced by this PropertyTreeValue instance.
|
||||
*
|
||||
* @returns pointer to a new PropertyTreeFrozenValue instance, belonging to
|
||||
* the caller.
|
||||
*/
|
||||
FrozenValue* freeze() const
|
||||
{
|
||||
if (array) {
|
||||
return new PropertyTreeFrozenValue(*array);
|
||||
} else if (object) {
|
||||
return new PropertyTreeFrozenValue(*object);
|
||||
} else {
|
||||
return new PropertyTreeFrozenValue(*value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an instance of PropertyTreeArrayAdapter.
|
||||
*
|
||||
* If the referenced property tree value is an array, this function will
|
||||
* return a std::optional containing a PropertyTreeArray instance
|
||||
* referencing the array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PropertyTreeArray> getArrayOptional() const
|
||||
{
|
||||
if (array) {
|
||||
return opt::make_optional(PropertyTreeArray(*array));
|
||||
}
|
||||
|
||||
return opt::optional<PropertyTreeArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced property tree value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (array) {
|
||||
result = array->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PropertyTreeObject instance.
|
||||
*
|
||||
* If the referenced property tree is an object, this function will return a
|
||||
* std::optional containing a PropertyTreeObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PropertyTreeObject> getObjectOptional() const
|
||||
{
|
||||
if (object) {
|
||||
return opt::make_optional(PropertyTreeObject(*object));
|
||||
}
|
||||
|
||||
return opt::optional<PropertyTreeObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced property tree value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (object) {
|
||||
result = object->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value) {
|
||||
result = *value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return static_cast<bool>(array);
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return static_cast<bool>(object);
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return static_cast<bool>(value);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference used if the value is known to be an array
|
||||
opt::optional<const boost::property_tree::ptree &> array;
|
||||
|
||||
/// Reference used if the value is known to be an object
|
||||
opt::optional<const boost::property_tree::ptree &> object;
|
||||
|
||||
/// Reference used if the value is known to be a POD type
|
||||
opt::optional<std::string> value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting the Boost
|
||||
* property tree library.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PropertyTreeAdapter:
|
||||
public BasicAdapter<PropertyTreeAdapter,
|
||||
PropertyTreeArray,
|
||||
PropertyTreeObjectMember,
|
||||
PropertyTreeObject,
|
||||
PropertyTreeValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a PropertyTreeAdapter for an empty property tree
|
||||
PropertyTreeAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a PropertyTreeAdapter using a specific property tree
|
||||
PropertyTreeAdapter(const boost::property_tree::ptree &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* PropertyTreeAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PropertyTreeArray
|
||||
*/
|
||||
class PropertyTreeArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PropertyTreeAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new PropertyTreeArrayValueIterator using an existing
|
||||
* property tree iterator.
|
||||
*
|
||||
* @param itr property tree iterator to store
|
||||
*/
|
||||
PropertyTreeArrayValueIterator(
|
||||
const boost::property_tree::ptree::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a PropertyTreeAdapter that contains the value of the current
|
||||
/// element.
|
||||
PropertyTreeAdapter operator*() const
|
||||
{
|
||||
return PropertyTreeAdapter(itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<PropertyTreeAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<PropertyTreeAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const PropertyTreeArrayValueIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PropertyTreeArrayValueIterator &rhs) const
|
||||
{
|
||||
return !(itr == rhs.itr);
|
||||
}
|
||||
|
||||
const PropertyTreeArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyTreeArrayValueIterator operator++(int)
|
||||
{
|
||||
PropertyTreeArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PropertyTreeArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
if (n > 0) {
|
||||
while (n-- > 0) {
|
||||
itr++;
|
||||
}
|
||||
} else {
|
||||
while (n++ < 0) {
|
||||
itr--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
boost::property_tree::ptree::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of PropertyTreeObjectMember representing one of the members of the object.
|
||||
* It has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PropertyTreeObject
|
||||
* @see PropertyTreeObjectMember
|
||||
*/
|
||||
class PropertyTreeObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PropertyTreeObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a PropertyTree iterator.
|
||||
*
|
||||
* @param itr PropertyTree iterator to store
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator(
|
||||
boost::property_tree::ptree::const_assoc_iterator itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a PropertyTreeObjectMember that contains the key and
|
||||
* value belonging to the object member identified by the iterator.
|
||||
*/
|
||||
PropertyTreeObjectMember operator*() const
|
||||
{
|
||||
return PropertyTreeObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<PropertyTreeObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<PropertyTreeObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const PropertyTreeObjectMemberIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PropertyTreeObjectMemberIterator &rhs) const
|
||||
{
|
||||
return !(itr == rhs.itr);
|
||||
}
|
||||
|
||||
const PropertyTreeObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyTreeObjectMemberIterator operator++(int)
|
||||
{
|
||||
PropertyTreeObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PropertyTreeObjectMemberIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
boost::property_tree::ptree::const_assoc_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for PropertyTreeAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::PropertyTreeAdapter>
|
||||
{
|
||||
typedef boost::property_tree::ptree DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "PropertyTreeAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool PropertyTreeFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return PropertyTreeAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline PropertyTreeArrayValueIterator PropertyTreeArray::begin() const
|
||||
{
|
||||
return array.begin();
|
||||
}
|
||||
|
||||
inline PropertyTreeArrayValueIterator PropertyTreeArray::end() const
|
||||
{
|
||||
return array.end();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::begin() const
|
||||
{
|
||||
return object.ordered_begin();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::end() const
|
||||
{
|
||||
return object.not_found();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
const boost::property_tree::ptree::const_assoc_iterator
|
||||
itr = object.find(propertyName);
|
||||
|
||||
if (itr != object.not_found()) {
|
||||
return itr;
|
||||
}
|
||||
|
||||
return object.not_found();
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,725 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the QtJson parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for QtJson.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - QtJsonAdapter
|
||||
* - QtJsonArray
|
||||
* - QtJsonArrayValueIterator
|
||||
* - QtJsonFrozenValue
|
||||
* - QtJsonObject
|
||||
* - QtJsonObjectMember
|
||||
* - QtJsonObjectMemberIterator
|
||||
* - QtJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is QtJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_QTJSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_QTJSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonArray>
|
||||
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class QtJsonAdapter;
|
||||
class QtJsonArrayValueIterator;
|
||||
class QtJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, QtJsonAdapter> QtJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a QtJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a QtJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* QtJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class QtJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef QtJsonArrayValueIterator const_iterator;
|
||||
typedef QtJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a QtJsonArray referencing an empty array.
|
||||
QtJsonArray()
|
||||
: value(emptyArray())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a QtJsonArray referencing a specific QtJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a QtJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
explicit QtJsonArray(const QJsonValue &value)
|
||||
: value(value.toArray())
|
||||
{
|
||||
if (!value.isArray()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying QtJson implementation.
|
||||
*/
|
||||
QtJsonArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying QtJson implementation.
|
||||
*/
|
||||
QtJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a QtJson value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const QJsonArray emptyArray()
|
||||
{
|
||||
static const QJsonArray array;
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const QJsonArray value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a QtJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a QtJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* QtJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class QtJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef QtJsonObjectMemberIterator const_iterator;
|
||||
typedef QtJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a QtJsonObject referencing an empty object singleton.
|
||||
QtJsonObject()
|
||||
: value(emptyObject())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a QtJsonObject referencing a specific QtJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a QtJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
QtJsonObject(const QJsonValue &value)
|
||||
: value(value.toObject())
|
||||
{
|
||||
if (!value.isObject()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying QtJson implementation.
|
||||
*/
|
||||
QtJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying QtJson implementation.
|
||||
*/
|
||||
QtJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
QtJsonObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a QtJson value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const QJsonObject emptyObject()
|
||||
{
|
||||
static const QJsonObject object;
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const QJsonObject value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a QtJson value.
|
||||
*
|
||||
* This class allows a QtJson value to be stored independent of its original
|
||||
* document. QtJson makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class QtJsonFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a QtJson value
|
||||
*
|
||||
* @param source the QtJson value to be copied
|
||||
*/
|
||||
explicit QtJsonFrozenValue(const QJsonValue &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new QtJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored QtJson value
|
||||
QJsonValue value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a QtJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a QtJson value. This class is responsible
|
||||
* for the mechanics of actually reading a QtJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class QtJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
QtJsonValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific QtJson value
|
||||
QtJsonValue(const QJsonValue &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new QtJsonFrozenValue instance that contains the
|
||||
* value referenced by this QtJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new QtJsonFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new QtJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a QtJsonArray instance.
|
||||
*
|
||||
* If the referenced QtJson value is an array, this function will return
|
||||
* a std::optional containing a QtJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<QtJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return opt::make_optional(QtJsonArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<QtJsonArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced QtJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
const QJsonArray array = value.toArray();
|
||||
result = array.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
result = value.toBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
result = value.toDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
result = value.toInt();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a QtJsonObject instance.
|
||||
*
|
||||
* If the referenced QtJson value is an object, this function will return a
|
||||
* std::optional containing a QtJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<QtJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return opt::make_optional(QtJsonObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<QtJsonObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced QtJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
const QJsonObject &object = value.toObject();
|
||||
result = object.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
result = value.toString().toStdString();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.isArray();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.isBool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
//toInt returns the default value (0, 1) if the value is not a whole number
|
||||
return value.isDouble() && (value.toInt(0) == value.toInt(1));
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.isObject();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const QJsonValue emptyObject()
|
||||
{
|
||||
static const QJsonValue object;
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained QtJson value.
|
||||
const QJsonValue value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting QtJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class QtJsonAdapter:
|
||||
public BasicAdapter<QtJsonAdapter,
|
||||
QtJsonArray,
|
||||
QtJsonObjectMember,
|
||||
QtJsonObject,
|
||||
QtJsonValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a QtJsonAdapter that contains an empty object
|
||||
QtJsonAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a QtJsonAdapter containing a specific QtJson value
|
||||
QtJsonAdapter(const QJsonValue &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* QtJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the std::iterator template.
|
||||
*
|
||||
* @see QtJsonArray
|
||||
*/
|
||||
class QtJsonArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
QtJsonAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new QtJsonArrayValueIterator using an existing
|
||||
* QtJson iterator.
|
||||
*
|
||||
* @param itr QtJson iterator to store
|
||||
*/
|
||||
QtJsonArrayValueIterator(
|
||||
const QJsonArray::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a QtJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
QtJsonAdapter operator*() const
|
||||
{
|
||||
return QtJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<QtJsonAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<QtJsonAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const QtJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const QtJsonArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const QtJsonArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
QtJsonArrayValueIterator operator++(int)
|
||||
{
|
||||
QtJsonArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const QtJsonArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
QJsonArray::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of QtJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see QtJsonObject
|
||||
* @see QtJsonObjectMember
|
||||
*/
|
||||
class QtJsonObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
QtJsonObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a QtJson iterator.
|
||||
*
|
||||
* @param itr QtJson iterator to store
|
||||
*/
|
||||
QtJsonObjectMemberIterator(
|
||||
const QJsonObject::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a QtJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
QtJsonObjectMember operator*() const
|
||||
{
|
||||
std::string key = itr.key().toStdString();
|
||||
return QtJsonObjectMember(key, itr.value());
|
||||
}
|
||||
|
||||
DerefProxy<QtJsonObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<QtJsonObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const QtJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const QtJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const QtJsonObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
QtJsonObjectMemberIterator operator++(int)
|
||||
{
|
||||
QtJsonObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const QtJsonObjectMemberIterator& operator--(int)
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original QtJson iterator
|
||||
QJsonObject::const_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for QtJsonAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::QtJsonAdapter>
|
||||
{
|
||||
typedef QJsonValue DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "QtJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool QtJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return QtJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline QtJsonArrayValueIterator QtJsonArray::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline QtJsonArrayValueIterator QtJsonArray::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline QtJsonObjectMemberIterator QtJsonObject::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline QtJsonObjectMemberIterator QtJsonObject::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline QtJsonObjectMemberIterator QtJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
return value.find(QString::fromStdString(propertyName));
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,933 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the RapidJson parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for RapidJson.
|
||||
*
|
||||
* This file defines the following template classes (not in this order):
|
||||
* - GenericRapidJsonAdapter
|
||||
* - GenericRapidJsonArray
|
||||
* - GenericRapidJsonArrayValueIterator
|
||||
* - GenericRapidJsonFrozenValue
|
||||
* - GenericRapidJsonObject
|
||||
* - GenericRapidJsonObjectMember
|
||||
* - GenericRapidJsonObjectMemberIterator
|
||||
* - GenericRapidJsonValue
|
||||
*
|
||||
* All of these classes share a template argument called 'ValueType', which can
|
||||
* be used to choose the underlying the RapidJson value type that is used. This
|
||||
* allows different RapidJson encodings and allocators to be used.
|
||||
*
|
||||
* This file also defines the following typedefs, which use RapidJson's default
|
||||
* ValueType:
|
||||
* - RapidJsonAdapter
|
||||
* - RapidJsonArray
|
||||
* - RapidJsonArrayValueIterator
|
||||
* - RapidJsonFrozenValue
|
||||
* - RapidJsonObject
|
||||
* - RapidJsonObjectMember
|
||||
* - RapidJsonObjectMemberIterator
|
||||
* - RapidJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is RapidJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_RAPIDJSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_RAPIDJSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
template<class ValueType = rapidjson::Value>
|
||||
class GenericRapidJsonAdapter;
|
||||
|
||||
template<class ValueType = rapidjson::Value>
|
||||
class GenericRapidJsonArrayValueIterator;
|
||||
|
||||
template<class ValueType = rapidjson::Value>
|
||||
class GenericRapidJsonObjectMemberIterator;
|
||||
|
||||
/// Container for a property name and an associated RapidJson value
|
||||
template<class ValueType = rapidjson::Value>
|
||||
class GenericRapidJsonObjectMember :
|
||||
public std::pair<std::string, GenericRapidJsonAdapter<ValueType> >
|
||||
{
|
||||
private:
|
||||
typedef std::pair<std::string, GenericRapidJsonAdapter<ValueType> > Super;
|
||||
|
||||
public:
|
||||
GenericRapidJsonObjectMember(
|
||||
const std::string &name,
|
||||
const GenericRapidJsonAdapter<ValueType> &value)
|
||||
: Super(name, value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a RapidJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a RapidJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to an underlying
|
||||
* RapidJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
template<class ValueType = rapidjson::Value>
|
||||
class GenericRapidJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef GenericRapidJsonArrayValueIterator<ValueType> const_iterator;
|
||||
typedef GenericRapidJsonArrayValueIterator<ValueType> iterator;
|
||||
|
||||
/// Construct a RapidJsonArray referencing an empty array singleton.
|
||||
GenericRapidJsonArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a RapidJsonArray referencing a specific RapidJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a RapidJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
GenericRapidJsonArray(const ValueType &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.IsArray()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator for the first element in the array.
|
||||
iterator begin() const;
|
||||
|
||||
/// Return an iterator for one-past the last element of the array.
|
||||
iterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.Size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a RapidJson value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const ValueType & emptyArray()
|
||||
{
|
||||
static const ValueType array(rapidjson::kArrayType);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const ValueType &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a RapidJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a RapidJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* RapidJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
template <class ValueType = rapidjson::Value>
|
||||
class GenericRapidJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef GenericRapidJsonObjectMemberIterator<ValueType> const_iterator;
|
||||
typedef GenericRapidJsonObjectMemberIterator<ValueType> iterator;
|
||||
|
||||
/// Construct a GenericRapidJsonObject referencing an empty object singleton.
|
||||
GenericRapidJsonObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a GenericRapidJsonObject referencing a specific
|
||||
* RapidJson value.
|
||||
*
|
||||
* @param value reference to a RapidJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
GenericRapidJsonObject(const ValueType &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.IsObject()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the pointer value returned by the underlying RapidJson implementation.
|
||||
*/
|
||||
iterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the pointer value returned by the underlying RapidJson implementation.
|
||||
*/
|
||||
iterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param property property name to search for
|
||||
*/
|
||||
iterator find(const std::string &property) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.MemberEnd() - value.MemberBegin();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a RapidJson value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const ValueType & emptyObject()
|
||||
{
|
||||
static ValueType object(rapidjson::kObjectType);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const ValueType &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a RapidJson value.
|
||||
*
|
||||
* This class allows a RapidJson value to be stored independent of its original
|
||||
* document. RapidJson makes this a bit harder than usual, because RapidJson
|
||||
* values are associated with a custom memory allocator. As such, RapidJson
|
||||
* values have to be copied recursively, referencing a custom allocator held
|
||||
* by this class.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
template<class ValueType = rapidjson::Value>
|
||||
class GenericRapidJsonFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
explicit GenericRapidJsonFrozenValue(const char *str)
|
||||
{
|
||||
value.SetString(str, allocator);
|
||||
}
|
||||
|
||||
explicit GenericRapidJsonFrozenValue(const std::string &str)
|
||||
{
|
||||
value.SetString(str.c_str(), (unsigned int)str.length(), allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a RapidJson value
|
||||
*
|
||||
* @param source the RapidJson value to be copied
|
||||
*/
|
||||
explicit GenericRapidJsonFrozenValue(const ValueType &source)
|
||||
{
|
||||
if (!copy(source, value, allocator)) {
|
||||
throw std::runtime_error("Failed to copy ValueType");
|
||||
}
|
||||
}
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new GenericRapidJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Recursively copy a RapidJson value using a separate allocator
|
||||
*
|
||||
* @param source value to copy from
|
||||
* @param dest value to copy into
|
||||
* @param allocator reference to an allocator held by this class
|
||||
*
|
||||
* @tparam Allocator type of RapidJson Allocator to be used
|
||||
*
|
||||
* @returns true if copied successfully, false otherwise.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
static bool copy(const ValueType &source,
|
||||
ValueType &dest,
|
||||
Allocator &allocator)
|
||||
{
|
||||
switch (source.GetType()) {
|
||||
case rapidjson::kNullType:
|
||||
dest.SetNull();
|
||||
return true;
|
||||
case rapidjson::kFalseType:
|
||||
dest.SetBool(false);
|
||||
return true;
|
||||
case rapidjson::kTrueType:
|
||||
dest.SetBool(true);
|
||||
return true;
|
||||
case rapidjson::kObjectType:
|
||||
dest.SetObject();
|
||||
for (typename ValueType::ConstMemberIterator itr = source.MemberBegin();
|
||||
itr != source.MemberEnd(); ++itr) {
|
||||
ValueType name(itr->name.GetString(), itr->name.GetStringLength(), allocator);
|
||||
ValueType value;
|
||||
copy(itr->value, value, allocator);
|
||||
dest.AddMember(name, value, allocator);
|
||||
}
|
||||
return true;
|
||||
case rapidjson::kArrayType:
|
||||
dest.SetArray();
|
||||
for (typename ValueType::ConstValueIterator itr = source.Begin(); itr != source.End(); ++itr) {
|
||||
ValueType value;
|
||||
copy(*itr, value, allocator);
|
||||
dest.PushBack(value, allocator);
|
||||
}
|
||||
return true;
|
||||
case rapidjson::kStringType:
|
||||
dest.SetString(source.GetString(), source.GetStringLength(), allocator);
|
||||
return true;
|
||||
case rapidjson::kNumberType:
|
||||
if (source.IsInt()) {
|
||||
dest.SetInt(source.GetInt());
|
||||
} else if (source.IsUint()) {
|
||||
dest.SetUint(source.GetUint());
|
||||
} else if (source.IsInt64()) {
|
||||
dest.SetInt64(source.GetInt64());
|
||||
} else if (source.IsUint64()) {
|
||||
dest.SetUint64(source.GetUint64());
|
||||
} else {
|
||||
dest.SetDouble(source.GetDouble());
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Local memory allocator for RapidJson value
|
||||
typename ValueType::AllocatorType allocator;
|
||||
|
||||
/// Local RapidJson value
|
||||
ValueType value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a RapidJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a RapidJson value. This class is responsible
|
||||
* for the mechanics of actually reading a RapidJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
template<class ValueType = rapidjson::Value>
|
||||
class GenericRapidJsonValue
|
||||
{
|
||||
public:
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
GenericRapidJsonValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific RapidJson value
|
||||
GenericRapidJsonValue(const ValueType &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new GenericRapidJsonFrozenValue instance that contains
|
||||
* the value referenced by this GenericRapidJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new GenericRapidJsonFrozenValue instance, belonging
|
||||
* to the caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new GenericRapidJsonFrozenValue<ValueType>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a GenericRapidJsonArray instance.
|
||||
*
|
||||
* If the referenced RapidJson value is an array, this function will return
|
||||
* a std::optional containing a GenericRapidJsonArray instance referencing
|
||||
* the array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<GenericRapidJsonArray<ValueType> > getArrayOptional() const
|
||||
{
|
||||
if (value.IsArray()) {
|
||||
return opt::make_optional(GenericRapidJsonArray<ValueType>(value));
|
||||
}
|
||||
|
||||
return opt::optional<GenericRapidJsonArray<ValueType> >();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced RapidJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.IsArray()) {
|
||||
result = value.Size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.IsBool()) {
|
||||
result = value.GetBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.IsDouble()) {
|
||||
result = value.GetDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.IsInt()) {
|
||||
result = value.GetInt();
|
||||
return true;
|
||||
} else if (value.IsInt64()) {
|
||||
result = value.GetInt64();
|
||||
return true;
|
||||
} else if (value.IsUint()) {
|
||||
result = static_cast<int64_t>(value.GetUint());
|
||||
return true;
|
||||
} else if (value.IsUint64()) {
|
||||
result = static_cast<int64_t>(value.GetUint64());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a GenericRapidJsonObject instance.
|
||||
*
|
||||
* If the referenced RapidJson value is an object, this function will return
|
||||
* a std::optional containing a GenericRapidJsonObject instance
|
||||
* referencing the object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<GenericRapidJsonObject<ValueType> > getObjectOptional() const
|
||||
{
|
||||
if (value.IsObject()) {
|
||||
return opt::make_optional(GenericRapidJsonObject<ValueType>(value));
|
||||
}
|
||||
|
||||
return opt::optional<GenericRapidJsonObject<ValueType> >();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced RapidJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.IsObject()) {
|
||||
result = value.MemberEnd() - value.MemberBegin();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.IsString()) {
|
||||
result.assign(value.GetString(), value.GetStringLength());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.IsArray();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.IsBool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.IsDouble();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.IsInt() || value.IsInt64() || value.IsUint() ||
|
||||
value.IsUint64();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.IsNull();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.IsNumber();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.IsObject();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.IsString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const ValueType & emptyObject()
|
||||
{
|
||||
static const ValueType object(rapidjson::kObjectType);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained RapidJson value.
|
||||
const ValueType &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting RapidJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
template<class ValueType>
|
||||
class GenericRapidJsonAdapter:
|
||||
public BasicAdapter<GenericRapidJsonAdapter<ValueType>,
|
||||
GenericRapidJsonArray<ValueType>,
|
||||
GenericRapidJsonObjectMember<ValueType>,
|
||||
GenericRapidJsonObject<ValueType>,
|
||||
GenericRapidJsonValue<ValueType> >
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a RapidJsonAdapter that contains an empty object
|
||||
GenericRapidJsonAdapter()
|
||||
: BasicAdapter<GenericRapidJsonAdapter<ValueType>,
|
||||
GenericRapidJsonArray<ValueType>,
|
||||
GenericRapidJsonObjectMember<ValueType>,
|
||||
GenericRapidJsonObject<ValueType>,
|
||||
GenericRapidJsonValue<ValueType> >() { }
|
||||
|
||||
/// Construct a RapidJsonAdapter containing a specific RapidJson value
|
||||
GenericRapidJsonAdapter(const ValueType &value)
|
||||
: BasicAdapter<GenericRapidJsonAdapter<ValueType>,
|
||||
GenericRapidJsonArray<ValueType>,
|
||||
GenericRapidJsonObjectMember<ValueType>,
|
||||
GenericRapidJsonObject<ValueType>,
|
||||
GenericRapidJsonValue<ValueType> >(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* RapidJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the std::iterator template.
|
||||
*
|
||||
* @see RapidJsonArray
|
||||
*/
|
||||
template<class ValueType>
|
||||
class GenericRapidJsonArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
GenericRapidJsonAdapter<ValueType> > // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new GenericRapidJsonArrayValueIterator using an
|
||||
* existing RapidJson iterator.
|
||||
*
|
||||
* @param itr RapidJson iterator to store
|
||||
*/
|
||||
GenericRapidJsonArrayValueIterator(
|
||||
const typename ValueType::ConstValueIterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a GenericRapidJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
GenericRapidJsonAdapter<ValueType> operator*() const
|
||||
{
|
||||
return GenericRapidJsonAdapter<ValueType>(*itr);
|
||||
}
|
||||
|
||||
/// Returns a proxy for the value of the current element
|
||||
DerefProxy<GenericRapidJsonAdapter<ValueType> > operator->() const
|
||||
{
|
||||
return DerefProxy<GenericRapidJsonAdapter<ValueType> >(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const GenericRapidJsonArrayValueIterator<ValueType> &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const GenericRapidJsonArrayValueIterator<ValueType>& other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
GenericRapidJsonArrayValueIterator<ValueType>& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericRapidJsonArrayValueIterator<ValueType> operator++(int) {
|
||||
GenericRapidJsonArrayValueIterator<ValueType> iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
GenericRapidJsonArrayValueIterator<ValueType>& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
std::ptrdiff_t difference(const GenericRapidJsonArrayValueIterator<ValueType> &other)
|
||||
{
|
||||
return std::distance(itr, other.itr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typename ValueType::ConstValueIterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of GenericRapidJsonObjectMember representing one of the members of the
|
||||
* object. It has been implemented using the std::iterator template.
|
||||
*
|
||||
* @see GenericRapidJsonObject
|
||||
* @see GenericRapidJsonObjectMember
|
||||
*/
|
||||
template<class ValueType>
|
||||
class GenericRapidJsonObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
GenericRapidJsonObjectMember<ValueType> > // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a RapidJson iterator.
|
||||
*
|
||||
* @param itr RapidJson iterator to store
|
||||
*/
|
||||
GenericRapidJsonObjectMemberIterator(
|
||||
const typename ValueType::ConstMemberIterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns a GenericRapidJsonObjectMember that contains the key and
|
||||
* value belonging to the object member identified by the iterator.
|
||||
*/
|
||||
GenericRapidJsonObjectMember<ValueType> operator*() const
|
||||
{
|
||||
return GenericRapidJsonObjectMember<ValueType>(
|
||||
std::string(itr->name.GetString(), itr->name.GetStringLength()),
|
||||
itr->value);
|
||||
}
|
||||
|
||||
/// Returns a proxy for the value of the current element
|
||||
DerefProxy<GenericRapidJsonObjectMember<ValueType> > operator->() const
|
||||
{
|
||||
return DerefProxy<GenericRapidJsonObjectMember<ValueType> >(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const GenericRapidJsonObjectMemberIterator<ValueType> &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const GenericRapidJsonObjectMemberIterator<ValueType> &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
GenericRapidJsonObjectMemberIterator<ValueType>& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericRapidJsonObjectMemberIterator<ValueType> operator++(int)
|
||||
{
|
||||
GenericRapidJsonObjectMemberIterator<ValueType> iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
GenericRapidJsonObjectMemberIterator<ValueType>& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ptrdiff_t difference(const GenericRapidJsonObjectMemberIterator &other)
|
||||
{
|
||||
return std::distance(itr, other.itr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original RapidJson iterator
|
||||
typename ValueType::ConstMemberIterator itr;
|
||||
};
|
||||
|
||||
template<class ValueType>
|
||||
inline bool GenericRapidJsonFrozenValue<ValueType>::equalTo(
|
||||
const Adapter &other, bool strict) const
|
||||
{
|
||||
return GenericRapidJsonAdapter<ValueType>(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline typename GenericRapidJsonArray<ValueType>::iterator
|
||||
GenericRapidJsonArray<ValueType>::begin() const
|
||||
{
|
||||
return value.Begin();
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline typename GenericRapidJsonArray<ValueType>::iterator
|
||||
GenericRapidJsonArray<ValueType>::end() const
|
||||
{
|
||||
return value.End();
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline typename GenericRapidJsonObject<ValueType>::iterator
|
||||
GenericRapidJsonObject<ValueType>::begin() const
|
||||
{
|
||||
return value.MemberBegin();
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline typename GenericRapidJsonObject<ValueType>::iterator
|
||||
GenericRapidJsonObject<ValueType>::end() const
|
||||
{
|
||||
return value.MemberEnd();
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline typename GenericRapidJsonObject<ValueType>::iterator
|
||||
GenericRapidJsonObject<ValueType>::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
// Hack to support older versions of rapidjson where pointers are used as
|
||||
// the built in iterator type. In those versions, the FindMember function
|
||||
// would return a null pointer when the requested member could not be
|
||||
// found. After calling FindMember on an empty object, we compare the
|
||||
// result against what we would expect if a non-null-pointer iterator was
|
||||
// returned.
|
||||
const ValueType empty(rapidjson::kObjectType);
|
||||
const typename ValueType::ConstMemberIterator maybeEnd = empty.FindMember("");
|
||||
if (maybeEnd != empty.MemberBegin() + 1) {
|
||||
// In addition to the pointer-based iterator issue, RapidJson's internal
|
||||
// string comparison code seemed to rely on the query string being
|
||||
// initialised to a length greater than or equal to that of the
|
||||
// properties being compared. We get around this by implementing our
|
||||
// own linear scan.
|
||||
const size_t propertyNameLength = propertyName.length();
|
||||
for (typename ValueType::ConstMemberIterator itr = value.MemberBegin();
|
||||
itr != value.MemberEnd(); ++itr) {
|
||||
const size_t memberNameLength = itr->name.GetStringLength();
|
||||
if (memberNameLength == propertyNameLength &&
|
||||
strncmp(itr->name.GetString(), propertyName.c_str(),
|
||||
itr->name.GetStringLength()) == 0) {
|
||||
return itr;
|
||||
}
|
||||
}
|
||||
|
||||
return value.MemberEnd();
|
||||
}
|
||||
|
||||
return value.FindMember(propertyName.c_str()); // Times are good.
|
||||
}
|
||||
|
||||
typedef GenericRapidJsonAdapter<> RapidJsonAdapter;
|
||||
typedef GenericRapidJsonArray<> RapidJsonArray;
|
||||
typedef GenericRapidJsonArrayValueIterator<> RapidJsonArrayValue;
|
||||
typedef GenericRapidJsonFrozenValue<> RapidJsonFrozenValue;
|
||||
typedef GenericRapidJsonObject<> RapidJsonObject;
|
||||
typedef GenericRapidJsonObjectMember<> RapidJsonObjectMember;
|
||||
typedef GenericRapidJsonObjectMemberIterator<> RapidJsonObjectMemberIterator;
|
||||
typedef GenericRapidJsonValue<> RapidJsonValue;
|
||||
|
||||
/**
|
||||
* @brief Specialisation of the AdapterTraits template struct for a
|
||||
* RapidJsonAdapter that uses a pool allocator
|
||||
*/
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::RapidJsonAdapter>
|
||||
{
|
||||
typedef rapidjson::Document DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "RapidJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
typedef rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>
|
||||
RapidJsonCrt;
|
||||
|
||||
/**
|
||||
* @brief Specialisation of the AdapterTraits template struct for a
|
||||
* RapidJsonAdapter that uses the default CRT allocator
|
||||
*/
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::GenericRapidJsonAdapter<RapidJsonCrt> >
|
||||
{
|
||||
typedef rapidjson::GenericDocument<rapidjson::UTF8<>,
|
||||
rapidjson::CrtAllocator> DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "GenericRapidJsonAdapter (using CrtAllocator)";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_CONSTRAINT_BUILDER_HPP
|
||||
#define __VALIJSON_CONSTRAINT_BUILDER_HPP
|
||||
|
||||
namespace valijson {
|
||||
|
||||
namespace adapters {
|
||||
class Adapter;
|
||||
}
|
||||
|
||||
namespace constraints {
|
||||
struct Constraint;
|
||||
}
|
||||
|
||||
class ConstraintBuilder
|
||||
{
|
||||
public:
|
||||
virtual ~ConstraintBuilder() {}
|
||||
|
||||
virtual constraints::Constraint * make(const adapters::Adapter &) const = 0;
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_CONSTRAINTS_BASIC_CONSTRAINT_HPP
|
||||
#define __VALIJSON_CONSTRAINTS_BASIC_CONSTRAINT_HPP
|
||||
|
||||
#include <valijson/constraints/constraint.hpp>
|
||||
#include <valijson/constraints/constraint_visitor.hpp>
|
||||
|
||||
#include <valijson/internal/custom_allocator.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace constraints {
|
||||
|
||||
/**
|
||||
* @brief Template class that implements the accept() and clone() functions of
|
||||
* the Constraint interface.
|
||||
*
|
||||
* @tparam ConstraintType name of the concrete constraint type, which must
|
||||
* provide a copy constructor.
|
||||
*/
|
||||
template<typename ConstraintType>
|
||||
struct BasicConstraint: Constraint
|
||||
{
|
||||
typedef internal::CustomAllocator<void *> Allocator;
|
||||
|
||||
typedef std::basic_string<char, std::char_traits<char>,
|
||||
internal::CustomAllocator<char> > String;
|
||||
|
||||
BasicConstraint()
|
||||
: allocator() { }
|
||||
|
||||
BasicConstraint(Allocator::CustomAlloc allocFn, Allocator::CustomFree freeFn)
|
||||
: allocator(allocFn, freeFn) { }
|
||||
|
||||
BasicConstraint(const BasicConstraint &other)
|
||||
: allocator(other.allocator) { }
|
||||
|
||||
virtual ~BasicConstraint<ConstraintType>() { }
|
||||
|
||||
virtual bool accept(ConstraintVisitor &visitor) const
|
||||
{
|
||||
return visitor.visit(*static_cast<const ConstraintType*>(this));
|
||||
}
|
||||
|
||||
virtual Constraint * clone(CustomAlloc allocFn, CustomFree freeFn) const
|
||||
{
|
||||
void *ptr = allocFn(sizeof(ConstraintType));
|
||||
if (!ptr) {
|
||||
throw std::runtime_error(
|
||||
"Failed to allocate memory for cloned constraint");
|
||||
}
|
||||
|
||||
try {
|
||||
return new (ptr) ConstraintType(
|
||||
*static_cast<const ConstraintType*>(this));
|
||||
} catch (...) {
|
||||
freeFn(ptr);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
Allocator allocator;
|
||||
};
|
||||
|
||||
} // namespace constraints
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_CONSTRAINTS_CONSTRAINT_HPP
|
||||
#define __VALIJSON_CONSTRAINTS_CONSTRAINT_HPP
|
||||
|
||||
namespace valijson {
|
||||
namespace constraints {
|
||||
|
||||
class ConstraintVisitor;
|
||||
|
||||
/**
|
||||
* @brief Interface that must be implemented by concrete constraint types.
|
||||
*
|
||||
* @todo Consider using something like the boost::cloneable concept here.
|
||||
*/
|
||||
struct Constraint
|
||||
{
|
||||
/// Typedef for custom new-/malloc-like function
|
||||
typedef void * (*CustomAlloc)(size_t size);
|
||||
|
||||
/// Typedef for custom free-like function
|
||||
typedef void (*CustomFree)(void *);
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor.
|
||||
*/
|
||||
virtual ~Constraint() { }
|
||||
|
||||
/**
|
||||
* @brief Perform an action on the constraint using the visitor pattern.
|
||||
*
|
||||
* Note that Constraints cannot be modified by visitors.
|
||||
*
|
||||
* @param visitor Reference to a ConstraintVisitor object.
|
||||
*
|
||||
* @returns the boolean value returned by one of the visitor's visit
|
||||
* functions.
|
||||
*/
|
||||
virtual bool accept(ConstraintVisitor &visitor) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a constraint.
|
||||
*
|
||||
* Note that this should be a deep copy of the constraint.
|
||||
*
|
||||
* @returns an owning-pointer to the new constraint.
|
||||
*/
|
||||
virtual Constraint * clone(CustomAlloc, CustomFree) const = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace constraints
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_CONSTRAINTS_CONSTRAINT_VISITOR_HPP
|
||||
#define __VALIJSON_CONSTRAINTS_CONSTRAINT_VISITOR_HPP
|
||||
|
||||
namespace valijson {
|
||||
namespace constraints {
|
||||
|
||||
class AllOfConstraint;
|
||||
class AnyOfConstraint;
|
||||
class DependenciesConstraint;
|
||||
class EnumConstraint;
|
||||
class LinearItemsConstraint;
|
||||
class MaxItemsConstraint;
|
||||
class MaximumConstraint;
|
||||
class MaxLengthConstraint;
|
||||
class MaxPropertiesConstraint;
|
||||
class MinItemsConstraint;
|
||||
class MinimumConstraint;
|
||||
class MinLengthConstraint;
|
||||
class MinPropertiesConstraint;
|
||||
class MultipleOfDoubleConstraint;
|
||||
class MultipleOfIntConstraint;
|
||||
class NotConstraint;
|
||||
class OneOfConstraint;
|
||||
class PatternConstraint;
|
||||
class PolyConstraint;
|
||||
class PropertiesConstraint;
|
||||
class RequiredConstraint;
|
||||
class SingularItemsConstraint;
|
||||
class TypeConstraint;
|
||||
class UniqueItemsConstraint;
|
||||
|
||||
/// Interface to allow usage of the visitor pattern with Constraints
|
||||
class ConstraintVisitor
|
||||
{
|
||||
protected:
|
||||
virtual ~ConstraintVisitor() {}
|
||||
|
||||
// Shorten type names for derived classes outside of this namespace
|
||||
typedef constraints::AllOfConstraint AllOfConstraint;
|
||||
typedef constraints::AnyOfConstraint AnyOfConstraint;
|
||||
typedef constraints::DependenciesConstraint DependenciesConstraint;
|
||||
typedef constraints::EnumConstraint EnumConstraint;
|
||||
typedef constraints::LinearItemsConstraint LinearItemsConstraint;
|
||||
typedef constraints::MaximumConstraint MaximumConstraint;
|
||||
typedef constraints::MaxItemsConstraint MaxItemsConstraint;
|
||||
typedef constraints::MaxLengthConstraint MaxLengthConstraint;
|
||||
typedef constraints::MaxPropertiesConstraint MaxPropertiesConstraint;
|
||||
typedef constraints::MinimumConstraint MinimumConstraint;
|
||||
typedef constraints::MinItemsConstraint MinItemsConstraint;
|
||||
typedef constraints::MinLengthConstraint MinLengthConstraint;
|
||||
typedef constraints::MinPropertiesConstraint MinPropertiesConstraint;
|
||||
typedef constraints::MultipleOfDoubleConstraint MultipleOfDoubleConstraint;
|
||||
typedef constraints::MultipleOfIntConstraint MultipleOfIntConstraint;
|
||||
typedef constraints::NotConstraint NotConstraint;
|
||||
typedef constraints::OneOfConstraint OneOfConstraint;
|
||||
typedef constraints::PatternConstraint PatternConstraint;
|
||||
typedef constraints::PolyConstraint PolyConstraint;
|
||||
typedef constraints::PropertiesConstraint PropertiesConstraint;
|
||||
typedef constraints::RequiredConstraint RequiredConstraint;
|
||||
typedef constraints::SingularItemsConstraint SingularItemsConstraint;
|
||||
typedef constraints::TypeConstraint TypeConstraint;
|
||||
typedef constraints::UniqueItemsConstraint UniqueItemsConstraint;
|
||||
|
||||
public:
|
||||
|
||||
virtual bool visit(const AllOfConstraint &) = 0;
|
||||
virtual bool visit(const AnyOfConstraint &) = 0;
|
||||
virtual bool visit(const DependenciesConstraint &) = 0;
|
||||
virtual bool visit(const EnumConstraint &) = 0;
|
||||
virtual bool visit(const LinearItemsConstraint &) = 0;
|
||||
virtual bool visit(const MaximumConstraint &) = 0;
|
||||
virtual bool visit(const MaxItemsConstraint &) = 0;
|
||||
virtual bool visit(const MaxLengthConstraint &) = 0;
|
||||
virtual bool visit(const MaxPropertiesConstraint &) = 0;
|
||||
virtual bool visit(const MinimumConstraint &) = 0;
|
||||
virtual bool visit(const MinItemsConstraint &) = 0;
|
||||
virtual bool visit(const MinLengthConstraint &) = 0;
|
||||
virtual bool visit(const MinPropertiesConstraint &) = 0;
|
||||
virtual bool visit(const MultipleOfDoubleConstraint &) = 0;
|
||||
virtual bool visit(const MultipleOfIntConstraint &) = 0;
|
||||
virtual bool visit(const NotConstraint &) = 0;
|
||||
virtual bool visit(const OneOfConstraint &) = 0;
|
||||
virtual bool visit(const PatternConstraint &) = 0;
|
||||
virtual bool visit(const PolyConstraint &) = 0;
|
||||
virtual bool visit(const PropertiesConstraint &) = 0;
|
||||
virtual bool visit(const RequiredConstraint &) = 0;
|
||||
virtual bool visit(const SingularItemsConstraint &) = 0;
|
||||
virtual bool visit(const TypeConstraint &) = 0;
|
||||
virtual bool visit(const UniqueItemsConstraint &) = 0;
|
||||
};
|
||||
|
||||
} // namespace constraints
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
#ifndef __VALIJSON_CUSTOM_ALLOCATOR_HPP
|
||||
#define __VALIJSON_CUSTOM_ALLOCATOR_HPP
|
||||
|
||||
namespace valijson {
|
||||
namespace internal {
|
||||
|
||||
template<class T>
|
||||
class CustomAllocator
|
||||
{
|
||||
public:
|
||||
/// Typedef for custom new-/malloc-like function
|
||||
typedef void * (*CustomAlloc)(size_t size);
|
||||
|
||||
/// Typedef for custom free-like function
|
||||
typedef void (*CustomFree)(void *);
|
||||
|
||||
// Standard allocator typedefs
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
template<typename U>
|
||||
struct rebind
|
||||
{
|
||||
typedef CustomAllocator<U> other;
|
||||
};
|
||||
|
||||
CustomAllocator()
|
||||
: allocFn(::operator new),
|
||||
freeFn(::operator delete) { }
|
||||
|
||||
CustomAllocator(CustomAlloc allocFn, CustomFree freeFn)
|
||||
: allocFn(allocFn),
|
||||
freeFn(freeFn) { }
|
||||
|
||||
CustomAllocator(const CustomAllocator &other)
|
||||
: allocFn(other.allocFn),
|
||||
freeFn(other.freeFn) { }
|
||||
|
||||
template<typename U>
|
||||
CustomAllocator(CustomAllocator<U> const &other)
|
||||
: allocFn(other.allocFn),
|
||||
freeFn(other.freeFn) { }
|
||||
|
||||
CustomAllocator & operator=(const CustomAllocator &other)
|
||||
{
|
||||
allocFn = other.allocFn;
|
||||
freeFn = other.freeFn;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
pointer address(reference r)
|
||||
{
|
||||
return &r;
|
||||
}
|
||||
|
||||
const_pointer address(const_reference r)
|
||||
{
|
||||
return &r;
|
||||
}
|
||||
|
||||
pointer allocate(size_type cnt, const void * = 0)
|
||||
{
|
||||
return reinterpret_cast<pointer>(allocFn(cnt * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type)
|
||||
{
|
||||
freeFn(p);
|
||||
}
|
||||
|
||||
size_type max_size() const
|
||||
{
|
||||
return std::numeric_limits<size_type>::max() / sizeof(T);
|
||||
}
|
||||
|
||||
void construct(pointer p, const T& t)
|
||||
{
|
||||
new(p) T(t);
|
||||
}
|
||||
|
||||
void destroy(pointer p)
|
||||
{
|
||||
p->~T();
|
||||
}
|
||||
|
||||
bool operator==(const CustomAllocator &other) const
|
||||
{
|
||||
return other.allocFn == allocFn && other.freeFn == freeFn;
|
||||
}
|
||||
|
||||
bool operator!=(const CustomAllocator &other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
CustomAlloc allocFn;
|
||||
|
||||
CustomFree freeFn;
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
} // end namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef __VALIJSON_DEBUG_HPP
|
||||
#define __VALIJSON_DEBUG_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace valijson {
|
||||
namespace internal {
|
||||
|
||||
template<typename AdapterType>
|
||||
std::string nodeTypeAsString(const AdapterType &node) {
|
||||
if (node.isArray()) {
|
||||
return "array";
|
||||
} else if (node.isObject()) {
|
||||
return "object";
|
||||
} else if (node.isString()) {
|
||||
return "string";
|
||||
} else if (node.isNull()) {
|
||||
return "null";
|
||||
} else if (node.isInteger()) {
|
||||
return "integer";
|
||||
} else if (node.isDouble()) {
|
||||
return "double";
|
||||
} else if (node.isBool()) {
|
||||
return "bool";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
} // end namespace internal
|
||||
} // end namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,257 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_INTERNAL_JSON_POINTER_HPP
|
||||
#define __VALIJSON_INTERNAL_JSON_POINTER_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/internal/optional.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace internal {
|
||||
namespace json_pointer {
|
||||
|
||||
/**
|
||||
* @brief Replace all occurrences of `search` with `replace`. Modifies `subject` in place
|
||||
*
|
||||
* @param subject string to operate on
|
||||
* @param search string to search
|
||||
* @param replace replacement string
|
||||
*/
|
||||
inline void replace_all_inplace(std::string& subject, const char* search,
|
||||
const char* replace)
|
||||
{
|
||||
size_t pos = 0;
|
||||
|
||||
while((pos = subject.find(search, pos)) != std::string::npos) {
|
||||
subject.replace(pos, strlen(search), replace);
|
||||
pos += strlen(replace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the char value corresponding to a 2-digit hexadecimal string
|
||||
*
|
||||
* @throws std::runtime_error for strings that are not exactly two characters
|
||||
* in length and for strings that contain non-hexadecimal characters
|
||||
*
|
||||
* @return decoded char value corresponding to the hexadecimal string
|
||||
*/
|
||||
inline char decodePercentEncodedChar(const std::string &digits)
|
||||
{
|
||||
if (digits.length() != 2) {
|
||||
throw std::runtime_error("Failed to decode %-encoded character '" +
|
||||
digits + "' due to unexpected number of characters; "
|
||||
"expected two characters");
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
const char *begin = digits.c_str();
|
||||
char *end = NULL;
|
||||
const unsigned long value = strtoul(begin, &end, 16);
|
||||
if (end != begin && *end != '\0') {
|
||||
throw std::runtime_error("Failed to decode %-encoded character '" +
|
||||
digits + "'");
|
||||
}
|
||||
|
||||
return char(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract and transform the token between two iterators
|
||||
*
|
||||
* This function is responsible for extracting a JSON Reference token from
|
||||
* between two iterators, and performing any necessary transformations, before
|
||||
* returning the resulting string. Its main purpose is to replace the escaped
|
||||
* character sequences defined in the RFC-6901 (JSON Pointer), and to decode
|
||||
* %-encoded character sequences defined in RFC-3986 (URI).
|
||||
*
|
||||
* The encoding used in RFC-3986 should be familiar to many developers, but
|
||||
* the escaped character sequences used in JSON Pointers may be less so. From
|
||||
* the JSON Pointer specification (RFC 6901, April 2013):
|
||||
*
|
||||
* Evaluation of each reference token begins by decoding any escaped
|
||||
* character sequence. This is performed by first transforming any
|
||||
* occurrence of the sequence '~1' to '/', and then transforming any
|
||||
* occurrence of the sequence '~0' to '~'. By performing the
|
||||
* substitutions in this order, an implementation avoids the error of
|
||||
* turning '~01' first into '~1' and then into '/', which would be
|
||||
* incorrect (the string '~01' correctly becomes '~1' after
|
||||
* transformation).
|
||||
*
|
||||
* @param begin iterator pointing to beginning of a token
|
||||
* @param end iterator pointing to one character past the end of the token
|
||||
*
|
||||
* @return string with escaped character sequences replaced
|
||||
*
|
||||
*/
|
||||
inline std::string extractReferenceToken(std::string::const_iterator begin,
|
||||
std::string::const_iterator end)
|
||||
{
|
||||
std::string token(begin, end);
|
||||
|
||||
// Replace JSON Pointer-specific escaped character sequences
|
||||
replace_all_inplace(token, "~1", "/");
|
||||
replace_all_inplace(token, "~0", "~");
|
||||
|
||||
// Replace %-encoded character sequences with their actual characters
|
||||
for (size_t n = token.find('%'); n != std::string::npos;
|
||||
n = token.find('%', n + 1)) {
|
||||
|
||||
try {
|
||||
const char c = decodePercentEncodedChar(token.substr(n + 1, 2));
|
||||
token.replace(n, 3, &c, 1);
|
||||
|
||||
} catch (const std::runtime_error &e) {
|
||||
throw std::runtime_error(
|
||||
std::string(e.what()) + "; in token: " + token);
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recursively locate the value referenced by a JSON Pointer
|
||||
*
|
||||
* This function takes both a string reference and an iterator to the beginning
|
||||
* of the substring that is being resolved. This iterator is expected to point
|
||||
* to the beginning of a reference token, whose length will be determined by
|
||||
* searching for the next delimiter ('/' or '\0'). A reference token must be
|
||||
* at least one character in length to be considered valid.
|
||||
*
|
||||
* Once the next reference token has been identified, it will be used either as
|
||||
* an array index or as an the name an object member. The validity of a
|
||||
* reference token depends on the type of the node currently being traversed,
|
||||
* and the applicability of the token to that node. For example, an array can
|
||||
* only be dereferenced by a non-negative integral index.
|
||||
*
|
||||
* Once the next node has been identified, the length of the remaining portion
|
||||
* of the JSON Pointer will be used to determine whether recursion should
|
||||
* terminate.
|
||||
*
|
||||
* @param node current node in recursive evaluation of JSON Pointer
|
||||
* @param jsonPointer string containing complete JSON Pointer
|
||||
* @param jsonPointerItr string iterator pointing the beginning of the next
|
||||
* reference token
|
||||
*
|
||||
* @return an instance of AdapterType that wraps the dereferenced node
|
||||
*/
|
||||
template<typename AdapterType>
|
||||
inline AdapterType resolveJsonPointer(
|
||||
const AdapterType &node,
|
||||
const std::string &jsonPointer,
|
||||
const std::string::const_iterator jsonPointerItr)
|
||||
{
|
||||
// TODO: This function will probably need to implement support for
|
||||
// fetching documents referenced by JSON Pointers, similar to the
|
||||
// populateSchema function.
|
||||
|
||||
const std::string::const_iterator jsonPointerEnd = jsonPointer.end();
|
||||
|
||||
// Terminate recursion if all reference tokens have been consumed
|
||||
if (jsonPointerItr == jsonPointerEnd) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// Reference tokens must begin with a leading slash
|
||||
if (*jsonPointerItr != '/') {
|
||||
throw std::runtime_error("Expected reference token to begin with "
|
||||
"leading slash; remaining tokens: " +
|
||||
std::string(jsonPointerItr, jsonPointerEnd));
|
||||
}
|
||||
|
||||
// Find iterator that points to next slash or newline character; this is
|
||||
// one character past the end of the current reference token
|
||||
std::string::const_iterator jsonPointerNext =
|
||||
std::find(jsonPointerItr + 1, jsonPointerEnd, '/');
|
||||
|
||||
// Extract the next reference token
|
||||
const std::string referenceToken = extractReferenceToken(
|
||||
jsonPointerItr + 1, jsonPointerNext);
|
||||
|
||||
// Empty reference tokens should be ignored
|
||||
if (referenceToken.empty()) {
|
||||
return resolveJsonPointer(node, jsonPointer, jsonPointerNext);
|
||||
|
||||
} else if (node.isArray()) {
|
||||
if (referenceToken.compare("-") == 0) {
|
||||
throw std::runtime_error("Hyphens cannot be used as array indices "
|
||||
"since the requested array element does not yet exist");
|
||||
}
|
||||
|
||||
try {
|
||||
// Fragment must be non-negative integer
|
||||
const uint64_t index = std::stoul(referenceToken);
|
||||
typedef typename AdapterType::Array Array;
|
||||
typename Array::const_iterator itr = node.asArray().begin();
|
||||
|
||||
if (index > node.asArray().size() - 1) {
|
||||
throw std::runtime_error("Expected reference token to identify "
|
||||
"an element in the current array, but array index is "
|
||||
"out of bounds; actual token: " + referenceToken);
|
||||
}
|
||||
|
||||
if (index > static_cast<uint64_t>(std::numeric_limits<std::ptrdiff_t>::max())) {
|
||||
throw std::runtime_error("Array index out of bounds; hard "
|
||||
"limit is " + std::to_string(
|
||||
std::numeric_limits<std::ptrdiff_t>::max()));
|
||||
}
|
||||
|
||||
itr.advance(static_cast<std::ptrdiff_t>(index));
|
||||
|
||||
// Recursively process the remaining tokens
|
||||
return resolveJsonPointer(*itr, jsonPointer, jsonPointerNext);
|
||||
|
||||
} catch (std::invalid_argument &) {
|
||||
throw std::runtime_error("Expected reference token to contain a "
|
||||
"non-negative integer to identify an element in the "
|
||||
"current array; actual token: " + referenceToken);
|
||||
}
|
||||
|
||||
} else if (node.maybeObject()) {
|
||||
// Fragment must identify a member of the candidate object
|
||||
typedef typename AdapterType::Object Object;
|
||||
typename Object::const_iterator itr = node.asObject().find(
|
||||
referenceToken);
|
||||
if (itr == node.asObject().end()) {
|
||||
throw std::runtime_error("Expected reference token to identify an "
|
||||
"element in the current object; "
|
||||
"actual token: " + referenceToken);
|
||||
}
|
||||
|
||||
// Recursively process the remaining tokens
|
||||
return resolveJsonPointer(itr->second, jsonPointer, jsonPointerNext);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Expected end of JSON Pointer, but at least "
|
||||
"one reference token has not been processed; remaining tokens: " +
|
||||
std::string(jsonPointerNext, jsonPointerEnd));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the JSON Value referenced by a JSON Pointer
|
||||
*
|
||||
* @param rootNode node to use as root for JSON Pointer resolution
|
||||
* @param jsonPointer string containing JSON Pointer
|
||||
*
|
||||
* @return an instance AdapterType in the specified document
|
||||
*/
|
||||
template<typename AdapterType>
|
||||
inline AdapterType resolveJsonPointer(
|
||||
const AdapterType &rootNode,
|
||||
const std::string &jsonPointer)
|
||||
{
|
||||
return resolveJsonPointer(rootNode, jsonPointer, jsonPointer.begin());
|
||||
}
|
||||
|
||||
} // namespace json_pointer
|
||||
} // namespace internal
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_INTERNAL_JSON_REFERENCE_HPP
|
||||
#define __VALIJSON_INTERNAL_JSON_REFERENCE_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <valijson/internal/optional.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace internal {
|
||||
namespace json_reference {
|
||||
|
||||
/**
|
||||
* @brief Extract URI from JSON Reference relative to the current schema
|
||||
*
|
||||
* @param jsonRef JSON Reference to extract from
|
||||
* @param schema Schema that JSON Reference URI is relative to
|
||||
*
|
||||
* @return Optional string containing URI
|
||||
*/
|
||||
inline opt::optional<std::string> getJsonReferenceUri(
|
||||
const std::string &jsonRef)
|
||||
{
|
||||
const size_t ptrPos = jsonRef.find("#");
|
||||
if (ptrPos == 0) {
|
||||
// The JSON Reference does not contain a URI, but might contain a
|
||||
// JSON Pointer that refers to the current document
|
||||
return opt::optional<std::string>();
|
||||
} else if (ptrPos != std::string::npos) {
|
||||
// The JSON Reference contains a URI and possibly a JSON Pointer
|
||||
return jsonRef.substr(0, ptrPos);
|
||||
}
|
||||
|
||||
// The entire JSON Reference should be treated as a URI
|
||||
return jsonRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract JSON Pointer portion of a JSON Reference
|
||||
*
|
||||
* @param jsonRef JSON Reference to extract from
|
||||
*
|
||||
* @return Optional string containing JSON Pointer
|
||||
*/
|
||||
inline opt::optional<std::string> getJsonReferencePointer(
|
||||
const std::string &jsonRef)
|
||||
{
|
||||
// Attempt to extract JSON Pointer if '#' character is present. Note
|
||||
// that a valid pointer would contain at least a leading forward
|
||||
// slash character.
|
||||
const size_t ptrPos = jsonRef.find("#");
|
||||
if (ptrPos != std::string::npos) {
|
||||
return jsonRef.substr(ptrPos + 1);
|
||||
}
|
||||
|
||||
return opt::optional<std::string>();
|
||||
}
|
||||
|
||||
|
||||
} // namespace json_reference
|
||||
} // namespace internal
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_OPTIONAL_HPP
|
||||
#define __VALIJSON_OPTIONAL_HPP
|
||||
|
||||
#if __cplusplus >= 201703
|
||||
// Visual C++ only supports __has_include in versions 14.12 and greater
|
||||
# if !defined(_MSC_VER) || _MSC_VER >= 1912
|
||||
# if __has_include(<optional>)
|
||||
# include <optional>
|
||||
namespace opt = std;
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# include <compat/optional.hpp>
|
||||
namespace opt = std::experimental;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_INTERNAL_URI_HPP
|
||||
#define __VALIJSON_INTERNAL_URI_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace valijson {
|
||||
namespace internal {
|
||||
namespace uri {
|
||||
|
||||
/**
|
||||
* @brief Placeholder function to check whether a URI is absolute
|
||||
*
|
||||
* This function just checks for '://'
|
||||
*/
|
||||
inline bool isUriAbsolute(const std::string &documentUri)
|
||||
{
|
||||
static const char * placeholderMarker = "://";
|
||||
|
||||
return documentUri.find(placeholderMarker) != std::string::npos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder function to resolve a relative URI within a given scope
|
||||
*/
|
||||
inline std::string resolveRelativeUri(
|
||||
const std::string &resolutionScope,
|
||||
const std::string &relativeUri)
|
||||
{
|
||||
return resolutionScope + relativeUri;
|
||||
}
|
||||
|
||||
} // namespace uri
|
||||
} // namespace internal
|
||||
} // namespace valijson
|
||||
|
||||
#endif // __VALIJSON_INTERNAL_URI_HPP
|
||||
213
examples/validator/valijson/include/valijson/schema.hpp
Normal file
213
examples/validator/valijson/include/valijson/schema.hpp
Normal file
@@ -0,0 +1,213 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_SCHEMA_HPP
|
||||
#define __VALIJSON_SCHEMA_HPP
|
||||
|
||||
#include <cstdio>
|
||||
#include <set>
|
||||
|
||||
#include <valijson/subschema.hpp>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
/**
|
||||
* Represents the root of a JSON Schema
|
||||
*
|
||||
* The root is distinct from other sub-schemas because it is the canonical
|
||||
* starting point for validation of a document against a given a JSON Schema.
|
||||
*/
|
||||
class Schema: public Subschema
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Schema instance with no constraints
|
||||
*/
|
||||
Schema()
|
||||
: sharedEmptySubschema(newSubschema()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a new Schema using custom memory management
|
||||
* functions
|
||||
*
|
||||
* @param allocFn malloc- or new-like function to allocate memory
|
||||
* within Schema, such as for Subschema instances
|
||||
* @param freeFn free-like function to free memory allocated with
|
||||
* the `customAlloc` function
|
||||
*/
|
||||
Schema(CustomAlloc allocFn, CustomFree freeFn)
|
||||
: Subschema(allocFn, freeFn),
|
||||
sharedEmptySubschema(newSubschema()) { }
|
||||
|
||||
/**
|
||||
* @brief Clean up and free all memory managed by the Schema
|
||||
*
|
||||
* Note that any Subschema pointers created and returned by this Schema
|
||||
* should be considered invalid.
|
||||
*/
|
||||
virtual ~Schema()
|
||||
{
|
||||
sharedEmptySubschema->~Subschema();
|
||||
freeFn(const_cast<Subschema *>(sharedEmptySubschema));
|
||||
sharedEmptySubschema = NULL;
|
||||
|
||||
try {
|
||||
for (std::set<Subschema *>::iterator itr = subschemaSet.begin();
|
||||
itr != subschemaSet.end(); ++itr) {
|
||||
Subschema *subschema = *itr;
|
||||
subschema->~Subschema();
|
||||
freeFn(subschema);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
fprintf(stderr, "Caught an exception while destroying Schema: %s",
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a constraint to a specific sub-schema
|
||||
*
|
||||
* @param constraint reference to a constraint that will be copied into
|
||||
* the sub-schema
|
||||
* @param subschema pointer to the sub-schema that will own the copied
|
||||
* constraint
|
||||
*
|
||||
* @throws std::runtime_error if the sub-schema is not owned by this Schema
|
||||
* instance
|
||||
*/
|
||||
void addConstraintToSubschema(const Constraint &constraint,
|
||||
const Subschema *subschema)
|
||||
{
|
||||
// TODO: Check heirarchy for subschemas that do not belong...
|
||||
|
||||
mutableSubschema(subschema)->addConstraint(constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new Subschema instance that is owned by this Schema
|
||||
*
|
||||
* @returns const pointer to the new Subschema instance
|
||||
*/
|
||||
const Subschema * createSubschema()
|
||||
{
|
||||
Subschema *subschema = newSubschema();
|
||||
|
||||
try {
|
||||
if (!subschemaSet.insert(subschema).second) {
|
||||
throw std::runtime_error(
|
||||
"Failed to store pointer for new sub-schema");
|
||||
}
|
||||
} catch (...) {
|
||||
subschema->~Subschema();
|
||||
freeFn(subschema);
|
||||
throw;
|
||||
}
|
||||
|
||||
return subschema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the shared empty schema
|
||||
*/
|
||||
const Subschema * emptySubschema() const
|
||||
{
|
||||
return sharedEmptySubschema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to the root sub-schema of this Schema instance
|
||||
*/
|
||||
const Subschema * root() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the description for one of the sub-schemas owned by this
|
||||
* Schema instance
|
||||
*
|
||||
* @param subschema sub-schema to update
|
||||
* @param description new description
|
||||
*/
|
||||
void setSubschemaDescription(const Subschema *subschema,
|
||||
const std::string &description)
|
||||
{
|
||||
mutableSubschema(subschema)->setDescription(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the ID for one of the sub-schemas owned by this Schema
|
||||
* instance
|
||||
*
|
||||
* @param subschema sub-schema to update
|
||||
* @param id new ID
|
||||
*/
|
||||
void setSubschemaId(const Subschema *subschema, const std::string &id)
|
||||
{
|
||||
mutableSubschema(subschema)->setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the title for one of the sub-schemas owned by this Schema
|
||||
* instance
|
||||
*
|
||||
* @param subschema sub-schema to update
|
||||
* @param title new title
|
||||
*/
|
||||
void setSubschemaTitle(const Subschema *subschema, const std::string &title)
|
||||
{
|
||||
mutableSubschema(subschema)->setTitle(title);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Disable copy construction
|
||||
Schema(const Schema &);
|
||||
|
||||
// Disable copy assignment
|
||||
Schema & operator=(const Schema &);
|
||||
|
||||
Subschema *newSubschema()
|
||||
{
|
||||
void *ptr = allocFn(sizeof(Subschema));
|
||||
if (!ptr) {
|
||||
throw std::runtime_error(
|
||||
"Failed to allocate memory for shared empty sub-schema");
|
||||
}
|
||||
|
||||
try {
|
||||
return new (ptr) Subschema();
|
||||
} catch (...) {
|
||||
freeFn(ptr);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Subschema * mutableSubschema(const Subschema *subschema)
|
||||
{
|
||||
if (subschema == this) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (subschema == sharedEmptySubschema) {
|
||||
throw std::runtime_error(
|
||||
"Cannot modify the shared empty sub-schema");
|
||||
}
|
||||
|
||||
Subschema *noConst = const_cast<Subschema*>(subschema);
|
||||
if (subschemaSet.find(noConst) == subschemaSet.end()) {
|
||||
throw std::runtime_error(
|
||||
"Subschema pointer is not owned by this Schema instance");
|
||||
}
|
||||
|
||||
return noConst;
|
||||
}
|
||||
|
||||
/// Set of Subschema instances owned by this schema
|
||||
std::set<Subschema*> subschemaSet;
|
||||
|
||||
/// Empty schema that can be reused by multiple constraints
|
||||
const Subschema *sharedEmptySubschema;
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
2120
examples/validator/valijson/include/valijson/schema_parser.hpp
Normal file
2120
examples/validator/valijson/include/valijson/schema_parser.hpp
Normal file
File diff suppressed because it is too large
Load Diff
286
examples/validator/valijson/include/valijson/subschema.hpp
Normal file
286
examples/validator/valijson/include/valijson/subschema.hpp
Normal file
@@ -0,0 +1,286 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_SUBSCHEMA_HPP
|
||||
#define __VALIJSON_SUBSCHEMA_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <valijson/constraints/constraint.hpp>
|
||||
#include <valijson/internal/optional.hpp>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
/**
|
||||
* Represents a sub-schema within a JSON Schema
|
||||
*
|
||||
* While all JSON Schemas have at least one sub-schema, the root, some will
|
||||
* have additional sub-schemas that are defined as part of constraints that are
|
||||
* included in the schema. For example, a 'oneOf' constraint maintains a set of
|
||||
* references to one or more nested sub-schemas. As per the definition of a
|
||||
* oneOf constraint, a document is valid within that constraint if it validates
|
||||
* against one of the nested sub-schemas.
|
||||
*/
|
||||
class Subschema
|
||||
{
|
||||
public:
|
||||
/// Typedef for custom new-/malloc-like function
|
||||
typedef void * (*CustomAlloc)(size_t size);
|
||||
|
||||
/// Typedef for custom free-like function
|
||||
typedef void (*CustomFree)(void *);
|
||||
|
||||
/// Typedef the Constraint class into the local namespace for convenience
|
||||
typedef constraints::Constraint Constraint;
|
||||
|
||||
/// Typedef for a function that can be applied to each of the Constraint
|
||||
/// instances owned by a Schema.
|
||||
typedef std::function<bool (const Constraint &)> ApplyFunction;
|
||||
|
||||
/**
|
||||
* @brief Construct a new Subschema object
|
||||
*/
|
||||
Subschema()
|
||||
: allocFn(::operator new)
|
||||
, freeFn(::operator delete) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a new Subschema using custom memory management
|
||||
* functions
|
||||
*
|
||||
* @param allocFn malloc- or new-like function to allocate memory
|
||||
* within Schema, such as for Subschema instances
|
||||
* @param freeFn free-like function to free memory allocated with
|
||||
* the `customAlloc` function
|
||||
*/
|
||||
Subschema(CustomAlloc allocFn, CustomFree freeFn)
|
||||
: allocFn(allocFn)
|
||||
, freeFn(freeFn) { }
|
||||
|
||||
/**
|
||||
* @brief Clean up and free all memory managed by the Subschema
|
||||
*/
|
||||
virtual ~Subschema()
|
||||
{
|
||||
try {
|
||||
for (std::vector<const Constraint *>::iterator itr =
|
||||
constraints.begin(); itr != constraints.end(); ++itr) {
|
||||
Constraint *constraint = const_cast<Constraint *>(*itr);
|
||||
constraint->~Constraint();
|
||||
freeFn(constraint);
|
||||
}
|
||||
constraints.clear();
|
||||
} catch (const std::exception &e) {
|
||||
fprintf(stderr, "Caught an exception in Subschema destructor: %s",
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a constraint to this sub-schema
|
||||
*
|
||||
* The constraint will be copied before being added to the list of
|
||||
* constraints for this Subschema. Note that constraints will be copied
|
||||
* only as deep as references to other Subschemas - e.g. copies of
|
||||
* constraints that refer to sub-schemas, will continue to refer to the
|
||||
* same Subschema instances.
|
||||
*
|
||||
* @param constraint Reference to the constraint to copy
|
||||
*/
|
||||
void addConstraint(const Constraint &constraint)
|
||||
{
|
||||
Constraint *newConstraint = constraint.clone(allocFn, freeFn);
|
||||
try {
|
||||
constraints.push_back(newConstraint);
|
||||
} catch (...) {
|
||||
newConstraint->~Constraint();
|
||||
freeFn(newConstraint);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invoke a function on each child Constraint
|
||||
*
|
||||
* This function will apply the callback function to each constraint in
|
||||
* the Subschema, even if one of the invokations returns \c false. However,
|
||||
* if one or more invokations of the callback function return \c false,
|
||||
* this function will also return \c false.
|
||||
*
|
||||
* @returns \c true if all invokations of the callback function are
|
||||
* successful, \c false otherwise
|
||||
*/
|
||||
bool apply(ApplyFunction &applyFunction) const
|
||||
{
|
||||
bool allTrue = true;
|
||||
for (const Constraint *constraint : constraints) {
|
||||
allTrue = allTrue && applyFunction(*constraint);
|
||||
}
|
||||
|
||||
return allTrue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invoke a function on each child Constraint
|
||||
*
|
||||
* This is a stricter version of the apply() function that will return
|
||||
* immediately if any of the invokations of the callback function return
|
||||
* \c false.
|
||||
*
|
||||
* @returns \c true if all invokations of the callback function are
|
||||
* successful, \c false otherwise
|
||||
*/
|
||||
bool applyStrict(ApplyFunction &applyFunction) const
|
||||
{
|
||||
for (const Constraint *constraint : constraints) {
|
||||
if (!applyFunction(*constraint)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the description associated with this sub-schema
|
||||
*
|
||||
* @throws std::runtime_error if a description has not been set
|
||||
*
|
||||
* @returns string containing sub-schema description
|
||||
*/
|
||||
std::string getDescription() const
|
||||
{
|
||||
if (description) {
|
||||
return *description;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Schema does not have a description");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the ID associated with this sub-schema
|
||||
*
|
||||
* @throws std::runtime_error if an ID has not been set
|
||||
*
|
||||
* @returns string containing sub-schema ID
|
||||
*/
|
||||
std::string getId() const
|
||||
{
|
||||
if (id) {
|
||||
return *id;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Schema does not have an ID");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the title associated with this sub-schema
|
||||
*
|
||||
* @throws std::runtime_error if a title has not been set
|
||||
*
|
||||
* @returns string containing sub-schema title
|
||||
*/
|
||||
std::string getTitle() const
|
||||
{
|
||||
if (title) {
|
||||
return *title;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Schema does not have a title");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether this sub-schema has a description
|
||||
*
|
||||
* @return boolean value
|
||||
*/
|
||||
bool hasDescription() const
|
||||
{
|
||||
return static_cast<bool>(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether this sub-schema has an ID
|
||||
*
|
||||
* @return boolean value
|
||||
*/
|
||||
bool hasId() const
|
||||
{
|
||||
return static_cast<bool>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether this sub-schema has a title
|
||||
*
|
||||
* @return boolean value
|
||||
*/
|
||||
bool hasTitle() const
|
||||
{
|
||||
return static_cast<bool>(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the description for this sub-schema
|
||||
*
|
||||
* The description will not be used for validation, but may be used as part
|
||||
* of the user interface for interacting with schemas and sub-schemas. As
|
||||
* an example, it may be used as part of the validation error descriptions
|
||||
* that are produced by the Validator and ValidationVisitor classes.
|
||||
*
|
||||
* @param description new description
|
||||
*/
|
||||
void setDescription(const std::string &description)
|
||||
{
|
||||
this->description = description;
|
||||
}
|
||||
|
||||
void setId(const std::string &id)
|
||||
{
|
||||
this->id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the title for this sub-schema
|
||||
*
|
||||
* The title will not be used for validation, but may be used as part
|
||||
* of the user interface for interacting with schemas and sub-schema. As an
|
||||
* example, it may be used as part of the validation error descriptions
|
||||
* that are produced by the Validator and ValidationVisitor classes.
|
||||
*
|
||||
* @param title new title
|
||||
*/
|
||||
void setTitle(const std::string &title)
|
||||
{
|
||||
this->title = title;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
CustomAlloc allocFn;
|
||||
|
||||
CustomFree freeFn;
|
||||
|
||||
private:
|
||||
|
||||
// Disable copy construction
|
||||
Subschema(const Subschema &);
|
||||
|
||||
// Disable copy assignment
|
||||
Subschema & operator=(const Subschema &);
|
||||
|
||||
/// List of pointers to constraints that apply to this schema.
|
||||
std::vector<const Constraint *> constraints;
|
||||
|
||||
/// Schema description (optional)
|
||||
opt::optional<std::string> description;
|
||||
|
||||
/// Id to apply when resolving the schema URI
|
||||
opt::optional<std::string> id;
|
||||
|
||||
/// Title string associated with the schema (optional)
|
||||
opt::optional<std::string> title;
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_FILE_UTILS_HPP
|
||||
#define __VALIJSON_FILE_UTILS_HPP
|
||||
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* Load a file into a string
|
||||
*
|
||||
* @param path path to the file to be loaded
|
||||
* @param dest string into which file should be loaded
|
||||
*
|
||||
* @return true if loaded, false otherwise
|
||||
*/
|
||||
inline bool loadFile(const std::string &path, std::string &dest)
|
||||
{
|
||||
// Open file for reading
|
||||
std::ifstream file(path.c_str());
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate space for file contents
|
||||
file.seekg(0, std::ios::end);
|
||||
const std::streamoff offset = file.tellg();
|
||||
if (offset < 0 || offset > std::numeric_limits<unsigned int>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dest.clear();
|
||||
dest.reserve(static_cast<unsigned int>(offset));
|
||||
|
||||
// Assign file contents to destination string
|
||||
file.seekg(0, std::ios::beg);
|
||||
dest.assign(std::istreambuf_iterator<char>(file),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_UTILS_JSON11_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_JSON11_UTILS_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <json11.hpp>
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, json11::Json &document)
|
||||
{
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse schema
|
||||
std::string err;
|
||||
document = json11::Json::parse(file, err);
|
||||
if (!err.empty()) {
|
||||
std::cerr << "json11 failed to parse the document:" << std::endl
|
||||
<< "Parse error: " << err << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_UTILS_JSONCPP_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_JSONCPP_UTILS_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, Json::Value &document)
|
||||
{
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
Json::Reader reader;
|
||||
bool parsingSuccessful = reader.parse(file, document);
|
||||
if (!parsingSuccessful) {
|
||||
std::cerr << "Jsoncpp parser failed to parse the document:" << std::endl
|
||||
<< reader.getFormattedErrorMessages();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#ifndef VALIJSON_NLOHMANN_JSON_UTILS_HPP
|
||||
#define VALIJSON_NLOHMANN_JSON_UTILS_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <json.hpp>
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, nlohmann::json &document) {
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'."
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse schema
|
||||
try {
|
||||
document = nlohmann::json::parse(file);
|
||||
} catch (std::invalid_argument const& exception) {
|
||||
std::cerr << "nlohmann::json failed to parse the document\n"
|
||||
<< "Parse error:" << exception.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif //VALIJSON_NLOHMANN_JSON_UTILS_HPP
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_UTILS_PICOJSON_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_PICOJSON_UTILS_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, picojson::value &document)
|
||||
{
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse schema
|
||||
std::string err = picojson::parse(document, file);
|
||||
if (!err.empty()) {
|
||||
std::cerr << "PicoJson failed to parse the document:" << std::endl
|
||||
<< "Parse error: " << err << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#ifndef VALIJSON_POCO_JSON_UTILS_HPP
|
||||
#define VALIJSON_POCO_JSON_UTILS_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <Poco/JSON/JSONException.h>
|
||||
#include <Poco/JSON/Object.h>
|
||||
#include <Poco/JSON/Parser.h>
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, Poco::Dynamic::Var &document) {
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'."
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse schema
|
||||
try {
|
||||
document = Poco::JSON::Parser().parse(file);
|
||||
} catch (Poco::Exception const& exception) {
|
||||
std::cerr << "Poco::JSON failed to parse the document\n"
|
||||
<< "Parse error:" << exception.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif //VALIJSON_POCO_JSON_UTILS_HPP
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_UTILS_PROPERTY_TREE_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_PROPERTY_TREE_UTILS_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
# include <boost/property_tree/json_parser.hpp>
|
||||
# pragma clang diagnostic pop
|
||||
#else
|
||||
# include <boost/property_tree/json_parser.hpp>
|
||||
#endif
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, boost::property_tree::ptree &document)
|
||||
{
|
||||
try {
|
||||
boost::property_tree::read_json(path, document);
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "Boost Property Tree JSON parser failed to parse the document:" << std::endl;
|
||||
std::cerr << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_UTILS_QTJSON_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_QTJSON_UTILS_HPP
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, QJsonValue &root)
|
||||
{
|
||||
// Load schema JSON from file
|
||||
QFile file(QString::fromStdString(path));
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
// Parse schema
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||
if (doc.isNull()) {
|
||||
std::cerr << "qt failed to parse the document:" << std::endl
|
||||
<< parseError.errorString().toStdString() << std::endl;
|
||||
return false;
|
||||
} else if (doc.isObject()) {
|
||||
root = QJsonValue(doc.object());
|
||||
} else if (doc.isArray()) {
|
||||
root = QJsonValue(doc.array());
|
||||
} else if (doc.isEmpty()) {
|
||||
root = QJsonValue();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_UTILS_RAPIDJSON_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_RAPIDJSON_UTILS_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
template<typename Encoding, typename Allocator>
|
||||
inline bool loadDocument(const std::string &path, rapidjson::GenericDocument<Encoding, Allocator> &document)
|
||||
{
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse schema
|
||||
document.template Parse<0>(file.c_str());
|
||||
if (document.HasParseError()) {
|
||||
std::cerr << "RapidJson failed to parse the document:" << std::endl;
|
||||
std::cerr << "Parse error: " << document.GetParseError() << std::endl;
|
||||
std::cerr << "Near: " << file.substr((std::max)(size_t(0), document.GetErrorOffset() - 20), 40) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_UTILS_UTF8_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_UTF8_UTILS_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
/*
|
||||
Basic UTF-8 manipulation routines, adapted from code that was released into
|
||||
the public domain by Jeff Bezanson.
|
||||
*/
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
static const uint32_t offsetsFromUTF8[6] = {
|
||||
0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
||||
0x03C82080UL, 0xFA082080UL, 0x82082080UL
|
||||
};
|
||||
|
||||
/* is c the start of a utf8 sequence? */
|
||||
inline bool isutf(char c) {
|
||||
return ((c & 0xC0) != 0x80);
|
||||
}
|
||||
|
||||
/* reads the next utf-8 sequence out of a string, updating an index */
|
||||
inline uint32_t u8_nextchar(const char *s, int *i)
|
||||
{
|
||||
uint32_t ch = 0;
|
||||
int sz = 0;
|
||||
|
||||
do {
|
||||
ch <<= 6;
|
||||
ch += (unsigned char)s[(*i)++];
|
||||
sz++;
|
||||
} while (s[*i] && !isutf(s[*i]));
|
||||
ch -= offsetsFromUTF8[sz-1];
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
/* number of characters */
|
||||
inline uint64_t u8_strlen(const char *s)
|
||||
{
|
||||
static const int maxLength = std::numeric_limits<int>::max();
|
||||
|
||||
uint64_t count = 0;
|
||||
int i = 0;
|
||||
|
||||
while (s[i] != 0 && u8_nextchar(s, &i) != 0) {
|
||||
if (i == maxLength) {
|
||||
throw std::runtime_error(
|
||||
"String exceeded maximum size of " +
|
||||
std::to_string(maxLength) + " bytes.");
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_VALIDATION_RESULTS_HPP
|
||||
#define __VALIJSON_VALIDATION_RESULTS_HPP
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
/**
|
||||
* @brief Class that encapsulates the storage of validation errors.
|
||||
*
|
||||
* This class maintains an internal FIFO queue of errors that are reported
|
||||
* during validation. Errors are pushed on to the back of an internal
|
||||
* queue, and can retrieved by popping them from the front of the queue.
|
||||
*/
|
||||
class ValidationResults
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Describes a validation error.
|
||||
*
|
||||
* This struct is used to pass around the context and description of a
|
||||
* validation error.
|
||||
*/
|
||||
struct Error
|
||||
{
|
||||
/**
|
||||
* @brief Construct an Error object with no context or description.
|
||||
*/
|
||||
Error() { }
|
||||
|
||||
/**
|
||||
* @brief Construct an Error object using a context and description.
|
||||
*
|
||||
* @param context Context string to use
|
||||
* @param description Description string to use
|
||||
*/
|
||||
Error(const std::vector<std::string> &context, const std::string &description)
|
||||
: context(context),
|
||||
description(description) { }
|
||||
|
||||
/// Path to the node that failed validation.
|
||||
std::vector<std::string> context;
|
||||
|
||||
/// A detailed description of the validation error.
|
||||
std::string description;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return begin iterator for results in the queue.
|
||||
*/
|
||||
std::deque<Error>::const_iterator begin() const
|
||||
{
|
||||
return errors.begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return end iterator for results in the queue.
|
||||
*/
|
||||
std::deque<Error>::const_iterator end() const
|
||||
{
|
||||
return errors.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the number of errors in the queue.
|
||||
*/
|
||||
size_t numErrors() const
|
||||
{
|
||||
return errors.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy an Error and push it on to the back of the queue.
|
||||
*
|
||||
* @param error Reference to an Error object to be copied.
|
||||
*/
|
||||
void pushError(const Error &error)
|
||||
{
|
||||
errors.push_back(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Push an error onto the back of the queue.
|
||||
*
|
||||
* @param context Context of the validation error.
|
||||
* @param description Description of the validation error.
|
||||
*/
|
||||
void
|
||||
pushError(const std::vector<std::string> &context, const std::string &description)
|
||||
{
|
||||
errors.push_back(Error(context, description));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pop an error from the front of the queue.
|
||||
*
|
||||
* @param error Reference to an Error object to populate.
|
||||
*
|
||||
* @returns true if an Error was popped, false otherwise.
|
||||
*/
|
||||
bool
|
||||
popError(Error &error)
|
||||
{
|
||||
if (errors.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
error = errors.front();
|
||||
errors.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// FIFO queue of validation errors that have been reported
|
||||
std::deque<Error> errors;
|
||||
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif // __VALIJSON_VALIDATION_RESULTS_HPP
|
||||
1706
examples/validator/valijson/include/valijson/validation_visitor.hpp
Normal file
1706
examples/validator/valijson/include/valijson/validation_visitor.hpp
Normal file
File diff suppressed because it is too large
Load Diff
78
examples/validator/valijson/include/valijson/validator.hpp
Normal file
78
examples/validator/valijson/include/valijson/validator.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_VALIDATOR_HPP
|
||||
#define __VALIJSON_VALIDATOR_HPP
|
||||
|
||||
#include <valijson/schema.hpp>
|
||||
#include <valijson/validation_visitor.hpp>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
class Schema;
|
||||
class ValidationResults;
|
||||
|
||||
/**
|
||||
* @brief Class that provides validation functionality.
|
||||
*/
|
||||
class Validator
|
||||
{
|
||||
public:
|
||||
enum TypeCheckingMode
|
||||
{
|
||||
kStrongTypes,
|
||||
kWeakTypes
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct a Validator that uses strong type checking by default
|
||||
*/
|
||||
Validator()
|
||||
: strictTypes(true) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a Validator using a specific type checking mode
|
||||
*
|
||||
* @param typeCheckingMode choice of strong or weak type checking
|
||||
*/
|
||||
Validator(TypeCheckingMode typeCheckingMode)
|
||||
: strictTypes(typeCheckingMode == kStrongTypes) { }
|
||||
|
||||
/**
|
||||
* @brief Validate a JSON document and optionally return the results.
|
||||
*
|
||||
* When a ValidationResults object is provided via the \c results parameter,
|
||||
* validation will be performed against each constraint defined by the
|
||||
* schema, even if validation fails for some or all constraints.
|
||||
*
|
||||
* If a pointer to a ValidationResults instance is not provided, validation
|
||||
* will only continue for as long as the constraints are validated
|
||||
* successfully.
|
||||
*
|
||||
* @param schema The schema to validate against
|
||||
* @param target A rapidjson::Value to be validated
|
||||
*
|
||||
* @param results An optional pointer to a ValidationResults instance that
|
||||
* will be used to report validation errors
|
||||
*
|
||||
* @returns true if validation succeeds, false otherwise
|
||||
*/
|
||||
template<typename AdapterType>
|
||||
bool validate(const Subschema &schema, const AdapterType &target,
|
||||
ValidationResults *results)
|
||||
{
|
||||
// Construct a ValidationVisitor to perform validation at the root level
|
||||
ValidationVisitor<AdapterType> v(target,
|
||||
std::vector<std::string>(1, "<root>"), strictTypes, results);
|
||||
|
||||
return v.validateSchema(schema);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Flag indicating that strict type comparisons should be used
|
||||
const bool strictTypes;
|
||||
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
all: ../tiny_gltf.h
|
||||
clang++ -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester tester.cc
|
||||
clang++ -DTINYGLTF_NOEXCEPTION -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester_noexcept tester.cc
|
||||
# clang++ -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester_noexcept tester.cc
|
||||
|
||||
2310
tiny_gltf.h
2310
tiny_gltf.h
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user