├── .clang-format ├── .github └── workflows │ ├── cifuzz.yml │ └── cmake.yml ├── .gitignore ├── .gitmodules ├── Authors ├── CMakeLists.txt ├── Doxyfile ├── LICENSE ├── README.md ├── bundle.sh ├── cmake ├── CMakeFindExtensions.cmake ├── CMakeHelpers.cmake ├── Findcurlpp.cmake └── README ├── doc ├── schema │ ├── draft-03.json │ ├── draft-04.json │ └── draft-07.json ├── screenshots │ ├── inspector.png │ └── wasm.png └── specifications │ ├── draft-fge-json-schema-validation-00.txt │ ├── draft-luff-json-hyper-schema-00.txt │ ├── draft-pbryan-zyp-json-ref-03.txt │ ├── draft-zyp-json-schema-03.txt │ ├── draft-zyp-json-schema-04.txt │ ├── rfc3339-timestamps.txt │ ├── rfc3986-uri.txt │ ├── rfc4627-json.txt │ └── rfc6901-json-pointer.txt ├── examples ├── array_iteration_basics.cpp ├── array_iteration_template_fn.cpp ├── boost_json_example.cpp ├── check_schema.cpp ├── custom_schema.cpp ├── external_schema.cpp ├── json_pointers.cpp ├── object_iteration.cpp ├── picojson_format_test.cpp ├── remote_resolution_local_file.cpp ├── remote_resolution_url.cpp ├── valijson_nlohmann_bundled.hpp └── valijson_nlohmann_bundled_test.cpp ├── include ├── compat │ └── optional.hpp └── valijson │ ├── adapters │ ├── boost_json_adapter.hpp │ ├── json11_adapter.hpp │ ├── jsoncpp_adapter.hpp │ ├── nlohmann_json_adapter.hpp │ ├── picojson_adapter.hpp │ ├── poco_json_adapter.hpp │ ├── property_tree_adapter.hpp │ ├── qtjson_adapter.hpp │ ├── rapidjson_adapter.hpp │ ├── std_string_adapter.hpp │ └── yaml_cpp_adapter.hpp │ ├── constraint_builder.hpp │ ├── constraints │ ├── basic_constraint.hpp │ ├── concrete_constraints.hpp │ ├── constraint.hpp │ └── constraint_visitor.hpp │ ├── exceptions.hpp │ ├── internal │ ├── adapter.hpp │ ├── basic_adapter.hpp │ ├── custom_allocator.hpp │ ├── debug.hpp │ ├── frozen_value.hpp │ ├── json_pointer.hpp │ ├── json_reference.hpp │ ├── optional.hpp │ ├── optional_bundled.hpp │ ├── regex.hpp │ └── uri.hpp │ ├── schema.hpp │ ├── schema_cache.hpp │ ├── schema_parser.hpp │ ├── subschema.hpp │ ├── utils │ ├── boost_json_utils.hpp │ ├── file_utils.hpp │ ├── json11_utils.hpp │ ├── jsoncpp_utils.hpp │ ├── nlohmann_json_utils.hpp │ ├── picojson_utils.hpp │ ├── poco_json_utils.hpp │ ├── property_tree_utils.hpp │ ├── qtjson_utils.hpp │ ├── rapidjson_utils.hpp │ ├── utf8_utils.hpp │ └── yaml_cpp_utils.hpp │ ├── validation_results.hpp │ ├── validation_visitor.hpp │ └── validator.hpp ├── inspector ├── .gitignore ├── CMakeLists.txt ├── cmake │ ├── MacOSXBundleInfo.plist.in │ ├── QtCommon.cmake │ └── windows_metafile.rc.in ├── inspector.qrc ├── res │ ├── AppIcon.icns │ └── AppIcon.ico └── src │ ├── highlighter.cpp │ ├── highlighter.h │ ├── main.cpp │ ├── window.cpp │ └── window.h ├── shellcheck.sh └── tests ├── data ├── documents │ ├── array_doubles_10_20_30_40.json │ ├── array_doubles_1_2_3.json │ ├── array_doubles_1_2_3_4.json │ ├── array_empty.json │ ├── array_integers_10_20_30_40.json │ ├── array_integers_1_2_3.json │ ├── array_integers_1_2_3_4.json │ ├── array_strings_10_20_30_40.json │ ├── array_strings_1_2_3.json │ ├── array_strings_1_2_3_4.json │ ├── date_time_format.json │ └── object_empty.json └── schemas │ ├── allof_integers_and_numbers.schema.json │ ├── circular_reference.schema.json │ └── date_time_format.schema.json ├── fuzzing ├── fuzzer.cpp └── oss-fuzz-build.sh ├── test_adapter_comparison.cpp ├── test_boost_json_adapter.cpp ├── test_date_time_format.cpp ├── test_fetch_absolute_uri_document_callback.cpp ├── test_fetch_urn_document_callback.cpp ├── test_json11_adapter.cpp ├── test_json_pointer.cpp ├── test_jsoncpp_adapter.cpp ├── test_nlohmann_json_adapter.cpp ├── test_picojson_adapter.cpp ├── test_poco_json_adapter.cpp ├── test_poly_constraint.cpp ├── test_property_tree_adapter.cpp ├── test_qtjson_adapter.cpp ├── test_rapidjson_adapter.cpp ├── test_utf8_utils.cpp ├── test_validation_errors.cpp ├── test_validator.cpp ├── test_validator_with_custom_regular_expression_engine.cpp └── test_yaml_cpp_adapter.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | BraceWrapping: 4 | AfterClass: true 5 | AfterControlStatement: false 6 | AfterEnum: true 7 | AfterFunction: true 8 | AfterNamespace: false 9 | AfterObjCDeclaration: true 10 | AfterStruct: true 11 | AfterUnion: true 12 | BeforeCatch: false 13 | BeforeElse: false 14 | IndentBraces: false 15 | BreakBeforeBraces: Custom 16 | IndentWidth: 4 17 | AllowShortFunctionsOnASingleLine: Empty 18 | -------------------------------------------------------------------------------- /.github/workflows/cifuzz.yml: -------------------------------------------------------------------------------- 1 | name: CIFuzz 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | Fuzzing: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | security-events: write 14 | steps: 15 | - name: Build Fuzzers 16 | id: build 17 | uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master 18 | with: 19 | oss-fuzz-project-name: 'valijson' 20 | language: c++ 21 | - name: Run Fuzzers 22 | uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master 23 | with: 24 | oss-fuzz-project-name: 'valijson' 25 | language: c++ 26 | fuzz-seconds: 300 27 | output-sarif: true 28 | - name: Upload Crash 29 | uses: actions/upload-artifact@v4 30 | if: failure() && steps.build.outcome == 'success' 31 | with: 32 | name: artifacts 33 | path: ./out/artifacts 34 | - name: Upload Sarif 35 | if: always() && steps.build.outcome == 'success' 36 | uses: github/codeql-action/upload-sarif@v3 37 | with: 38 | # Path to SARIF file relative to the root of the repository 39 | sarif_file: cifuzz-sarif/results.sarif 40 | checkout_path: cifuzz-sarif 41 | category: CIFuzz 42 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | with: 23 | submodules: 'recursive' 24 | 25 | - name: Install dependencies 26 | run: | 27 | sudo apt update --yes 28 | sudo apt install --yes libboost-all-dev qtbase5-dev libcurlpp-dev libcurl4-openssl-dev 29 | 30 | - name: Configure CMake 31 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 32 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 33 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -Dvalijson_BUILD_TESTS=ON -Dvalijson_BUILD_EXAMPLES=ON 34 | 35 | - name: Build 36 | # Build your program with the given configuration 37 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j$(nproc) 38 | 39 | - name: Test 40 | working-directory: ${{github.workspace}}/build 41 | run: ./test_suite -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | bin 3 | xcode/valijson.xcodeproj/project.xcworkspace 4 | xcode/valijson.xcodeproj/project.xcworkspace/xcuserdata 5 | xcode/valijson.xcodeproj/xcuserdata 6 | doc/html 7 | .idea 8 | cmake-build-* 9 | CMakeFiles/ 10 | .vs 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "thirdparty/picojson"] 2 | path = thirdparty/picojson 3 | url = https://github.com/tristanpenman/picojson.git 4 | shallow = true 5 | [submodule "thirdparty/rapidjson"] 6 | path = thirdparty/rapidjson 7 | url = https://github.com/Tencent/rapidjson.git 8 | shallow = true 9 | [submodule "thirdparty/nlohmann-json"] 10 | path = thirdparty/nlohmann-json 11 | url = https://github.com/nlohmann/json.git 12 | shallow = true 13 | [submodule "thirdparty/yaml-cpp"] 14 | path = thirdparty/yaml-cpp 15 | url = https://github.com/jbeder/yaml-cpp.git 16 | shallow = true 17 | [submodule "thirdparty/jsoncpp"] 18 | path = thirdparty/jsoncpp 19 | url = https://github.com/open-source-parsers/jsoncpp.git 20 | shallow = true 21 | [submodule "thirdparty/json11"] 22 | path = thirdparty/json11 23 | url = https://github.com/dropbox/json11.git 24 | shallow = true 25 | [submodule "thirdparty/googletest"] 26 | path = thirdparty/googletest 27 | url = https://github.com/google/googletest.git 28 | shallow = true 29 | [submodule "thirdparty/JSON-Schema-Test-Suite"] 30 | path = thirdparty/JSON-Schema-Test-Suite 31 | url = https://github.com/json-schema-org/JSON-Schema-Test-Suite.git 32 | shallow = true 33 | -------------------------------------------------------------------------------- /Authors: -------------------------------------------------------------------------------- 1 | Tristan Penman, tristan@tristanpenman.com 2 | Core library development 3 | 4 | James Jensen, jjensen@akamai.com, changes Copyright (c) 2016 Akamai Technologies 5 | Polymorphic constraint support 6 | 7 | Tengiz Sharafiev, btolfa+github@gmail.com 8 | Adapter for nlohmann/json parser library 9 | 10 | hotwatermorning (github username), hotwatermorning@gmail.com 11 | Adapter for json11 parser library 12 | 13 | shsfre09 (github username), 14 | Adapter for picojson parser library 15 | 16 | Michael Smith, michael.smith@puppetlabs.com 17 | Memory management improvements for RapidJson parser library 18 | 19 | Richard Clamp, richardc@unixbeard.net 20 | Boost-related fixes 21 | 22 | Lars Immisch, lars@ibp.de 23 | noboost branch 24 | 25 | Nicolas Witczak, n_witczak@yahoo.com 26 | C++11 improvements 27 | 28 | Martin Robinson, 0x4d52@gmail.com 29 | Bug fixes 30 | 31 | Pierre Lamot, pierre.lamot@yahoo.fr 32 | Adapter for Qt JSON parser 33 | 34 | drewxa (github username), bo.ro.da@mail.ru 35 | Adapter for Poco JSON parser 36 | 37 | Jordan Bayles (jophba), jophba@chromium.org 38 | JsonCpp owner 39 | 40 | Matt Young (matty0ung), 41 | Adapter for Boost.JSON parser library 42 | 43 | Pras Velagapudi (psigen), 44 | Adapter for yaml-cpp parser library 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Tristan Penman 2 | Copyright (c) 2016, Akamai Technologies, Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /bundle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Exit codes: 5 | # 6 | # 0 - success 7 | # 64 - usage 8 | # 65 - invalid adapter 9 | # 10 | 11 | set -euo pipefail 12 | 13 | adapter_path=include/valijson/adapters 14 | utils_path=include/valijson/utils 15 | 16 | # find all available adapters 17 | pushd "${adapter_path}" > /dev/null 18 | adapters=($(ls *.hpp)) 19 | popd > /dev/null 20 | 21 | # remove _adapter.hpp suffix 22 | adapters=("${adapters[@]/_adapter.hpp/}") 23 | 24 | usage() { 25 | echo 'Generates a single header file for a particular Valijson adapter' 26 | echo 27 | echo 'This makes it easier to embed Valijson in smaller projects, where integrating a' 28 | echo 'third-party dependency is inconvenient or undesirable.' 29 | echo 30 | echo 'Output is written to STDOUT.' 31 | echo 32 | echo 'Usage:' 33 | echo 34 | echo ' ./bundle.sh ' 35 | echo 36 | echo 'Example usage:' 37 | echo 38 | echo ' ./bundle.sh nlohmann_json > valijson_nlohmann_bundled.hpp' 39 | echo 40 | echo 'Available adapters:' 41 | echo 42 | for adapter in "${adapters[@]}"; do 43 | echo " - ${adapter}" 44 | done 45 | echo 46 | exit 64 47 | } 48 | 49 | if [ $# -ne 1 ]; then 50 | usage 51 | fi 52 | 53 | adapter_header= 54 | for adapter in "${adapters[@]}"; do 55 | if [ "${adapter}" == "$1" ]; then 56 | adapter_header="${adapter_path}/${adapter}_adapter.hpp" 57 | break 58 | fi 59 | done 60 | 61 | if [ -z "${adapter_header}" ]; then 62 | echo "Error: Adapter name is not valid." 63 | exit 65 64 | fi 65 | 66 | common_headers=( 67 | include/valijson/exceptions.hpp 68 | include/compat/optional.hpp 69 | include/valijson/internal/optional_bundled.hpp 70 | include/valijson/internal/adapter.hpp 71 | include/valijson/internal/basic_adapter.hpp 72 | include/valijson/internal/custom_allocator.hpp 73 | include/valijson/internal/debug.hpp 74 | include/valijson/internal/frozen_value.hpp 75 | include/valijson/internal/json_pointer.hpp 76 | include/valijson/internal/json_reference.hpp 77 | include/valijson/internal/uri.hpp 78 | include/valijson/utils/file_utils.hpp 79 | include/valijson/utils/utf8_utils.hpp 80 | include/valijson/constraints/constraint.hpp 81 | include/valijson/subschema.hpp 82 | include/valijson/schema_cache.hpp 83 | include/valijson/schema.hpp 84 | include/valijson/constraints/constraint_visitor.hpp 85 | include/valijson/constraints/basic_constraint.hpp 86 | include/valijson/constraints/concrete_constraints.hpp 87 | include/valijson/constraint_builder.hpp 88 | include/valijson/schema_parser.hpp 89 | include/valijson/adapters/std_string_adapter.hpp 90 | include/valijson/validation_results.hpp 91 | include/valijson/validation_visitor.hpp 92 | include/valijson/validator.hpp) 93 | 94 | # remove internal #includes 95 | grep --no-filename -v "include "help string describing the option" [IF ]) 139 | # 140 | macro(set_option variable description value) 141 | set(__value ${value}) 142 | set(__condition "") 143 | set(__varname "__value") 144 | foreach(arg ${ARGN}) 145 | if(arg STREQUAL "IF" OR arg STREQUAL "if") 146 | set(__varname "__condition") 147 | else() 148 | list(APPEND ${__varname} ${arg}) 149 | endif() 150 | endforeach() 151 | unset(__varname) 152 | if(__condition STREQUAL "") 153 | set(__condition 2 GREATER 1) 154 | endif() 155 | 156 | if(DEFINED ${variable}) 157 | # set the default option value from existing value or command line arguments 158 | option(${variable} "${description}" ${${variable}}) 159 | else() 160 | # if variable not defined set default from condition 161 | if(${__condition}) 162 | if(${__value} MATCHES ";") 163 | if(${__value}) 164 | option(${variable} "${description}" ON) 165 | else() 166 | option(${variable} "${description}" OFF) 167 | endif() 168 | elseif(DEFINED ${__value}) 169 | if(${__value}) 170 | option(${variable} "${description}" ON) 171 | else() 172 | option(${variable} "${description}" OFF) 173 | endif() 174 | else() 175 | option(${variable} "${description}" ${__value}) 176 | endif() 177 | else() 178 | unset(${variable} CACHE) 179 | endif() 180 | endif() 181 | unset(__condition) 182 | unset(__value) 183 | endmacro() 184 | 185 | 186 | # 187 | ### Function: status 188 | # 189 | # Status report function. 190 | # Automatically align right column and selects text based on condition. 191 | # Usage: 192 | # status() 193 | # status( [ ...]) 194 | # status( THEN ELSE ) 195 | # 196 | function(status text) 197 | set(status_cond) 198 | set(status_then) 199 | set(status_else) 200 | 201 | set(status_current_name "cond") 202 | foreach(arg ${ARGN}) 203 | if(arg STREQUAL "THEN") 204 | set(status_current_name "then") 205 | elseif(arg STREQUAL "ELSE") 206 | set(status_current_name "else") 207 | else() 208 | list(APPEND status_${status_current_name} ${arg}) 209 | endif() 210 | endforeach() 211 | 212 | if(DEFINED status_cond) 213 | set(status_placeholder_length 32) 214 | string(RANDOM LENGTH ${status_placeholder_length} ALPHABET " " status_placeholder) 215 | string(LENGTH "${text}" status_text_length) 216 | if(status_text_length LESS status_placeholder_length) 217 | string(SUBSTRING "${text}${status_placeholder}" 0 ${status_placeholder_length} status_text) 218 | elseif(DEFINED status_then OR DEFINED status_else) 219 | message(STATUS "${text}") 220 | set(status_text "${status_placeholder}") 221 | else() 222 | set(status_text "${text}") 223 | endif() 224 | 225 | if(DEFINED status_then OR DEFINED status_else) 226 | if(${status_cond}) 227 | string(REPLACE ";" " " status_then "${status_then}") 228 | string(REGEX REPLACE "^[ \t]+" "" status_then "${status_then}") 229 | message(STATUS "${status_text} ${status_then}") 230 | else() 231 | string(REPLACE ";" " " status_else "${status_else}") 232 | string(REGEX REPLACE "^[ \t]+" "" status_else "${status_else}") 233 | message(STATUS "${status_text} ${status_else}") 234 | endif() 235 | else() 236 | string(REPLACE ";" " " status_cond "${status_cond}") 237 | string(REGEX REPLACE "^[ \t]+" "" status_cond "${status_cond}") 238 | message(STATUS "${status_text} ${status_cond}") 239 | endif() 240 | else() 241 | message(STATUS "${text}") 242 | endif() 243 | endfunction() 244 | 245 | # 246 | ### Macro: messageV 247 | # 248 | # Prints message only with MSG_VERBOSE=ON 249 | # Usage: 250 | # messageV() 251 | # 252 | function(messageV text) 253 | if(${MSG_VERBOSE}) 254 | message(STATUS "${text}") 255 | endif() 256 | endfunction() 257 | -------------------------------------------------------------------------------- /cmake/Findcurlpp.cmake: -------------------------------------------------------------------------------- 1 | include(FindPkgConfig) 2 | include(FindPackageHandleStandardArgs) 3 | 4 | pkg_check_modules(curlpp_PKGCONF REQUIRED curlpp) 5 | 6 | find_path(curlpp_INCLUDE_DIR 7 | NAMES curlpp/cURLpp.hpp 8 | PATHS ${curlpp_PKGCONF_INCLUDE_DIRS} 9 | ) 10 | 11 | find_library(curlpp_LIBRARIES 12 | NAMES curlpp 13 | PATHS ${curlpp_PKGCONF_LIBRARY_DIRS} 14 | ) 15 | 16 | if(curlpp_PKGCONF_FOUND) 17 | set(curlpp_FOUND yes) 18 | else() 19 | set(curlpp_FOUND no) 20 | endif() 21 | -------------------------------------------------------------------------------- /cmake/README: -------------------------------------------------------------------------------- 1 | CMake modules copied from: 2 | 3 | https://github.com/sourcey/libsourcey 4 | 5 | -------------------------------------------------------------------------------- /doc/schema/draft-03.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema" : "http://json-schema.org/draft-03/schema#", 3 | "id" : "http://json-schema.org/draft-03/schema#", 4 | "type" : "object", 5 | 6 | "properties" : { 7 | "type" : { 8 | "type" : ["string", "array"], 9 | "items" : { 10 | "type" : ["string", {"$ref" : "#"}] 11 | }, 12 | "uniqueItems" : true, 13 | "default" : "any" 14 | }, 15 | 16 | "properties" : { 17 | "type" : "object", 18 | "additionalProperties" : {"$ref" : "#"}, 19 | "default" : {} 20 | }, 21 | 22 | "patternProperties" : { 23 | "type" : "object", 24 | "additionalProperties" : {"$ref" : "#"}, 25 | "default" : {} 26 | }, 27 | 28 | "additionalProperties" : { 29 | "type" : [{"$ref" : "#"}, "boolean"], 30 | "default" : {} 31 | }, 32 | 33 | "items" : { 34 | "type" : [{"$ref" : "#"}, "array"], 35 | "items" : {"$ref" : "#"}, 36 | "default" : {} 37 | }, 38 | 39 | "additionalItems" : { 40 | "type" : [{"$ref" : "#"}, "boolean"], 41 | "default" : {} 42 | }, 43 | 44 | "required" : { 45 | "type" : "boolean", 46 | "default" : false 47 | }, 48 | 49 | "dependencies" : { 50 | "type" : "object", 51 | "additionalProperties" : { 52 | "type" : ["string", "array", {"$ref" : "#"}], 53 | "items" : { 54 | "type" : "string" 55 | } 56 | }, 57 | "default" : {} 58 | }, 59 | 60 | "minimum" : { 61 | "type" : "number" 62 | }, 63 | 64 | "maximum" : { 65 | "type" : "number" 66 | }, 67 | 68 | "exclusiveMinimum" : { 69 | "type" : "boolean", 70 | "default" : false 71 | }, 72 | 73 | "exclusiveMaximum" : { 74 | "type" : "boolean", 75 | "default" : false 76 | }, 77 | 78 | "minItems" : { 79 | "type" : "integer", 80 | "minimum" : 0, 81 | "default" : 0 82 | }, 83 | 84 | "maxItems" : { 85 | "type" : "integer", 86 | "minimum" : 0 87 | }, 88 | 89 | "uniqueItems" : { 90 | "type" : "boolean", 91 | "default" : false 92 | }, 93 | 94 | "pattern" : { 95 | "type" : "string", 96 | "format" : "regex" 97 | }, 98 | 99 | "minLength" : { 100 | "type" : "integer", 101 | "minimum" : 0, 102 | "default" : 0 103 | }, 104 | 105 | "maxLength" : { 106 | "type" : "integer" 107 | }, 108 | 109 | "enum" : { 110 | "type" : "array", 111 | "minItems" : 1, 112 | "uniqueItems" : true 113 | }, 114 | 115 | "default" : { 116 | "type" : "any" 117 | }, 118 | 119 | "title" : { 120 | "type" : "string" 121 | }, 122 | 123 | "description" : { 124 | "type" : "string" 125 | }, 126 | 127 | "format" : { 128 | "type" : "string" 129 | }, 130 | 131 | "divisibleBy" : { 132 | "type" : "number", 133 | "minimum" : 0, 134 | "exclusiveMinimum" : true, 135 | "default" : 1 136 | }, 137 | 138 | "disallow" : { 139 | "type" : ["string", "array"], 140 | "items" : { 141 | "type" : ["string", {"$ref" : "#"}] 142 | }, 143 | "uniqueItems" : true 144 | }, 145 | 146 | "extends" : { 147 | "type" : [{"$ref" : "#"}, "array"], 148 | "items" : {"$ref" : "#"}, 149 | "default" : {} 150 | }, 151 | 152 | "id" : { 153 | "type" : "string", 154 | "format" : "uri" 155 | }, 156 | 157 | "$ref" : { 158 | "type" : "string", 159 | "format" : "uri" 160 | }, 161 | 162 | "$schema" : { 163 | "type" : "string", 164 | "format" : "uri" 165 | } 166 | }, 167 | 168 | "dependencies" : { 169 | "exclusiveMinimum" : "minimum", 170 | "exclusiveMaximum" : "maximum" 171 | }, 172 | 173 | "default" : {} 174 | } -------------------------------------------------------------------------------- /doc/schema/draft-04.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://json-schema.org/draft-04/schema#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "description": "Core schema meta-schema", 5 | "definitions": { 6 | "schemaArray": { 7 | "type": "array", 8 | "minItems": 1, 9 | "items": { "$ref": "#" } 10 | }, 11 | "positiveInteger": { 12 | "type": "integer", 13 | "minimum": 0 14 | }, 15 | "positiveIntegerDefault0": { 16 | "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] 17 | }, 18 | "simpleTypes": { 19 | "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] 20 | }, 21 | "stringArray": { 22 | "type": "array", 23 | "items": { "type": "string" }, 24 | "minItems": 1, 25 | "uniqueItems": true 26 | } 27 | }, 28 | "type": "object", 29 | "properties": { 30 | "id": { 31 | "type": "string", 32 | "format": "uri" 33 | }, 34 | "$schema": { 35 | "type": "string", 36 | "format": "uri" 37 | }, 38 | "title": { 39 | "type": "string" 40 | }, 41 | "description": { 42 | "type": "string" 43 | }, 44 | "default": {}, 45 | "multipleOf": { 46 | "type": "number", 47 | "minimum": 0, 48 | "exclusiveMinimum": true 49 | }, 50 | "maximum": { 51 | "type": "number" 52 | }, 53 | "exclusiveMaximum": { 54 | "type": "boolean", 55 | "default": false 56 | }, 57 | "minimum": { 58 | "type": "number" 59 | }, 60 | "exclusiveMinimum": { 61 | "type": "boolean", 62 | "default": false 63 | }, 64 | "maxLength": { "$ref": "#/definitions/positiveInteger" }, 65 | "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, 66 | "pattern": { 67 | "type": "string", 68 | "format": "regex" 69 | }, 70 | "additionalItems": { 71 | "anyOf": [ 72 | { "type": "boolean" }, 73 | { "$ref": "#" } 74 | ], 75 | "default": {} 76 | }, 77 | "items": { 78 | "anyOf": [ 79 | { "$ref": "#" }, 80 | { "$ref": "#/definitions/schemaArray" } 81 | ], 82 | "default": {} 83 | }, 84 | "maxItems": { "$ref": "#/definitions/positiveInteger" }, 85 | "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, 86 | "uniqueItems": { 87 | "type": "boolean", 88 | "default": false 89 | }, 90 | "maxProperties": { "$ref": "#/definitions/positiveInteger" }, 91 | "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, 92 | "required": { "$ref": "#/definitions/stringArray" }, 93 | "additionalProperties": { 94 | "anyOf": [ 95 | { "type": "boolean" }, 96 | { "$ref": "#" } 97 | ], 98 | "default": {} 99 | }, 100 | "definitions": { 101 | "type": "object", 102 | "additionalProperties": { "$ref": "#" }, 103 | "default": {} 104 | }, 105 | "properties": { 106 | "type": "object", 107 | "additionalProperties": { "$ref": "#" }, 108 | "default": {} 109 | }, 110 | "patternProperties": { 111 | "type": "object", 112 | "additionalProperties": { "$ref": "#" }, 113 | "default": {} 114 | }, 115 | "dependencies": { 116 | "type": "object", 117 | "additionalProperties": { 118 | "anyOf": [ 119 | { "$ref": "#" }, 120 | { "$ref": "#/definitions/stringArray" } 121 | ] 122 | } 123 | }, 124 | "enum": { 125 | "type": "array", 126 | "minItems": 1, 127 | "uniqueItems": true 128 | }, 129 | "type": { 130 | "anyOf": [ 131 | { "$ref": "#/definitions/simpleTypes" }, 132 | { 133 | "type": "array", 134 | "items": { "$ref": "#/definitions/simpleTypes" }, 135 | "minItems": 1, 136 | "uniqueItems": true 137 | } 138 | ] 139 | }, 140 | "allOf": { "$ref": "#/definitions/schemaArray" }, 141 | "anyOf": { "$ref": "#/definitions/schemaArray" }, 142 | "oneOf": { "$ref": "#/definitions/schemaArray" }, 143 | "not": { "$ref": "#" } 144 | }, 145 | "dependencies": { 146 | "exclusiveMaximum": [ "maximum" ], 147 | "exclusiveMinimum": [ "minimum" ] 148 | }, 149 | "default": {} 150 | } 151 | -------------------------------------------------------------------------------- /doc/schema/draft-07.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "http://json-schema.org/draft-07/schema#", 4 | "title": "Core schema meta-schema", 5 | "definitions": { 6 | "schemaArray": { 7 | "type": "array", 8 | "minItems": 1, 9 | "items": {"$ref": "#"} 10 | }, 11 | "nonNegativeInteger": { 12 | "type": "integer", 13 | "minimum": 0 14 | }, 15 | "nonNegativeIntegerDefault0": { 16 | "allOf": [{"$ref": "#/definitions/nonNegativeInteger"}, {"default": 0}] 17 | }, 18 | "simpleTypes": { 19 | "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] 20 | }, 21 | "stringArray": { 22 | "type": "array", 23 | "items": {"type": "string"}, 24 | "uniqueItems": true, 25 | "default": [] 26 | } 27 | }, 28 | "type": ["object", "boolean"], 29 | "properties": { 30 | "$id": { 31 | "type": "string", 32 | "format": "uri-reference" 33 | }, 34 | "$schema": { 35 | "type": "string", 36 | "format": "uri" 37 | }, 38 | "$ref": { 39 | "type": "string", 40 | "format": "uri-reference" 41 | }, 42 | "$comment": { 43 | "type": "string" 44 | }, 45 | "title": { 46 | "type": "string" 47 | }, 48 | "description": { 49 | "type": "string" 50 | }, 51 | "default": true, 52 | "readOnly": { 53 | "type": "boolean", 54 | "default": false 55 | }, 56 | "examples": { 57 | "type": "array", 58 | "items": true 59 | }, 60 | "multipleOf": { 61 | "type": "number", 62 | "exclusiveMinimum": 0 63 | }, 64 | "maximum": { 65 | "type": "number" 66 | }, 67 | "exclusiveMaximum": { 68 | "type": "number" 69 | }, 70 | "minimum": { 71 | "type": "number" 72 | }, 73 | "exclusiveMinimum": { 74 | "type": "number" 75 | }, 76 | "maxLength": {"$ref": "#/definitions/nonNegativeInteger"}, 77 | "minLength": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, 78 | "pattern": { 79 | "type": "string", 80 | "format": "regex" 81 | }, 82 | "additionalItems": {"$ref": "#"}, 83 | "items": { 84 | "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/schemaArray"}], 85 | "default": true 86 | }, 87 | "maxItems": {"$ref": "#/definitions/nonNegativeInteger"}, 88 | "minItems": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, 89 | "uniqueItems": { 90 | "type": "boolean", 91 | "default": false 92 | }, 93 | "contains": {"$ref": "#"}, 94 | "maxProperties": {"$ref": "#/definitions/nonNegativeInteger"}, 95 | "minProperties": {"$ref": "#/definitions/nonNegativeIntegerDefault0"}, 96 | "required": {"$ref": "#/definitions/stringArray"}, 97 | "additionalProperties": {"$ref": "#"}, 98 | "definitions": { 99 | "type": "object", 100 | "additionalProperties": {"$ref": "#"}, 101 | "default": {} 102 | }, 103 | "properties": { 104 | "type": "object", 105 | "additionalProperties": {"$ref": "#"}, 106 | "default": {} 107 | }, 108 | "patternProperties": { 109 | "type": "object", 110 | "additionalProperties": {"$ref": "#"}, 111 | "propertyNames": {"format": "regex"}, 112 | "default": {} 113 | }, 114 | "dependencies": { 115 | "type": "object", 116 | "additionalProperties": { 117 | "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/stringArray"}] 118 | } 119 | }, 120 | "propertyNames": {"$ref": "#"}, 121 | "const": true, 122 | "enum": { 123 | "type": "array", 124 | "items": true, 125 | "minItems": 1, 126 | "uniqueItems": true 127 | }, 128 | "type": { 129 | "anyOf": [ 130 | {"$ref": "#/definitions/simpleTypes"}, 131 | { 132 | "type": "array", 133 | "items": {"$ref": "#/definitions/simpleTypes"}, 134 | "minItems": 1, 135 | "uniqueItems": true 136 | } 137 | ] 138 | }, 139 | "format": {"type": "string"}, 140 | "contentMediaType": {"type": "string"}, 141 | "contentEncoding": {"type": "string"}, 142 | "if": {"$ref": "#"}, 143 | "then": {"$ref": "#"}, 144 | "else": {"$ref": "#"}, 145 | "allOf": {"$ref": "#/definitions/schemaArray"}, 146 | "anyOf": {"$ref": "#/definitions/schemaArray"}, 147 | "oneOf": {"$ref": "#/definitions/schemaArray"}, 148 | "not": {"$ref": "#"} 149 | }, 150 | "default": true 151 | } 152 | -------------------------------------------------------------------------------- /doc/screenshots/inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tristanpenman/valijson/4edda758546436462da479bb8c8514f8a95c35ad/doc/screenshots/inspector.png -------------------------------------------------------------------------------- /doc/screenshots/wasm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tristanpenman/valijson/4edda758546436462da479bb8c8514f8a95c35ad/doc/screenshots/wasm.png -------------------------------------------------------------------------------- /doc/specifications/draft-pbryan-zyp-json-ref-03.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Internet Engineering Task Force P. Bryan, Ed. 5 | Internet-Draft Salesforce.com 6 | Intended status: Informational K. Zyp 7 | Expires: March 20, 2013 SitePen (USA) 8 | September 16, 2012 9 | 10 | 11 | JSON Reference 12 | draft-pbryan-zyp-json-ref-03 13 | 14 | Abstract 15 | 16 | JSON Reference allows a JSON value to reference another value in a 17 | JSON document. 18 | 19 | Status of this Memo 20 | 21 | This Internet-Draft is submitted in full conformance with the 22 | provisions of BCP 78 and BCP 79. 23 | 24 | Internet-Drafts are working documents of the Internet Engineering 25 | Task Force (IETF). Note that other groups may also distribute 26 | working documents as Internet-Drafts. The list of current Internet- 27 | Drafts is at http://datatracker.ietf.org/drafts/current/. 28 | 29 | Internet-Drafts are draft documents valid for a maximum of six months 30 | and may be updated, replaced, or obsoleted by other documents at any 31 | time. It is inappropriate to use Internet-Drafts as reference 32 | material or to cite them other than as "work in progress." 33 | 34 | This Internet-Draft will expire on March 20, 2013. 35 | 36 | Copyright Notice 37 | 38 | Copyright (c) 2012 IETF Trust and the persons identified as the 39 | document authors. All rights reserved. 40 | 41 | This document is subject to BCP 78 and the IETF Trust's Legal 42 | Provisions Relating to IETF Documents 43 | (http://trustee.ietf.org/license-info) in effect on the date of 44 | publication of this document. Please review these documents 45 | carefully, as they describe your rights and restrictions with respect 46 | to this document. Code Components extracted from this document must 47 | include Simplified BSD License text as described in Section 4.e of 48 | the Trust Legal Provisions and are provided without warranty as 49 | described in the Simplified BSD License. 50 | 51 | 52 | 53 | 54 | 55 | Bryan & Zyp Expires March 20, 2013 [Page 1] 56 | 57 | Internet-Draft JSON Reference September 2012 58 | 59 | 60 | Table of Contents 61 | 62 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 63 | 2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . 3 64 | 3. Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 65 | 4. Resolution . . . . . . . . . . . . . . . . . . . . . . . . . . 3 66 | 5. Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 4 67 | 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 4 68 | 7. Security Considerations . . . . . . . . . . . . . . . . . . . . 4 69 | 8. Normative References . . . . . . . . . . . . . . . . . . . . . 4 70 | Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . . 4 71 | Appendix B. Examples . . . . . . . . . . . . . . . . . . . . . . . 5 72 | Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 5 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | Bryan & Zyp Expires March 20, 2013 [Page 2] 112 | 113 | Internet-Draft JSON Reference September 2012 114 | 115 | 116 | 1. Introduction 117 | 118 | This specification defines a JSON [RFC4627] structure which allows a 119 | JSON value to reference another value in a JSON document. This 120 | provides the basis for transclusion in JSON: the use of a target 121 | resource as an effective substitute for the reference. 122 | 123 | 124 | 2. Conventions 125 | 126 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 127 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 128 | document are to be interpreted as described in [RFC2119]. 129 | 130 | 131 | 3. Syntax 132 | 133 | A JSON Reference is a JSON object, which contains a member named 134 | "$ref", which has a JSON string value. Example: 135 | 136 | { "$ref": "http://example.com/example.json#/foo/bar" } 137 | 138 | If a JSON value does not have these characteristics, then it SHOULD 139 | NOT be interpreted as a JSON Reference. 140 | 141 | The "$ref" string value contains a URI [RFC3986], which identifies 142 | the location of the JSON value being referenced. It is an error 143 | condition if the string value does not conform to URI syntax rules. 144 | Any members other than "$ref" in a JSON Reference object SHALL be 145 | ignored. 146 | 147 | 148 | 4. Resolution 149 | 150 | Resolution of a JSON Reference object SHOULD yield the referenced 151 | JSON value. Implementations MAY choose to replace the reference with 152 | the referenced value. 153 | 154 | If the URI contained in the JSON Reference value is a relative URI, 155 | then the base URI resolution MUST be calculated according to 156 | [RFC3986], section 5.2. Resolution is performed relative to the 157 | referring document. 158 | 159 | If a URI contains a fragment identifier, then the fragment should be 160 | resolved per the fragment resolution mechansim of the referrant 161 | document. If the representation of the referrant document is JSON, 162 | then the fragment identifier SHOULD be interpreted as a 163 | [JSON-Pointer]. 164 | 165 | 166 | 167 | Bryan & Zyp Expires March 20, 2013 [Page 3] 168 | 169 | Internet-Draft JSON Reference September 2012 170 | 171 | 172 | 5. Error Handling 173 | 174 | In the event of an error condition, evaluation of the JSON Reference 175 | SHOULD fail to complete. 176 | 177 | 178 | 6. IANA Considerations 179 | 180 | This draft includes no request to IANA. 181 | 182 | 183 | 7. Security Considerations 184 | 185 | A JSON Reference is not guaranteed to resolve to a JSON value. 186 | Implementations of this specification SHOULD take appropriate 187 | precautions. 188 | 189 | Documents containing JSON References can be structured to resolve 190 | cyclically. Implementations SHOULD include appropriate checks to 191 | prevent such structures from resulting in infinite recursion or 192 | iteration. 193 | 194 | 195 | 8. Normative References 196 | 197 | [JSON-Pointer] 198 | Bryan, P., Zyp, K., and M. Nottingham, "JSON Pointer", 199 | draft-ietf-appsawg-json-pointer-04 (work in progress), 200 | September 2012. 201 | 202 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 203 | Requirement Levels", BCP 14, RFC 2119, March 1997. 204 | 205 | [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform 206 | Resource Identifier (URI): Generic Syntax", STD 66, 207 | RFC 3986, January 2005. 208 | 209 | [RFC4627] Crockford, D., "The application/json Media Type for 210 | JavaScript Object Notation (JSON)", RFC 4627, July 2006. 211 | 212 | 213 | Appendix A. Acknowledgements 214 | 215 | The following individuals contributed ideas, feedback and wording to 216 | this specification: 217 | 218 | Bob Aman, Francis Galiegue. 219 | 220 | 221 | 222 | 223 | Bryan & Zyp Expires March 20, 2013 [Page 4] 224 | 225 | Internet-Draft JSON Reference September 2012 226 | 227 | 228 | Appendix B. Examples 229 | 230 | TBD. 231 | 232 | 233 | Authors' Addresses 234 | 235 | Paul C. Bryan (editor) 236 | Salesforce.com 237 | 238 | Phone: +1 604 783 1481 239 | Email: pbryan@anode.ca 240 | 241 | 242 | Kris Zyp 243 | SitePen (USA) 244 | 245 | Phone: +1 650 968 8787 246 | Email: kris@sitepen.com 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | Bryan & Zyp Expires March 20, 2013 [Page 5] 280 | 281 | -------------------------------------------------------------------------------- /examples/array_iteration_basics.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Demonstrates iteration over an array, and how to use type check functions 5 | */ 6 | 7 | #include 8 | 9 | // jsoncpp 10 | #include 11 | #include 12 | 13 | // RapidJSON 14 | #include 15 | #include 16 | 17 | using std::cerr; 18 | using std::cout; 19 | using std::endl; 20 | using std::runtime_error; 21 | 22 | // The first example uses RapidJson to load a JSON document. If the document 23 | // contains an array, this function will print any array values that have a 24 | // valid string representation. 25 | void usingRapidJson(const char *filename) 26 | { 27 | using valijson::adapters::RapidJsonAdapter; 28 | 29 | rapidjson::Document document; 30 | if (!valijson::utils::loadDocument(filename, document)) { 31 | return; 32 | } 33 | 34 | const RapidJsonAdapter adapter(document); 35 | if (!adapter.isArray()) { 36 | cout << "Not an array." << endl; 37 | return; 38 | } 39 | 40 | cout << "Array values:" << endl; 41 | int index = 0; 42 | 43 | const RapidJsonAdapter::Array array = adapter.asArray(); 44 | for (auto &&item : array) { 45 | cout << " " << index++ << ": "; 46 | 47 | // maybeString is a loose type check 48 | if (item.maybeString()) { 49 | // If a value may be a string, we are allowed to get a string 50 | // representation of the value using asString 51 | cout << item.asString(); 52 | } 53 | 54 | cout << endl; 55 | } 56 | } 57 | 58 | // The second example uses JsonCpp to perform the same task, but unlike the 59 | // RapidJson example, we see how to use strict type checks and exception 60 | // handling. 61 | void usingJsonCpp(const char *filename) 62 | { 63 | using valijson::adapters::JsonCppAdapter; 64 | 65 | Json::Value value; 66 | if (!valijson::utils::loadDocument(filename, value)) { 67 | return; 68 | } 69 | 70 | const JsonCppAdapter adapter(value); 71 | if (!adapter.isArray()) { 72 | cout << "Not an array." << endl; 73 | return; 74 | } 75 | 76 | cout << "Array values:" << endl; 77 | int index = 0; 78 | 79 | // If a value is not an array, then calling getArray will cause a runtime 80 | // exception to be raised. 81 | const JsonCppAdapter::Array array = adapter.getArray(); 82 | for (auto &&item : array) { 83 | cout << " " << index++ << ": "; 84 | 85 | // isString is another strict type check. Valijson uses the convention 86 | // that strict type check functions are prefixed with 'is'. 87 | if (!item.isString()) { 88 | cout << "Not a string. "; 89 | } 90 | 91 | try { 92 | // Also by convention, functions prefixed with 'get' will raise a 93 | // runtime exception if the item is not of the correct type. 94 | cout << item.getString() << endl; 95 | } catch (const runtime_error &e) { 96 | cout << "Caught exception: " << e.what() << endl; 97 | } 98 | } 99 | } 100 | 101 | int main(int argc, char **argv) 102 | { 103 | if (argc != 2) { 104 | cerr << "Usage: " << endl; 105 | cerr << " " << argv[0] << " " << endl; 106 | return 1; 107 | } 108 | 109 | // Load the document using rapidjson 110 | cout << "-- Iteration using RapidJSON --" << endl; 111 | usingRapidJson(argv[1]); 112 | cout << endl; 113 | 114 | // Load the document using jsoncpp 115 | cout << "-- Iteration using jsoncpp --" << endl; 116 | usingJsonCpp(argv[1]); 117 | cout << endl; 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /examples/array_iteration_template_fn.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Demonstrates iteration over an array using template functions 5 | * 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | using std::cerr; 15 | using std::cout; 16 | using std::endl; 17 | 18 | template 19 | void iterateJsonArray(const AdapterType &adapter) 20 | { 21 | if (!adapter.isArray()) { 22 | cout << "Not an array." << endl; 23 | return; 24 | } 25 | 26 | cout << "Array values:" << endl; 27 | int index = 0; 28 | 29 | for (auto value : adapter.getArray()) { 30 | cout << " " << index++ << ": "; 31 | 32 | if (value.maybeString()) { 33 | cout << value.asString(); 34 | } 35 | 36 | cout << endl; 37 | } 38 | } 39 | 40 | void usingJsonCppWithTemplateFn(const char *filename) 41 | { 42 | Json::Value value; 43 | if (!valijson::utils::loadDocument(filename, value)) { 44 | return; 45 | } 46 | 47 | valijson::adapters::JsonCppAdapter adapter(value); 48 | iterateJsonArray(adapter); 49 | } 50 | 51 | int main(int argc, char **argv) 52 | { 53 | if (argc != 2) { 54 | cerr << "Usage: " << endl; 55 | cerr << " " << argv[0] << " " << endl; 56 | return 1; 57 | } 58 | 59 | // Load the document using jsoncpp and iterate over array using function template 60 | cout << "-- Array iteration using jsoncpp via template function --" << endl; 61 | usingJsonCppWithTemplateFn(argv[1]); 62 | cout << endl; 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /examples/boost_json_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std::string_literals; 10 | 11 | namespace json = boost::json; 12 | 13 | const auto schemaJson = R"({ 14 | "$schema": "http://json-schema.org/draft-04/schema#", 15 | "title": "Product", 16 | "description": "A product from Acme's catalog", 17 | "type": "object", 18 | "properties": { 19 | "id": { 20 | "description": "The unique identifier for a product", 21 | "type": "integer" 22 | }, 23 | "name": { 24 | "description": "Name of the product", 25 | "type": "string" 26 | }, 27 | "price": { 28 | "type": "number", 29 | "minimum": 0 30 | }, 31 | "tags": { 32 | "type": "array", 33 | "items": { "type": "string" }, 34 | "minItems": 1, 35 | "uniqueItems": true 36 | } 37 | }, 38 | "required": ["id", "name", "price" ] 39 | })"s; 40 | 41 | const auto targetJson = R"({ 42 | "id": 123, 43 | "name": "Test" 44 | })"s; 45 | 46 | int main() 47 | { 48 | boost::system::error_code ec; 49 | auto schemaDoc = json::parse(schemaJson, ec); 50 | if (ec) { 51 | std::cerr << "Error parsing schema json: " << ec.message() << std::endl; 52 | return 1; 53 | } 54 | 55 | auto obj = schemaDoc.as_object(); 56 | auto iter = obj.find("$schema"); 57 | if (iter == obj.cend()) { 58 | std::cerr << "Error finding key $schema" << std::endl; 59 | return 2; 60 | } 61 | 62 | iter = obj.find("$ref"); 63 | if (iter != obj.cend()) { 64 | std::cerr << "Invalid iterator for nonexistent key $ref" << std::endl; 65 | return 3; 66 | } 67 | 68 | valijson::Schema schema; 69 | valijson::SchemaParser schemaParser; 70 | 71 | // Won't compile because the necessary constructor has been deleted 72 | // valijson::adapters::BoostJsonAdapter schemaAdapter(obj); 73 | 74 | valijson::adapters::BoostJsonAdapter schemaAdapter(schemaDoc); 75 | std::cerr << "Populating schema..." << std::endl; 76 | schemaParser.populateSchema(schemaAdapter, schema); 77 | 78 | auto targetDoc = json::parse(targetJson, ec); 79 | if (ec) { 80 | std::cerr << "Error parsing target json: " << ec.message() << std::endl; 81 | return 1; 82 | } 83 | 84 | valijson::Validator validator; 85 | valijson::ValidationResults results; 86 | valijson::adapters::BoostJsonAdapter targetAdapter(targetDoc); 87 | if (validator.validate(schema, targetAdapter, &results)) { 88 | std::cerr << "Validation succeeded." << std::endl; 89 | return 0; 90 | } 91 | 92 | std::cerr << "Validation failed." << std::endl; 93 | valijson::ValidationResults::Error error; 94 | unsigned int errorNum = 1; 95 | while (results.popError(error)) { 96 | std::cerr << "Error #" << errorNum << std::endl; 97 | std::cerr << " "; 98 | for (const std::string &contextElement : error.context) { 99 | std::cerr << contextElement << " "; 100 | } 101 | std::cerr << std::endl; 102 | std::cerr << " - " << error.description << std::endl; 103 | ++errorNum; 104 | } 105 | 106 | return 1; 107 | } -------------------------------------------------------------------------------- /examples/check_schema.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Loads a schema then exits. Exit code will be 0 if the schema is 5 | * valid, and 1 otherwise. This example uses jsoncpp to parse the 6 | * schema document. 7 | */ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using std::cerr; 17 | using std::endl; 18 | 19 | using valijson::Schema; 20 | using valijson::SchemaParser; 21 | using valijson::adapters::JsonCppAdapter; 22 | 23 | int main(int argc, char *argv[]) 24 | { 25 | if (argc != 2) { 26 | cerr << "Usage: " << argv[0] << " " << endl; 27 | return 1; 28 | } 29 | 30 | // Load the document containing the schema 31 | Json::Value schemaDocument; 32 | if (!valijson::utils::loadDocument(argv[1], schemaDocument)) { 33 | cerr << "Failed to load schema document." << endl; 34 | return 1; 35 | } 36 | 37 | Schema schema; 38 | SchemaParser parser; 39 | JsonCppAdapter adapter(schemaDocument); 40 | try { 41 | parser.populateSchema(adapter, schema); 42 | } catch (std::exception &e) { 43 | cerr << "Failed to parse schema: " << e.what() << endl; 44 | return 1; 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /examples/custom_schema.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Demonstrates validation against a manually constructed schema. 5 | * 6 | * This example demonstrates the construction and composition of a Schema object 7 | * using manually created Constraint objects. The following Constraint classes 8 | * are used: 9 | * - EnumConstraint 10 | * - MaxLengthConstraint 11 | * - MinimumConstraint 12 | * - MinLengthConstraint 13 | * - PropertiesConstraint 14 | * - RequiredConstraint 15 | * - TypeConstraint 16 | * 17 | * The MinimumConstraint class provides support for the exclusiveMinimum and 18 | * minimum keywords in JSON Schema. And the PropertiesConstraint class provides 19 | * support for the properties, patternProperties, and additionalProperties 20 | * keywords. 21 | * 22 | * This is the JSON Schema representation of the Schema that will be created: 23 | * 24 | * { 25 | * "properties": { 26 | * "category": { 27 | * "enum": [ 28 | * "album", 29 | * "book", 30 | * "other", 31 | * "video" 32 | * ] 33 | * }, 34 | * "description": { 35 | * "type": "string" 36 | * }, 37 | * "price": { 38 | * "exclusiveMinimum": true, 39 | * "minimum": 0.0, 40 | * "type": "number" 41 | * }, 42 | * "title": { 43 | * "maxLength": 200, 44 | * "minLength": 1, 45 | * "type": "string" 46 | * } 47 | * }, 48 | * "required": [ 49 | * "category", 50 | * "price", 51 | * "title" 52 | * ], 53 | * "type": "object" 54 | * } 55 | * 56 | */ 57 | 58 | #include 59 | #include 60 | #include 61 | 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | 69 | using std::cerr; 70 | using std::endl; 71 | 72 | using valijson::Schema; 73 | using valijson::Subschema; 74 | using valijson::Validator; 75 | using valijson::ValidationResults; 76 | using valijson::adapters::RapidJsonAdapter; 77 | using valijson::adapters::RapidJsonFrozenValue; 78 | using valijson::constraints::EnumConstraint; 79 | using valijson::constraints::MaxLengthConstraint; 80 | using valijson::constraints::MinimumConstraint; 81 | using valijson::constraints::MinLengthConstraint; 82 | using valijson::constraints::PropertiesConstraint; 83 | using valijson::constraints::RequiredConstraint; 84 | using valijson::constraints::TypeConstraint; 85 | 86 | void addPropertiesConstraint(Schema &schema) 87 | { 88 | 89 | PropertiesConstraint propertiesConstraint; 90 | 91 | { 92 | // Prepare an enum constraint requires a document to be equal to at 93 | // least one of a set of possible values 94 | EnumConstraint constraint; 95 | constraint.addValue(RapidJsonFrozenValue("album")); 96 | constraint.addValue(RapidJsonFrozenValue("book")); 97 | constraint.addValue(RapidJsonFrozenValue("other")); 98 | constraint.addValue(RapidJsonFrozenValue("video")); 99 | 100 | // Create a subschema, owned by the root schema, with a constraint 101 | const Subschema *subschema = schema.createSubschema(); 102 | schema.addConstraintToSubschema(constraint, subschema); 103 | 104 | // Include subschema in properties constraint 105 | propertiesConstraint.addPropertySubschema("category", subschema); 106 | } 107 | 108 | { 109 | // Create a child schema for the 'description' property that requires 110 | // a string, but does not enforce any length constraints. 111 | const Subschema *subschema = schema.createSubschema(); 112 | TypeConstraint typeConstraint; 113 | typeConstraint.addNamedType(TypeConstraint::kString); 114 | schema.addConstraintToSubschema(typeConstraint, subschema); 115 | 116 | // Include subschema in properties constraint 117 | propertiesConstraint.addPropertySubschema("description", subschema); 118 | } 119 | 120 | { 121 | // Create a child schema for the 'price' property, that requires a 122 | // number with a value greater than zero. 123 | const Subschema *subschema = schema.createSubschema(); 124 | MinimumConstraint minimumConstraint; 125 | minimumConstraint.setMinimum(0.0); 126 | minimumConstraint.setExclusiveMinimum(true); 127 | schema.addConstraintToSubschema(minimumConstraint, subschema); 128 | TypeConstraint typeConstraint; 129 | typeConstraint.addNamedType(TypeConstraint::kNumber); 130 | schema.addConstraintToSubschema(typeConstraint, subschema); 131 | 132 | // Include subschema in properties constraint 133 | propertiesConstraint.addPropertySubschema("price", subschema); 134 | } 135 | 136 | { 137 | // Create a child schema for the 'title' property that requires a string 138 | // that is between 1 and 200 characters in length. 139 | const Subschema *subschema = schema.createSubschema(); 140 | MaxLengthConstraint maxLengthConstraint; 141 | maxLengthConstraint.setMaxLength(200); 142 | schema.addConstraintToSubschema(maxLengthConstraint, subschema); 143 | MinLengthConstraint minLengthConstraint; 144 | minLengthConstraint.setMinLength(0); 145 | schema.addConstraintToSubschema(minLengthConstraint, subschema); 146 | TypeConstraint typeConstraint; 147 | typeConstraint.addNamedType(TypeConstraint::kString); 148 | schema.addConstraintToSubschema(typeConstraint, subschema); 149 | 150 | // Include subschema in properties constraint 151 | propertiesConstraint.addPropertySubschema("title", subschema); 152 | } 153 | 154 | // Add a PropertiesConstraint to the root schema 155 | schema.addConstraint(propertiesConstraint); 156 | } 157 | 158 | void addRequiredConstraint(Schema &schema) 159 | { 160 | // Add a RequiredConstraint to the schema, specifying that the category, 161 | // price, and title properties must be present. 162 | RequiredConstraint constraint; 163 | constraint.addRequiredProperty("category"); 164 | constraint.addRequiredProperty("price"); 165 | constraint.addRequiredProperty("title"); 166 | schema.addConstraint(constraint); 167 | } 168 | 169 | void addTypeConstraint(Schema &schema) 170 | { 171 | // Add a TypeConstraint to the schema, specifying that the root of the 172 | // document must be an object. 173 | TypeConstraint typeConstraint; 174 | typeConstraint.addNamedType(TypeConstraint::kObject); 175 | schema.addConstraint(typeConstraint); 176 | } 177 | 178 | int main(int argc, char *argv[]) 179 | { 180 | if (argc != 2) { 181 | cerr << "Usage:" << endl; 182 | cerr << " ./custom_schema " << endl; 183 | cerr << endl; 184 | return 1; 185 | } 186 | 187 | // Load the document that is to be validated 188 | rapidjson::Document targetDocument; 189 | if (!valijson::utils::loadDocument(argv[1], targetDocument)) { 190 | cerr << "Failed to load target document." << endl; 191 | return 1; 192 | } 193 | 194 | // Populate a schema 195 | Schema schema; 196 | addPropertiesConstraint(schema); 197 | addRequiredConstraint(schema); 198 | addTypeConstraint(schema); 199 | 200 | // Perform validation 201 | Validator validator; 202 | ValidationResults results; 203 | RapidJsonAdapter targetDocumentAdapter(targetDocument); 204 | if (!validator.validate(schema, targetDocumentAdapter, &results)) { 205 | std::cerr << "Validation failed." << endl; 206 | ValidationResults::Error error; 207 | unsigned int errorNum = 1; 208 | while (results.popError(error)) { 209 | cerr << "Error #" << errorNum << std::endl; 210 | cerr << " "; 211 | for (const std::string &contextElement : error.context) { 212 | cerr << contextElement << " "; 213 | } 214 | cerr << endl; 215 | cerr << " - " << error.description << endl; 216 | ++errorNum; 217 | } 218 | return 1; 219 | } 220 | 221 | return 0; 222 | } 223 | -------------------------------------------------------------------------------- /examples/external_schema.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Demonstrates validation against a schema loaded from a file. 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using std::cerr; 19 | using std::endl; 20 | 21 | using valijson::Schema; 22 | using valijson::SchemaParser; 23 | using valijson::Validator; 24 | using valijson::ValidationResults; 25 | using valijson::adapters::RapidJsonAdapter; 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | if (argc != 3) { 30 | cerr << "Usage: " << argv[0] << " " << endl; 31 | return 1; 32 | } 33 | 34 | // Load the document containing the schema 35 | rapidjson::Document schemaDocument; 36 | if (!valijson::utils::loadDocument(argv[1], schemaDocument)) { 37 | cerr << "Failed to load schema document." << endl; 38 | return 1; 39 | } 40 | 41 | // Load the document that is to be validated 42 | rapidjson::Document targetDocument; 43 | if (!valijson::utils::loadDocument(argv[2], targetDocument)) { 44 | cerr << "Failed to load target document." << endl; 45 | return 1; 46 | } 47 | 48 | // Parse the json schema into an internal schema format 49 | Schema schema; 50 | SchemaParser parser; 51 | RapidJsonAdapter schemaDocumentAdapter(schemaDocument); 52 | try { 53 | parser.populateSchema(schemaDocumentAdapter, schema); 54 | } catch (std::exception &e) { 55 | cerr << "Failed to parse schema: " << e.what() << endl; 56 | return 1; 57 | } 58 | 59 | // Perform validation 60 | Validator validator(Validator::kStrongTypes); 61 | ValidationResults results; 62 | RapidJsonAdapter targetDocumentAdapter(targetDocument); 63 | if (!validator.validate(schema, targetDocumentAdapter, &results)) { 64 | std::cerr << "Validation failed." << endl; 65 | ValidationResults::Error error; 66 | unsigned int errorNum = 1; 67 | while (results.popError(error)) { 68 | 69 | std::string context; 70 | std::vector::iterator itr = error.context.begin(); 71 | for (; itr != error.context.end(); itr++) { 72 | context += *itr; 73 | } 74 | 75 | cerr << "Error #" << errorNum << std::endl 76 | << " context: " << context << endl 77 | << " desc: " << error.description << endl; 78 | ++errorNum; 79 | } 80 | return 1; 81 | } 82 | 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /examples/json_pointers.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Demonstrates how to resolve JSON pointers against the current document 5 | * 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using std::cerr; 16 | using std::cout; 17 | using std::endl; 18 | 19 | template 20 | std::string maybeResolveRef(const AdapterType &value, const AdapterType &root) 21 | { 22 | if (!value.isObject()) { 23 | // Not an object, therefore not a JSON reference 24 | return ""; 25 | } 26 | 27 | const auto &object = value.getObject(); 28 | const auto itr = object.find("$ref"); 29 | if (itr == object.end()) { 30 | // Object does not contain $ref property 31 | return ""; 32 | } 33 | 34 | const AdapterType maybeRef = itr->second; 35 | if (!maybeRef.isString()) { 36 | return "[$ref did not contain a string value]"; 37 | } 38 | 39 | // Attempt to extract a JSON pointer 40 | const std::string ref = maybeRef.getString(); 41 | const auto maybePointer = valijson::internal::json_reference::getJsonReferencePointer(ref); 42 | if (!maybePointer) { 43 | return "[$ref did not contain valid JSON pointer]"; 44 | } 45 | 46 | const auto refAdapter = valijson::internal::json_pointer::resolveJsonPointer(root, *maybePointer); 47 | if (!refAdapter.maybeString()) { 48 | return "[$ref did not point to a string value]"; 49 | } 50 | 51 | return refAdapter.asString(); 52 | } 53 | 54 | template 55 | void iterateJsonObject(const AdapterType &adapter) 56 | { 57 | if (!adapter.maybeObject()) { 58 | cout << "Not an object." << endl; 59 | return; 60 | } 61 | 62 | cout << "Object members:" << endl; 63 | 64 | // JSON objects are an unordered collection of key-value pairs, 65 | // so the members of the object may be printed in an order that is 66 | // different to that in the source JSON document. 67 | for (auto member : adapter.asObject()) { 68 | // The key is a std::string that can be accessed using 'first' 69 | cout << " " << member.first << ": "; 70 | 71 | // The value is just another Adapter, and can be accessed using 'second' 72 | const AdapterType &value = member.second; 73 | if (value.maybeString()) { 74 | cout << value.asString(); 75 | } else { 76 | cout << maybeResolveRef(value, adapter); 77 | } 78 | 79 | cout << endl; 80 | } 81 | } 82 | 83 | void usingJsonCppWithTemplateFn(const char *filename) 84 | { 85 | rapidjson::Document document; 86 | if (!valijson::utils::loadDocument(filename, document)) { 87 | return; 88 | } 89 | 90 | valijson::adapters::RapidJsonAdapter adapter(document); 91 | iterateJsonObject(adapter); 92 | } 93 | 94 | int main(int argc, char **argv) 95 | { 96 | if (argc != 2) { 97 | cerr << "Usage: " << endl; 98 | cerr << " " << argv[0] << " " << endl; 99 | return 1; 100 | } 101 | 102 | // Load the document using jsoncpp and iterate over array using function template 103 | cout << "-- Resolving JSON pointers using RapidJSON --" << endl; 104 | usingJsonCppWithTemplateFn(argv[1]); 105 | cout << endl; 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /examples/object_iteration.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Demonstrates iteration over the members of an object 5 | * 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | using std::cerr; 15 | using std::cout; 16 | using std::endl; 17 | 18 | template 19 | void iterateJsonObject(const AdapterType &adapter) 20 | { 21 | if (!adapter.maybeObject()) { 22 | cout << "Not an object." << endl; 23 | return; 24 | } 25 | 26 | cout << "Object members:" << endl; 27 | 28 | // JSON objects are an unordered collection of key-value pairs, 29 | // so the members of the object may be printed in an order that is 30 | // different to that in the source JSON document. 31 | for (auto member : adapter.asObject()) { 32 | // The key is a std::string that can be accessed using 'first' 33 | cout << " " << member.first << ": "; 34 | 35 | // The value is just another Adapter, and can be accessed using 'second' 36 | const AdapterType &value = member.second; 37 | if (value.maybeString()) { 38 | cout << value.asString(); 39 | } 40 | 41 | cout << endl; 42 | } 43 | } 44 | 45 | void usingJsonCppWithTemplateFn(const char *filename) 46 | { 47 | Json::Value value; 48 | if (!valijson::utils::loadDocument(filename, value)) { 49 | return; 50 | } 51 | 52 | valijson::adapters::JsonCppAdapter adapter(value); 53 | iterateJsonObject(adapter); 54 | } 55 | 56 | int main(int argc, char **argv) 57 | { 58 | if (argc != 2) { 59 | cerr << "Usage: " << endl; 60 | cerr << " " << argv[0] << " " << endl; 61 | return 1; 62 | } 63 | 64 | cout << "-- Object iteration using jsoncpp via template function --" << endl; 65 | usingJsonCppWithTemplateFn(argv[1]); 66 | cout << endl; 67 | 68 | return 0; 69 | } -------------------------------------------------------------------------------- /examples/picojson_format_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define PICOJSON_USE_INT64 5 | #include "picojson.h" 6 | 7 | #include "valijson/adapters/picojson_adapter.hpp" 8 | #include "valijson/validation_results.hpp" 9 | #include "valijson/schema_parser.hpp" 10 | #include "valijson/validator.hpp" 11 | 12 | constexpr auto schemaStr = R"JSON({ 13 | "additionalItems": false, 14 | "items": [ 15 | { 16 | "format": "date-time", 17 | "type": "string" 18 | }, 19 | { 20 | "format": "date-time", 21 | "type": "string" 22 | } 23 | ], 24 | "maxItems": 2, 25 | "minItems": 2, 26 | "type": "array" 27 | })JSON"; 28 | 29 | constexpr auto validStr = R"JSON([ 30 | "2023-07-18T14:46:22Z", 31 | "2023-07-18T14:46:22Z" 32 | ])JSON"; 33 | 34 | constexpr auto invalidStrs = R"JSON([ 35 | ["um 12", "um 12"], 36 | ["2023-07-18T14:46:22Z"], 37 | ["2023-07-18T14:46:22Z", "2023-07-18T14:46:22Z", "2023-07-18T14:46:22Z", "2023-07-18T14:46:22Z"] 38 | ])JSON"; 39 | 40 | picojson::value Parse(std::string serialized, picojson::value def) 41 | { 42 | picojson::value v; 43 | auto first = serialized.data(); 44 | auto last = first + serialized.size(); 45 | auto err = picojson::parse(v, first, last); 46 | 47 | if (!err.empty()) { 48 | return def; 49 | } 50 | 51 | return v; 52 | } 53 | 54 | int main(int argc, char **argv) 55 | { 56 | auto validatorSchema = std::make_shared(); 57 | { 58 | auto schemaJson = Parse(schemaStr, picojson::value{}); 59 | auto schemaAdapter = valijson::adapters::PicoJsonAdapter(schemaJson); 60 | valijson::SchemaParser parser; 61 | parser.populateSchema(schemaAdapter, *validatorSchema); 62 | std::cout << "Schema:" << std::endl << schemaStr << std::endl << std::endl;; 63 | } 64 | 65 | { 66 | // valid 67 | auto targetJson = Parse(validStr, picojson::value{}); 68 | auto targetAdapter = valijson::adapters::PicoJsonAdapter(targetJson); 69 | std::cout << "Valid Target:" << std::endl << validStr << std::endl << std::endl; 70 | 71 | valijson::ValidationResults results; 72 | auto validator = valijson::Validator(); 73 | auto isValid = validator.validate( 74 | *validatorSchema, 75 | targetAdapter, 76 | &results); 77 | 78 | std::cout << "Is valid: " << (isValid ? "YES" : "NO") << std::endl << std::endl;; 79 | } 80 | 81 | { 82 | // invalid 83 | auto targetJsonArray = Parse(invalidStrs, picojson::value{}); 84 | std::cout << "Invalid Targets:" << std::endl << invalidStrs << std::endl << std::endl; 85 | 86 | for (auto &&testCase : targetJsonArray.get()) { 87 | auto targetAdapter = valijson::adapters::PicoJsonAdapter(testCase); 88 | 89 | valijson::ValidationResults results; 90 | auto validator = valijson::Validator(); 91 | auto isValid = validator.validate( 92 | *validatorSchema, 93 | targetAdapter, 94 | &results); 95 | 96 | std::cout << "Is valid: " << (isValid ? "YES" : "NO") << std::endl << std::endl; 97 | 98 | valijson::ValidationResults::Error error; 99 | unsigned int errorNum = 1; 100 | while (results.popError(error)) { 101 | std::cerr << "Error #" << errorNum << std::endl; 102 | std::cerr << " "; 103 | for (const std::string &contextElement : error.context) { 104 | std::cerr << contextElement << " "; 105 | } 106 | std::cerr << std::endl; 107 | std::cerr << " - " << error.description << std::endl << std::endl; 108 | ++errorNum; 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /examples/remote_resolution_local_file.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Demonstrates resolution of remote JSON references using cURLpp 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | using std::cerr; 20 | using std::cout; 21 | using std::endl; 22 | 23 | using valijson::Schema; 24 | using valijson::SchemaParser; 25 | using valijson::Validator; 26 | using valijson::ValidationResults; 27 | using valijson::adapters::RapidJsonAdapter; 28 | 29 | const rapidjson::Document * fetchDocument(const std::string &uri) 30 | { 31 | cout << "Fetching " << uri << "..." << endl; 32 | rapidjson::Document *fetchedRoot = new rapidjson::Document(); 33 | if (!valijson::utils::loadDocument(uri.substr(0, 7) == "file://" ? uri.substr(7, uri.size()) : uri, *fetchedRoot)) { 34 | return nullptr; 35 | } 36 | 37 | return fetchedRoot; 38 | } 39 | 40 | void freeDocument(const rapidjson::Document *adapter) 41 | { 42 | delete adapter; 43 | } 44 | 45 | int main(int argc, char *argv[]) 46 | { 47 | if (argc != 3) { 48 | cerr << "Usage: " << argv[0] << " " << endl; 49 | return 1; 50 | } 51 | 52 | // Load the document containing the schema 53 | rapidjson::Document schemaDocument; 54 | if (!valijson::utils::loadDocument(argv[1], schemaDocument)) { 55 | cerr << "Failed to load schema document." << endl; 56 | return 1; 57 | } 58 | 59 | // Load the document that is to be validated 60 | rapidjson::Document targetDocument; 61 | if (!valijson::utils::loadDocument(argv[2], targetDocument)) { 62 | cerr << "Failed to load target document." << endl; 63 | return 1; 64 | } 65 | 66 | // Parse the json schema into an internal schema format 67 | Schema schema; 68 | SchemaParser parser; 69 | RapidJsonAdapter schemaDocumentAdapter(schemaDocument); 70 | try { 71 | parser.populateSchema(schemaDocumentAdapter, schema, fetchDocument, freeDocument); 72 | } catch (std::exception &e) { 73 | cerr << "Failed to parse schema: " << e.what() << endl; 74 | return 1; 75 | } 76 | 77 | // Perform validation 78 | Validator validator(Validator::kWeakTypes); 79 | ValidationResults results; 80 | RapidJsonAdapter targetDocumentAdapter(targetDocument); 81 | if (!validator.validate(schema, targetDocumentAdapter, &results)) { 82 | std::cerr << "Validation failed." << endl; 83 | ValidationResults::Error error; 84 | unsigned int errorNum = 1; 85 | while (results.popError(error)) { 86 | 87 | std::string context; 88 | std::vector::iterator itr = error.context.begin(); 89 | for (; itr != error.context.end(); itr++) { 90 | context += *itr; 91 | } 92 | 93 | cerr << "Error #" << errorNum << std::endl 94 | << " context: " << context << endl 95 | << " desc: " << error.description << endl; 96 | ++errorNum; 97 | } 98 | return 1; 99 | } 100 | 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /examples/remote_resolution_url.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * 4 | * @brief Demonstrates resolution of remote JSON references using cURLpp 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using std::cerr; 23 | using std::cout; 24 | using std::endl; 25 | 26 | using valijson::Schema; 27 | using valijson::SchemaParser; 28 | using valijson::Validator; 29 | using valijson::ValidationResults; 30 | using valijson::adapters::RapidJsonAdapter; 31 | 32 | const rapidjson::Document * fetchDocument(const std::string &uri) 33 | { 34 | cout << "Fetching " << uri << "..." << endl; 35 | curlpp::Cleanup myCleanup; 36 | std::ostringstream os; 37 | os << curlpp::options::Url(uri); 38 | rapidjson::Document *fetchedRoot = new rapidjson::Document(); 39 | fetchedRoot->template Parse<0>(os.str().c_str()); 40 | return fetchedRoot; 41 | } 42 | 43 | void freeDocument(const rapidjson::Document *adapter) 44 | { 45 | delete adapter; 46 | } 47 | 48 | int main(int argc, char *argv[]) 49 | { 50 | if (argc != 3) { 51 | cerr << "Usage: " << argv[0] << " " << endl; 52 | return 1; 53 | } 54 | 55 | // Load the document containing the schema 56 | rapidjson::Document schemaDocument; 57 | if (!valijson::utils::loadDocument(argv[1], schemaDocument)) { 58 | cerr << "Failed to load schema document." << endl; 59 | return 1; 60 | } 61 | 62 | // Load the document that is to be validated 63 | rapidjson::Document targetDocument; 64 | if (!valijson::utils::loadDocument(argv[2], targetDocument)) { 65 | cerr << "Failed to load target document." << endl; 66 | return 1; 67 | } 68 | 69 | // Parse the json schema into an internal schema format 70 | Schema schema; 71 | SchemaParser parser; 72 | RapidJsonAdapter schemaDocumentAdapter(schemaDocument); 73 | try { 74 | parser.populateSchema(schemaDocumentAdapter, schema, fetchDocument, freeDocument); 75 | } catch (std::exception &e) { 76 | cerr << "Failed to parse schema: " << e.what() << endl; 77 | return 1; 78 | } 79 | 80 | // Perform validation 81 | Validator validator(Validator::kWeakTypes); 82 | ValidationResults results; 83 | RapidJsonAdapter targetDocumentAdapter(targetDocument); 84 | if (!validator.validate(schema, targetDocumentAdapter, &results)) { 85 | std::cerr << "Validation failed." << endl; 86 | ValidationResults::Error error; 87 | unsigned int errorNum = 1; 88 | while (results.popError(error)) { 89 | 90 | std::string context; 91 | std::vector::iterator itr = error.context.begin(); 92 | for (; itr != error.context.end(); itr++) { 93 | context += *itr; 94 | } 95 | 96 | cerr << "Error #" << errorNum << std::endl 97 | << " context: " << context << endl 98 | << " desc: " << error.description << endl; 99 | ++errorNum; 100 | } 101 | return 1; 102 | } 103 | 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /examples/valijson_nlohmann_bundled_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "valijson_nlohmann_bundled.hpp" 4 | 5 | using namespace std; 6 | using namespace valijson; 7 | using namespace valijson::adapters; 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | if (argc != 3) { 12 | cerr << "Usage: " << argv[0] << " " << endl; 13 | return 1; 14 | } 15 | 16 | // Load the document containing the schema 17 | nlohmann::json schemaDocument; 18 | if (!valijson::utils::loadDocument(argv[1], schemaDocument)) { 19 | cerr << "Failed to load schema document." << endl; 20 | return 1; 21 | } 22 | 23 | // Load the document that is to be validated 24 | nlohmann::json targetDocument; 25 | if (!valijson::utils::loadDocument(argv[2], targetDocument)) { 26 | cerr << "Failed to load target document." << endl; 27 | return 1; 28 | } 29 | 30 | // Parse the json schema into an internal schema format 31 | Schema schema; 32 | SchemaParser parser; 33 | NlohmannJsonAdapter schemaDocumentAdapter(schemaDocument); 34 | try { 35 | parser.populateSchema(schemaDocumentAdapter, schema); 36 | } catch (std::exception &e) { 37 | cerr << "Failed to parse schema: " << e.what() << endl; 38 | return 1; 39 | } 40 | 41 | // Perform validation 42 | Validator validator(Validator::kStrongTypes); 43 | ValidationResults results; 44 | NlohmannJsonAdapter targetDocumentAdapter(targetDocument); 45 | if (!validator.validate(schema, targetDocumentAdapter, &results)) { 46 | std::cerr << "Validation failed." << endl; 47 | ValidationResults::Error error; 48 | unsigned int errorNum = 1; 49 | while (results.popError(error)) { 50 | 51 | std::string context; 52 | std::vector::iterator itr = error.context.begin(); 53 | for (; itr != error.context.end(); itr++) { 54 | context += *itr; 55 | } 56 | 57 | cerr << "Error #" << errorNum << std::endl 58 | << " context: " << context << endl 59 | << " desc: " << error.description << endl; 60 | ++errorNum; 61 | } 62 | return 1; 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /include/valijson/constraint_builder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace valijson { 4 | 5 | namespace adapters { 6 | class Adapter; 7 | } 8 | 9 | namespace constraints { 10 | struct Constraint; 11 | } 12 | 13 | class ConstraintBuilder 14 | { 15 | public: 16 | virtual ~ConstraintBuilder() = default; 17 | 18 | virtual constraints::Constraint * make(const adapters::Adapter &) const = 0; 19 | }; 20 | 21 | } // namespace valijson 22 | -------------------------------------------------------------------------------- /include/valijson/constraints/basic_constraint.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace valijson { 9 | namespace constraints { 10 | 11 | /** 12 | * @brief Template class that implements the accept() and clone() functions of the Constraint interface. 13 | * 14 | * @tparam ConstraintType name of the concrete constraint type, which must provide a copy constructor. 15 | */ 16 | template 17 | struct BasicConstraint: Constraint 18 | { 19 | typedef internal::CustomAllocator Allocator; 20 | 21 | typedef std::basic_string, internal::CustomAllocator> String; 22 | 23 | BasicConstraint() 24 | : m_allocator() { } 25 | 26 | BasicConstraint(Allocator::CustomAlloc allocFn, Allocator::CustomFree freeFn) 27 | : m_allocator(allocFn, freeFn) { } 28 | 29 | BasicConstraint(const BasicConstraint &other) 30 | : m_allocator(other.m_allocator) { } 31 | 32 | ~BasicConstraint() override = default; 33 | 34 | bool accept(ConstraintVisitor &visitor) const override 35 | { 36 | return visitor.visit(*static_cast(this)); 37 | } 38 | 39 | OwningPointer clone(CustomAlloc allocFn, CustomFree freeFn) const override 40 | { 41 | // smart pointer to automatically free raw memory on exception 42 | typedef std::unique_ptr RawOwningPointer; 43 | auto ptr = RawOwningPointer(static_cast(allocFn(sizeof(ConstraintType))), freeFn); 44 | if (!ptr) { 45 | throwRuntimeError("Failed to allocate memory for cloned constraint"); 46 | } 47 | 48 | // constructor might throw but the memory will be taken care of anyways 49 | (void)new (ptr.get()) ConstraintType(*static_cast(this)); 50 | 51 | // implicitly convert to smart pointer that will also destroy object instance 52 | return ptr; 53 | } 54 | 55 | protected: 56 | 57 | Allocator m_allocator; 58 | }; 59 | 60 | } // namespace constraints 61 | } // namespace valijson 62 | -------------------------------------------------------------------------------- /include/valijson/constraints/constraint.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace valijson { 7 | namespace constraints { 8 | 9 | class ConstraintVisitor; 10 | 11 | /** 12 | * @brief Interface that must be implemented by concrete constraint types. 13 | * 14 | * @todo Consider using something like the boost::cloneable concept here. 15 | */ 16 | struct Constraint 17 | { 18 | /// Typedef for custom new-/malloc-like function 19 | typedef void * (*CustomAlloc)(size_t size); 20 | 21 | /// Typedef for custom free-like function 22 | typedef void (*CustomFree)(void *); 23 | 24 | /// Deleter type to be used with std::unique_ptr / std::shared_ptr 25 | /// @tparam T Const or non-const type (same as the one used in unique_ptr/shared_ptr) 26 | template 27 | struct CustomDeleter 28 | { 29 | CustomDeleter(CustomFree freeFn) 30 | : m_freeFn(freeFn) { } 31 | 32 | void operator()(T *ptr) const 33 | { 34 | auto *nonconst = const_cast::type *>(ptr); 35 | nonconst->~T(); 36 | m_freeFn(nonconst); 37 | } 38 | 39 | private: 40 | CustomFree m_freeFn; 41 | }; 42 | 43 | /// Exclusive-ownership pointer to automatically handle deallocation 44 | typedef std::unique_ptr> OwningPointer; 45 | 46 | /** 47 | * @brief Virtual destructor. 48 | */ 49 | virtual ~Constraint() = default; 50 | 51 | /** 52 | * @brief Perform an action on the constraint using the visitor pattern. 53 | * 54 | * Note that Constraints cannot be modified by visitors. 55 | * 56 | * @param visitor Reference to a ConstraintVisitor object. 57 | * 58 | * @returns the boolean value returned by one of the visitor's visit 59 | * functions. 60 | */ 61 | virtual bool accept(ConstraintVisitor &visitor) const = 0; 62 | 63 | /** 64 | * @brief Make a copy of a constraint. 65 | * 66 | * Note that this should be a deep copy of the constraint. 67 | * 68 | * @returns an owning-pointer to the new constraint. 69 | */ 70 | virtual OwningPointer clone(CustomAlloc, CustomFree) const = 0; 71 | 72 | }; 73 | 74 | } // namespace constraints 75 | } // namespace valijson 76 | -------------------------------------------------------------------------------- /include/valijson/constraints/constraint_visitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace valijson { 4 | namespace constraints { 5 | 6 | class AllOfConstraint; 7 | class AnyOfConstraint; 8 | class ConditionalConstraint; 9 | class ConstConstraint; 10 | class ContainsConstraint; 11 | class DependenciesConstraint; 12 | class EnumConstraint; 13 | class FormatConstraint; 14 | class LinearItemsConstraint; 15 | class MaxItemsConstraint; 16 | class MaximumConstraint; 17 | class MaxLengthConstraint; 18 | class MaxPropertiesConstraint; 19 | class MinItemsConstraint; 20 | class MinimumConstraint; 21 | class MinLengthConstraint; 22 | class MinPropertiesConstraint; 23 | class MultipleOfDoubleConstraint; 24 | class MultipleOfIntConstraint; 25 | class NotConstraint; 26 | class OneOfConstraint; 27 | class PatternConstraint; 28 | class PolyConstraint; 29 | class PropertiesConstraint; 30 | class PropertyNamesConstraint; 31 | class RequiredConstraint; 32 | class SingularItemsConstraint; 33 | class TypeConstraint; 34 | class UniqueItemsConstraint; 35 | 36 | /// Interface to allow usage of the visitor pattern with Constraints 37 | class ConstraintVisitor 38 | { 39 | protected: 40 | virtual ~ConstraintVisitor() = default; 41 | 42 | // Shorten type names for derived classes outside of this namespace 43 | typedef constraints::AllOfConstraint AllOfConstraint; 44 | typedef constraints::AnyOfConstraint AnyOfConstraint; 45 | typedef constraints::ConditionalConstraint ConditionalConstraint; 46 | typedef constraints::ConstConstraint ConstConstraint; 47 | typedef constraints::ContainsConstraint ContainsConstraint; 48 | typedef constraints::DependenciesConstraint DependenciesConstraint; 49 | typedef constraints::EnumConstraint EnumConstraint; 50 | typedef constraints::FormatConstraint FormatConstraint; 51 | typedef constraints::LinearItemsConstraint LinearItemsConstraint; 52 | typedef constraints::MaximumConstraint MaximumConstraint; 53 | typedef constraints::MaxItemsConstraint MaxItemsConstraint; 54 | typedef constraints::MaxLengthConstraint MaxLengthConstraint; 55 | typedef constraints::MaxPropertiesConstraint MaxPropertiesConstraint; 56 | typedef constraints::MinimumConstraint MinimumConstraint; 57 | typedef constraints::MinItemsConstraint MinItemsConstraint; 58 | typedef constraints::MinLengthConstraint MinLengthConstraint; 59 | typedef constraints::MinPropertiesConstraint MinPropertiesConstraint; 60 | typedef constraints::MultipleOfDoubleConstraint MultipleOfDoubleConstraint; 61 | typedef constraints::MultipleOfIntConstraint MultipleOfIntConstraint; 62 | typedef constraints::NotConstraint NotConstraint; 63 | typedef constraints::OneOfConstraint OneOfConstraint; 64 | typedef constraints::PatternConstraint PatternConstraint; 65 | typedef constraints::PolyConstraint PolyConstraint; 66 | typedef constraints::PropertiesConstraint PropertiesConstraint; 67 | typedef constraints::PropertyNamesConstraint PropertyNamesConstraint; 68 | typedef constraints::RequiredConstraint RequiredConstraint; 69 | typedef constraints::SingularItemsConstraint SingularItemsConstraint; 70 | typedef constraints::TypeConstraint TypeConstraint; 71 | typedef constraints::UniqueItemsConstraint UniqueItemsConstraint; 72 | 73 | public: 74 | 75 | virtual bool visit(const AllOfConstraint &) = 0; 76 | virtual bool visit(const AnyOfConstraint &) = 0; 77 | virtual bool visit(const ConditionalConstraint &) = 0; 78 | virtual bool visit(const ConstConstraint &) = 0; 79 | virtual bool visit(const ContainsConstraint &) = 0; 80 | virtual bool visit(const DependenciesConstraint &) = 0; 81 | virtual bool visit(const EnumConstraint &) = 0; 82 | virtual bool visit(const FormatConstraint &) = 0; 83 | virtual bool visit(const LinearItemsConstraint &) = 0; 84 | virtual bool visit(const MaximumConstraint &) = 0; 85 | virtual bool visit(const MaxItemsConstraint &) = 0; 86 | virtual bool visit(const MaxLengthConstraint &) = 0; 87 | virtual bool visit(const MaxPropertiesConstraint &) = 0; 88 | virtual bool visit(const MinimumConstraint &) = 0; 89 | virtual bool visit(const MinItemsConstraint &) = 0; 90 | virtual bool visit(const MinLengthConstraint &) = 0; 91 | virtual bool visit(const MinPropertiesConstraint &) = 0; 92 | virtual bool visit(const MultipleOfDoubleConstraint &) = 0; 93 | virtual bool visit(const MultipleOfIntConstraint &) = 0; 94 | virtual bool visit(const NotConstraint &) = 0; 95 | virtual bool visit(const OneOfConstraint &) = 0; 96 | virtual bool visit(const PatternConstraint &) = 0; 97 | virtual bool visit(const PolyConstraint &) = 0; 98 | virtual bool visit(const PropertiesConstraint &) = 0; 99 | virtual bool visit(const PropertyNamesConstraint &) = 0; 100 | virtual bool visit(const RequiredConstraint &) = 0; 101 | virtual bool visit(const SingularItemsConstraint &) = 0; 102 | virtual bool visit(const TypeConstraint &) = 0; 103 | virtual bool visit(const UniqueItemsConstraint &) = 0; 104 | }; 105 | 106 | } // namespace constraints 107 | } // namespace valijson 108 | -------------------------------------------------------------------------------- /include/valijson/exceptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace valijson { 7 | #if defined(_MSC_VER) && _MSC_VER == 1800 8 | #define VALIJSON_NORETURN __declspec(noreturn) 9 | #else 10 | #define VALIJSON_NORETURN [[noreturn]] 11 | #endif 12 | 13 | #if VALIJSON_USE_EXCEPTIONS 14 | #include 15 | 16 | VALIJSON_NORETURN inline void throwRuntimeError(const std::string& msg) { 17 | throw std::runtime_error(msg); 18 | } 19 | 20 | VALIJSON_NORETURN inline void throwLogicError(const std::string& msg) { 21 | throw std::logic_error(msg); 22 | } 23 | #else 24 | VALIJSON_NORETURN inline void throwRuntimeError(const std::string& msg) { 25 | std::cerr << msg << std::endl; 26 | abort(); 27 | } 28 | VALIJSON_NORETURN inline void throwLogicError(const std::string& msg) { 29 | std::cerr << msg << std::endl; 30 | abort(); 31 | } 32 | 33 | #endif 34 | 35 | VALIJSON_NORETURN inline void throwNotSupported() { 36 | throwRuntimeError("Not supported"); 37 | } 38 | 39 | } // namespace valijson 40 | -------------------------------------------------------------------------------- /include/valijson/internal/custom_allocator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace valijson { 6 | namespace internal { 7 | 8 | template 9 | class CustomAllocator 10 | { 11 | public: 12 | /// Typedef for custom new-/malloc-like function 13 | typedef void * (*CustomAlloc)(size_t size); 14 | 15 | /// Typedef for custom free-like function 16 | typedef void (*CustomFree)(void *); 17 | 18 | // Standard allocator typedefs 19 | typedef T value_type; 20 | typedef T* pointer; 21 | typedef const T* const_pointer; 22 | typedef T& reference; 23 | typedef const T& const_reference; 24 | typedef std::size_t size_type; 25 | typedef std::ptrdiff_t difference_type; 26 | 27 | template 28 | struct rebind 29 | { 30 | typedef CustomAllocator other; 31 | }; 32 | 33 | CustomAllocator() 34 | : m_allocFn([](size_t size) { return ::operator new(size, std::nothrow); }), 35 | m_freeFn(::operator delete) { } 36 | 37 | CustomAllocator(CustomAlloc allocFn, CustomFree freeFn) 38 | : m_allocFn(allocFn), 39 | m_freeFn(freeFn) { } 40 | 41 | CustomAllocator(const CustomAllocator &other) 42 | : m_allocFn(other.m_allocFn), 43 | m_freeFn(other.m_freeFn) { } 44 | 45 | template 46 | CustomAllocator(CustomAllocator const &other) 47 | : m_allocFn(other.m_allocFn), 48 | m_freeFn(other.m_freeFn) { } 49 | 50 | CustomAllocator & operator=(const CustomAllocator &other) 51 | { 52 | m_allocFn = other.m_allocFn; 53 | m_freeFn = other.m_freeFn; 54 | 55 | return *this; 56 | } 57 | 58 | pointer address(reference r) 59 | { 60 | return &r; 61 | } 62 | 63 | const_pointer address(const_reference r) 64 | { 65 | return &r; 66 | } 67 | 68 | pointer allocate(size_type cnt, const void * = nullptr) 69 | { 70 | return reinterpret_cast(m_allocFn(cnt * sizeof(T))); 71 | } 72 | 73 | void deallocate(pointer p, size_type) 74 | { 75 | m_freeFn(p); 76 | } 77 | 78 | size_type max_size() const 79 | { 80 | return std::numeric_limits::max() / sizeof(T); 81 | } 82 | 83 | void construct(pointer p, const T& t) 84 | { 85 | new(p) T(t); 86 | } 87 | 88 | void destroy(pointer p) 89 | { 90 | p->~T(); 91 | } 92 | 93 | bool operator==(const CustomAllocator &other) const 94 | { 95 | return other.m_allocFn == m_allocFn && other.m_freeFn == m_freeFn; 96 | } 97 | 98 | bool operator!=(const CustomAllocator &other) const 99 | { 100 | return !operator==(other); 101 | } 102 | 103 | CustomAlloc m_allocFn; 104 | 105 | CustomFree m_freeFn; 106 | }; 107 | 108 | } // end namespace internal 109 | } // end namespace valijson 110 | -------------------------------------------------------------------------------- /include/valijson/internal/debug.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace valijson { 6 | namespace internal { 7 | 8 | template 9 | std::string nodeTypeAsString(const AdapterType &node) { 10 | if (node.isArray()) { 11 | return "array"; 12 | } else if (node.isObject()) { 13 | return "object"; 14 | } else if (node.isString()) { 15 | return "string"; 16 | } else if (node.isNull()) { 17 | return "null"; 18 | } else if (node.isInteger()) { 19 | return "integer"; 20 | } else if (node.isDouble()) { 21 | return "double"; 22 | } else if (node.isBool()) { 23 | return "bool"; 24 | } 25 | 26 | return "unknown"; 27 | } 28 | 29 | } // end namespace internal 30 | } // end namespace valijson 31 | -------------------------------------------------------------------------------- /include/valijson/internal/frozen_value.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace valijson { 6 | namespace adapters { 7 | 8 | /** 9 | * @brief An interface that provides minimal access to a stored JSON value. 10 | * 11 | * The main reason that this interface exists is to support the 'enum' 12 | * constraint. Each Adapter type is expected to provide an implementation of 13 | * this interface. That class should be able to maintain its own copy of a 14 | * JSON value, independent of the original document. 15 | * 16 | * This interface currently provides just the clone and equalTo functions, but 17 | * could be expanded to include other functions declared in the Adapter 18 | * interface. 19 | * 20 | * @todo it would be nice to better integrate this with the Adapter interface 21 | */ 22 | class FrozenValue 23 | { 24 | public: 25 | 26 | /** 27 | * @brief Virtual destructor defined to ensure deletion via base-class 28 | * pointers is safe. 29 | */ 30 | virtual ~FrozenValue() { } 31 | 32 | /** 33 | * @brief Clone the stored value and return a pointer to a new FrozenValue 34 | * object containing the value. 35 | */ 36 | virtual FrozenValue *clone() const = 0; 37 | 38 | /** 39 | * @brief Return true if the stored value is equal to the value contained 40 | * by an Adapter instance. 41 | * 42 | * @param adapter Adapter to compare value against 43 | * @param strict Flag to use strict type comparison 44 | * 45 | * @returns true if values are equal, false otherwise 46 | */ 47 | virtual bool equalTo(const Adapter &adapter, bool strict) const = 0; 48 | 49 | }; 50 | 51 | } // namespace adapters 52 | } // namespace valijson 53 | -------------------------------------------------------------------------------- /include/valijson/internal/json_reference.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace valijson { 9 | namespace internal { 10 | namespace json_reference { 11 | 12 | /** 13 | * @brief Extract URI from JSON Reference relative to the current schema 14 | * 15 | * @param jsonRef JSON Reference to extract from 16 | * 17 | * @return Optional string containing URI 18 | */ 19 | inline opt::optional getJsonReferenceUri( 20 | const std::string &jsonRef) 21 | { 22 | const size_t ptrPos = jsonRef.find('#'); 23 | if (ptrPos == 0) { 24 | // The JSON Reference does not contain a URI, but might contain a 25 | // JSON Pointer that refers to the current document 26 | return opt::optional(); 27 | } else if (ptrPos != std::string::npos) { 28 | // The JSON Reference contains a URI and possibly a JSON Pointer 29 | return jsonRef.substr(0, ptrPos); 30 | } 31 | 32 | // The entire JSON Reference should be treated as a URI 33 | return jsonRef; 34 | } 35 | 36 | /** 37 | * @brief Extract JSON Pointer portion of a JSON Reference 38 | * 39 | * @param jsonRef JSON Reference to extract from 40 | * 41 | * @return Optional string containing JSON Pointer 42 | */ 43 | inline opt::optional getJsonReferencePointer( 44 | const std::string &jsonRef) 45 | { 46 | // Attempt to extract JSON Pointer if '#' character is present. Note 47 | // that a valid pointer would contain at least a leading forward 48 | // slash character. 49 | const size_t ptrPos = jsonRef.find('#'); 50 | if (ptrPos != std::string::npos) { 51 | return jsonRef.substr(ptrPos + 1); 52 | } 53 | 54 | return opt::optional(); 55 | } 56 | 57 | } // namespace json_reference 58 | } // namespace internal 59 | } // namespace valijson 60 | -------------------------------------------------------------------------------- /include/valijson/internal/optional.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if __cplusplus >= 201703 4 | // Visual C++ only supports __has_include in versions 14.12 and greater 5 | # if !defined(_MSC_VER) || _MSC_VER >= 1912 6 | # if __has_include() 7 | # include 8 | namespace opt = std; 9 | # endif 10 | # endif 11 | #else 12 | # include 13 | namespace opt = std::experimental; 14 | #endif 15 | -------------------------------------------------------------------------------- /include/valijson/internal/optional_bundled.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace opt = std::experimental; 4 | -------------------------------------------------------------------------------- /include/valijson/internal/regex.hpp: -------------------------------------------------------------------------------- 1 | #if defined(VALIJSON_USE_BOOST_REGEX) && VALIJSON_USE_BOOST_REGEX 2 | 3 | #include 4 | 5 | namespace valijson { 6 | namespace internal { 7 | using boost::regex; 8 | using boost::regex_match; 9 | using boost::regex_search; 10 | using boost::smatch; 11 | } // namespace internal 12 | } // namespace valijson 13 | 14 | #else 15 | 16 | #include 17 | 18 | namespace valijson { 19 | namespace internal { 20 | using std::regex; 21 | using std::regex_match; 22 | using std::regex_search; 23 | using std::smatch; 24 | } // namespace internal 25 | } // namespace valijson 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/valijson/internal/uri.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace valijson { 8 | namespace internal { 9 | namespace uri { 10 | 11 | /** 12 | * @brief Placeholder function to check whether a URI is absolute 13 | * 14 | * This function just checks for '://' 15 | */ 16 | inline bool isUriAbsolute(const std::string &documentUri) 17 | { 18 | static const char * placeholderMarker = "://"; 19 | 20 | return documentUri.find(placeholderMarker) != std::string::npos; 21 | } 22 | 23 | /** 24 | * @brief Placeholder function to check whether a URI is a URN 25 | * 26 | * This function validates that the URI matches the RFC 8141 spec 27 | */ 28 | inline bool isUrn(const std::string &documentUri) { 29 | static const internal::regex pattern( 30 | "^((urn)|(URN)):(?!urn:)([a-zA-Z0-9][a-zA-Z0-9-]{1,31})(:[-a-zA-Z0-9\\\\._~%!$&'()\\/*+,;=]+)+(\\?[-a-zA-Z0-9\\\\._~%!$&'()\\/*+,;:=]+){0,1}(#[-a-zA-Z0-9\\\\._~%!$&'()\\/*+,;:=]+){0,1}$"); 31 | 32 | return internal::regex_match(documentUri, pattern); 33 | } 34 | 35 | /** 36 | * Placeholder function to resolve a relative URI within a given scope 37 | */ 38 | inline std::string resolveRelativeUri( 39 | const std::string &resolutionScope, 40 | const std::string &relativeUri) 41 | { 42 | return resolutionScope + relativeUri; 43 | } 44 | 45 | } // namespace uri 46 | } // namespace internal 47 | } // namespace valijson 48 | -------------------------------------------------------------------------------- /include/valijson/schema.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | namespace valijson { 10 | 11 | /** 12 | * Represents the root of a JSON Schema 13 | * 14 | * The root is distinct from other sub-schemas because it is the canonical 15 | * starting point for validation of a document against a given a JSON Schema. 16 | */ 17 | class Schema: public Subschema 18 | { 19 | public: 20 | /** 21 | * @brief Construct a new Schema instance with no constraints 22 | */ 23 | Schema() 24 | : sharedEmptySubschema(newSubschema()) { } 25 | 26 | /** 27 | * @brief Construct a new Schema using custom memory management 28 | * functions 29 | * 30 | * @param allocFn malloc- or new-like function to allocate memory 31 | * within Schema, such as for Subschema instances 32 | * @param freeFn free-like function to free memory allocated with 33 | * the `customAlloc` function 34 | */ 35 | Schema(CustomAlloc allocFn, CustomFree freeFn) 36 | : Subschema(allocFn, freeFn), 37 | sharedEmptySubschema(newSubschema()) { } 38 | 39 | // Disable copy construction 40 | Schema(const Schema &) = delete; 41 | 42 | // Disable copy assignment 43 | Schema & operator=(const Schema &) = delete; 44 | 45 | /** 46 | * @brief Move construct a new Schema 47 | * 48 | * @param other Schema that is moved into the new Schema 49 | */ 50 | Schema(Schema &&other) 51 | : Subschema(std::move(other)), 52 | subschemaSet(std::move(other.subschemaSet)), 53 | sharedEmptySubschema(other.sharedEmptySubschema) 54 | { 55 | // Makes other invalid by setting sharedEmptySubschema to nullptr 56 | other.sharedEmptySubschema = nullptr; 57 | } 58 | 59 | /** 60 | * @brief Move assign a Schema 61 | * 62 | * @param other Schema that is move assigned to this Schema 63 | * @return Schema& 64 | */ 65 | Schema & operator=(Schema &&other) 66 | { 67 | // Calls the base class move assignment operator 68 | Subschema::operator=(std::move(other)); 69 | 70 | // Swaps all Schema members 71 | std::swap(subschemaSet, other.subschemaSet); 72 | std::swap(sharedEmptySubschema, other.sharedEmptySubschema); 73 | 74 | return *this; 75 | } 76 | 77 | /** 78 | * @brief Clean up and free all memory managed by the Schema 79 | * 80 | * Note that any Subschema pointers created and returned by this Schema 81 | * should be considered invalid. 82 | */ 83 | ~Schema() override 84 | { 85 | if(sharedEmptySubschema != nullptr) 86 | { 87 | sharedEmptySubschema->~Subschema(); 88 | m_freeFn(const_cast(sharedEmptySubschema)); 89 | sharedEmptySubschema = nullptr; 90 | } 91 | 92 | #if VALIJSON_USE_EXCEPTIONS 93 | try { 94 | #endif 95 | for (auto subschema : subschemaSet) { 96 | subschema->~Subschema(); 97 | m_freeFn(subschema); 98 | } 99 | #if VALIJSON_USE_EXCEPTIONS 100 | } catch (const std::exception &e) { 101 | fprintf(stderr, "Caught an exception while destroying Schema: %s", 102 | e.what()); 103 | } 104 | #endif 105 | } 106 | 107 | /** 108 | * @brief Copy a constraint to a specific sub-schema 109 | * 110 | * @param constraint reference to a constraint that will be copied into 111 | * the sub-schema 112 | * @param subschema pointer to the sub-schema that will own the copied 113 | * constraint 114 | * 115 | * @throws std::runtime_error if the sub-schema is not owned by this Schema 116 | * instance 117 | */ 118 | void addConstraintToSubschema(const Constraint &constraint, 119 | const Subschema *subschema) 120 | { 121 | // TODO: Check hierarchy for subschemas that do not belong... 122 | 123 | mutableSubschema(subschema)->addConstraint(constraint); 124 | } 125 | 126 | /** 127 | * @brief Create a new Subschema instance that is owned by this Schema 128 | * 129 | * @returns const pointer to the new Subschema instance 130 | */ 131 | const Subschema * createSubschema() 132 | { 133 | Subschema *subschema = newSubschema(); 134 | 135 | #if VALIJSON_USE_EXCEPTIONS 136 | try { 137 | #endif 138 | if (!subschemaSet.insert(subschema).second) { 139 | throwRuntimeError( 140 | "Failed to store pointer for new sub-schema"); 141 | } 142 | #if VALIJSON_USE_EXCEPTIONS 143 | } catch (...) { 144 | subschema->~Subschema(); 145 | m_freeFn(subschema); 146 | throw; 147 | } 148 | #endif 149 | return subschema; 150 | } 151 | 152 | /** 153 | * @brief Return a pointer to the shared empty schema 154 | */ 155 | const Subschema * emptySubschema() const 156 | { 157 | return sharedEmptySubschema; 158 | } 159 | 160 | /** 161 | * @brief Get a pointer to the root sub-schema of this Schema instance 162 | */ 163 | const Subschema * root() const 164 | { 165 | return this; 166 | } 167 | 168 | void setAlwaysInvalid(const Subschema *subschema, bool value) 169 | { 170 | mutableSubschema(subschema)->setAlwaysInvalid(value); 171 | } 172 | 173 | /** 174 | * @brief Update the description for one of the sub-schemas owned by this 175 | * Schema instance 176 | * 177 | * @param subschema sub-schema to update 178 | * @param description new description 179 | */ 180 | void setSubschemaDescription(const Subschema *subschema, 181 | const std::string &description) 182 | { 183 | mutableSubschema(subschema)->setDescription(description); 184 | } 185 | 186 | /** 187 | * @brief Update the ID for one of the sub-schemas owned by this Schema 188 | * instance 189 | * 190 | * @param subschema sub-schema to update 191 | * @param id new ID 192 | */ 193 | void setSubschemaId(const Subschema *subschema, const std::string &id) 194 | { 195 | mutableSubschema(subschema)->setId(id); 196 | } 197 | 198 | /** 199 | * @brief Update the title for one of the sub-schemas owned by this Schema 200 | * instance 201 | * 202 | * @param subschema sub-schema to update 203 | * @param title new title 204 | */ 205 | void setSubschemaTitle(const Subschema *subschema, const std::string &title) 206 | { 207 | mutableSubschema(subschema)->setTitle(title); 208 | } 209 | 210 | private: 211 | 212 | Subschema *newSubschema() 213 | { 214 | void *ptr = m_allocFn(sizeof(Subschema)); 215 | if (!ptr) { 216 | throwRuntimeError( 217 | "Failed to allocate memory for shared empty sub-schema"); 218 | } 219 | 220 | #if VALIJSON_USE_EXCEPTIONS 221 | try { 222 | #endif 223 | return new (ptr) Subschema(); 224 | #if VALIJSON_USE_EXCEPTIONS 225 | } catch (...) { 226 | m_freeFn(ptr); 227 | throw; 228 | } 229 | #endif 230 | } 231 | 232 | Subschema * mutableSubschema(const Subschema *subschema) 233 | { 234 | if (subschema == this) { 235 | return this; 236 | } 237 | 238 | if (subschema == sharedEmptySubschema) { 239 | throwRuntimeError( 240 | "Cannot modify the shared empty sub-schema"); 241 | } 242 | 243 | auto *noConst = const_cast(subschema); 244 | if (subschemaSet.find(noConst) == subschemaSet.end()) { 245 | throwRuntimeError( 246 | "Subschema pointer is not owned by this Schema instance"); 247 | } 248 | 249 | return noConst; 250 | } 251 | 252 | /// Set of Subschema instances owned by this schema 253 | std::set subschemaSet; 254 | 255 | /// Empty schema that can be reused by multiple constraints 256 | const Subschema *sharedEmptySubschema; 257 | }; 258 | 259 | } // namespace valijson 260 | -------------------------------------------------------------------------------- /include/valijson/schema_cache.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace valijson { 9 | 10 | typedef std::map SchemaCache; 11 | 12 | } // namespace valijson 13 | -------------------------------------------------------------------------------- /include/valijson/utils/boost_json_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace valijson { 10 | namespace utils { 11 | 12 | inline bool loadDocument(const std::string &path, boost::json::value &document) 13 | { 14 | // Load schema JSON from file 15 | std::string file; 16 | if (!loadFile(path, file)) { 17 | std::cerr << "Failed to load json from file '" << path << "'." 18 | << std::endl; 19 | return false; 20 | } 21 | 22 | // Parse schema 23 | #if VALIJSON_USE_EXCEPTIONS 24 | try { 25 | #endif 26 | boost::system::error_code errorCode; 27 | boost::json::string_view stringView{file}; 28 | document = boost::json::parse(stringView, errorCode); 29 | if (errorCode) { 30 | std::cerr << "Boost.JSON parsing error: " << errorCode.message(); 31 | return false; 32 | } 33 | #if VALIJSON_USE_EXCEPTIONS 34 | } catch (std::exception const & exception) { 35 | std::cerr << "Boost.JSON parsing exception: " << exception.what(); 36 | return false; 37 | } 38 | #endif 39 | 40 | return true; 41 | } 42 | 43 | } // namespace utils 44 | } // namespace valijson 45 | -------------------------------------------------------------------------------- /include/valijson/utils/file_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace valijson { 7 | namespace utils { 8 | 9 | /** 10 | * Load a file into a string 11 | * 12 | * @param path path to the file to be loaded 13 | * @param dest string into which file should be loaded 14 | * 15 | * @return true if loaded, false otherwise 16 | */ 17 | inline bool loadFile(const std::string &path, std::string &dest) 18 | { 19 | // Open file for reading 20 | std::ifstream file(path.c_str()); 21 | if (!file.is_open()) { 22 | return false; 23 | } 24 | 25 | // Allocate space for file contents 26 | file.seekg(0, std::ios::end); 27 | const std::streamoff offset = file.tellg(); 28 | if (offset < 0 || offset > std::numeric_limits::max()) { 29 | return false; 30 | } 31 | 32 | dest.clear(); 33 | dest.reserve(static_cast(offset)); 34 | 35 | // Assign file contents to destination string 36 | file.seekg(0, std::ios::beg); 37 | dest.assign(std::istreambuf_iterator(file), 38 | std::istreambuf_iterator()); 39 | 40 | return true; 41 | } 42 | 43 | } // namespace utils 44 | } // namespace valijson 45 | -------------------------------------------------------------------------------- /include/valijson/utils/json11_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace valijson { 10 | namespace utils { 11 | 12 | inline bool loadDocument(const std::string &path, json11::Json &document) 13 | { 14 | // Load schema JSON from file 15 | std::string file; 16 | if (!loadFile(path, file)) { 17 | std::cerr << "Failed to load json from file '" << path << "'." << std::endl; 18 | return false; 19 | } 20 | 21 | // Parse schema 22 | std::string err; 23 | document = json11::Json::parse(file, err); 24 | if (!err.empty()) { 25 | std::cerr << "json11 failed to parse the document:" << std::endl 26 | << "Parse error: " << err << std::endl; 27 | return false; 28 | } 29 | 30 | return true; 31 | } 32 | 33 | } // namespace utils 34 | } // namespace valijson 35 | -------------------------------------------------------------------------------- /include/valijson/utils/jsoncpp_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace valijson { 12 | namespace utils { 13 | 14 | inline bool loadDocument(const std::string &path, Json::Value &document) 15 | { 16 | // Load schema JSON from file 17 | std::string file; 18 | if (!loadFile(path, file)) { 19 | std::cerr << "Failed to load json from file '" << path << "'." << std::endl; 20 | return false; 21 | } 22 | 23 | const auto fileLength = static_cast(file.length()); 24 | Json::CharReaderBuilder builder; 25 | const std::unique_ptr reader(builder.newCharReader()); 26 | std::string err; 27 | if (!reader->parse(file.c_str(), file.c_str() + fileLength, &document, &err)) { 28 | std::cerr << "Jsoncpp parser failed to parse the document:" << std::endl << err; 29 | return false; 30 | } 31 | return true; 32 | } 33 | 34 | } // namespace utils 35 | } // namespace valijson 36 | -------------------------------------------------------------------------------- /include/valijson/utils/nlohmann_json_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace valijson { 10 | namespace utils { 11 | 12 | inline bool loadDocument(const std::string &path, nlohmann::json &document) 13 | { 14 | // Load schema JSON from file 15 | std::string file; 16 | if (!loadFile(path, file)) { 17 | std::cerr << "Failed to load json from file '" << path << "'." 18 | << std::endl; 19 | return false; 20 | } 21 | 22 | // Parse schema 23 | #if VALIJSON_USE_EXCEPTIONS 24 | try { 25 | document = nlohmann::json::parse(file); 26 | } catch (std::invalid_argument const& exception) { 27 | std::cerr << "nlohmann::json failed to parse the document\n" 28 | << "Parse error:" << exception.what() << "\n"; 29 | return false; 30 | } 31 | #else 32 | document = nlohmann::json::parse(file, nullptr, false); 33 | if (document.is_discarded()) { 34 | std::cerr << "nlohmann::json failed to parse the document."; 35 | return false; 36 | } 37 | #endif 38 | 39 | return true; 40 | } 41 | 42 | } // namespace utils 43 | } // namespace valijson 44 | -------------------------------------------------------------------------------- /include/valijson/utils/picojson_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef _MSC_VER 6 | #pragma warning(disable: 4706) 7 | #include 8 | #pragma warning(default: 4706) 9 | #else 10 | #include 11 | #endif 12 | 13 | #include 14 | 15 | namespace valijson { 16 | namespace utils { 17 | 18 | inline bool loadDocument(const std::string &path, picojson::value &document) 19 | { 20 | // Load schema JSON from file 21 | std::string file; 22 | if (!loadFile(path, file)) { 23 | std::cerr << "Failed to load json from file '" << path << "'." << std::endl; 24 | return false; 25 | } 26 | 27 | // Parse schema 28 | std::string err = picojson::parse(document, file); 29 | if (!err.empty()) { 30 | std::cerr << "PicoJson failed to parse the document:" << std::endl 31 | << "Parse error: " << err << std::endl; 32 | return false; 33 | } 34 | 35 | return true; 36 | } 37 | 38 | } // namespace utils 39 | } // namespace valijson 40 | -------------------------------------------------------------------------------- /include/valijson/utils/poco_json_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace valijson { 12 | namespace utils { 13 | 14 | inline bool loadDocument(const std::string &path, Poco::Dynamic::Var &document) 15 | { 16 | // Load schema JSON from file 17 | std::string file; 18 | if (!loadFile(path, file)) { 19 | std::cerr << "Failed to load json from file '" << path << "'." 20 | << std::endl; 21 | return false; 22 | } 23 | 24 | // Parse schema 25 | try { 26 | document = Poco::JSON::Parser().parse(file); 27 | } catch (Poco::Exception const& exception) { 28 | std::cerr << "Poco::JSON failed to parse the document\n" 29 | << "Parse error:" << exception.what() << "\n"; 30 | return false; 31 | } 32 | 33 | return true; 34 | } 35 | 36 | } // namespace utils 37 | } // namespace valijson 38 | -------------------------------------------------------------------------------- /include/valijson/utils/property_tree_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #if defined(__clang__) 10 | # pragma clang diagnostic push 11 | # pragma clang diagnostic ignored "-Wshadow" 12 | # include 13 | # pragma clang diagnostic pop 14 | #else 15 | # include 16 | #endif 17 | 18 | // Source locations were added in boost 1.73. 19 | #include 20 | #if (BOOST_VERSION > 107300) 21 | #include 22 | #endif 23 | 24 | #include 25 | #include 26 | 27 | #if !VALIJSON_USE_EXCEPTIONS 28 | 29 | namespace boost { 30 | 31 | // Boost requires used-defined exception throwers when exceptions are 32 | // disabled. 33 | // NOTE: BOOST_NORETURN attribute was added in 1.71. 34 | #if (BOOST_VERSION >= 107100) 35 | BOOST_NORETURN 36 | #endif 37 | void throw_exception(std::exception const & e ) { 38 | valijson::throwRuntimeError(e.what()); 39 | } 40 | 41 | // Source location override was added in 1.73. 42 | #if (BOOST_VERSION >= 107300) 43 | BOOST_NORETURN 44 | void throw_exception(std::exception const & e, boost::source_location const & loc ) { 45 | valijson::throwRuntimeError(e.what()); 46 | } 47 | #endif 48 | 49 | } // namespace boost 50 | 51 | #endif 52 | 53 | namespace valijson { 54 | namespace utils { 55 | 56 | inline bool loadDocument(const std::string &path, boost::property_tree::ptree &document) 57 | { 58 | #if !defined(BOOST_NO_EXCEPTIONS) 59 | try { 60 | #endif 61 | boost::property_tree::read_json(path, document); 62 | #if !defined(BOOST_NO_EXCEPTIONS) 63 | } catch (std::exception &e) { 64 | std::cerr << "Boost Property Tree JSON parser failed to parse the document:" << std::endl; 65 | std::cerr << e.what() << std::endl; 66 | return false; 67 | } 68 | #endif 69 | 70 | return true; 71 | } 72 | 73 | } // namespace utils 74 | } // namespace valijson 75 | -------------------------------------------------------------------------------- /include/valijson/utils/qtjson_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace valijson { 12 | namespace utils { 13 | 14 | inline bool loadDocument(const std::string &path, QJsonValue &root) 15 | { 16 | // Load schema JSON from file 17 | QFile file(QString::fromStdString(path)); 18 | if (!file.open(QFile::ReadOnly)) { 19 | std::cerr << "Failed to load json from file '" << path << "'." << std::endl; 20 | return false; 21 | } 22 | 23 | QByteArray data = file.readAll(); 24 | 25 | // Parse schema 26 | QJsonParseError parseError; 27 | QJsonDocument doc = QJsonDocument::fromJson(data); 28 | if (doc.isNull()) { 29 | std::cerr << "qt failed to parse the document:" << std::endl 30 | << parseError.errorString().toStdString() << std::endl; 31 | return false; 32 | } else if (doc.isObject()) { 33 | root = QJsonValue(doc.object()); 34 | } else if (doc.isArray()) { 35 | root = QJsonValue(doc.array()); 36 | } else if (doc.isEmpty()) { 37 | root = QJsonValue(); 38 | } 39 | 40 | return true; 41 | } 42 | 43 | } // namespace utils 44 | } // namespace valijson 45 | -------------------------------------------------------------------------------- /include/valijson/utils/rapidjson_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace valijson { 11 | namespace utils { 12 | 13 | template 14 | inline bool loadDocument(const std::string &path, rapidjson::GenericDocument &document) 15 | { 16 | // Load schema JSON from file 17 | std::string file; 18 | if (!loadFile(path, file)) { 19 | std::cerr << "Failed to load json from file '" << path << "'." << std::endl; 20 | return false; 21 | } 22 | 23 | // Parse schema 24 | #if VALIJSON_USE_EXCEPTIONS 25 | try { 26 | #endif 27 | document.template Parse(file.c_str()); 28 | if (document.HasParseError()) { 29 | std::cerr << "RapidJson failed to parse the document:" << std::endl; 30 | std::cerr << "Parse error: " << document.GetParseError() << std::endl; 31 | std::cerr << "Near: " << file.substr((std::max)(size_t(0), document.GetErrorOffset() - 20), 40) << std::endl; 32 | return false; 33 | } 34 | #if VALIJSON_USE_EXCEPTIONS 35 | } catch (const std::runtime_error &e) { 36 | std::cerr << "RapidJson failed to parse the document:" << std::endl; 37 | std::cerr << "Runtime error: " << e.what() << std::endl; 38 | return false; 39 | } 40 | #endif 41 | 42 | return true; 43 | } 44 | 45 | } // namespace utils 46 | } // namespace valijson 47 | -------------------------------------------------------------------------------- /include/valijson/utils/utf8_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | /* 10 | Basic UTF-8 manipulation routines, adapted from code that was released into 11 | the public domain by Jeff Bezanson. 12 | */ 13 | 14 | namespace valijson { 15 | namespace utils { 16 | 17 | /* is c the start of a utf8 sequence? */ 18 | inline bool isutf(char c) 19 | { 20 | return ((c & 0xC0) != 0x80); 21 | } 22 | 23 | /* number of characters */ 24 | inline uint64_t u8_strlen(const char *s) 25 | { 26 | uint64_t count = 0; 27 | 28 | while (*s) { 29 | unsigned char p = static_cast(*s); 30 | 31 | size_t seqLen = p < 0x80 ? 1 // 0xxxxxxx: 1-byte (ASCII) 32 | : p < 0xE0 ? 2 // 110xxxxx: 2-byte sequence 33 | : p < 0xF0 ? 3 // 1110xxxx: 3-byte sequence 34 | : p < 0xF8 ? 4 // 11110xxx: 4-byte sequence 35 | : 1; // treat as a single character 36 | 37 | for (size_t i = 1; i < seqLen; ++i) { 38 | if (s[i] == 0 || isutf(s[i])) { 39 | seqLen = i; 40 | break; 41 | } 42 | } 43 | 44 | s += seqLen; 45 | count++; 46 | } 47 | 48 | return count; 49 | } 50 | 51 | } // namespace utils 52 | } // namespace valijson 53 | -------------------------------------------------------------------------------- /include/valijson/utils/yaml_cpp_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace valijson { 12 | namespace utils { 13 | 14 | inline bool loadDocument(const std::string &path, YAML::Node &document) 15 | { 16 | try { 17 | document = YAML::LoadFile(path); 18 | return true; 19 | } catch (const YAML::BadFile &ex) { 20 | std::cerr << "Failed to load YAML from file '" << path << "'." << std::endl; 21 | return false; 22 | } catch (const YAML::ParserException &ex) { 23 | std::cout << "yaml-cpp failed to parse the document '" << ex.what() << std::endl; 24 | return false; 25 | } 26 | } 27 | 28 | } // namespace utils 29 | } // namespace valijson 30 | -------------------------------------------------------------------------------- /include/valijson/validation_results.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace valijson { 9 | 10 | /** 11 | * @brief Class that encapsulates the storage of validation errors. 12 | * 13 | * This class maintains an internal FIFO queue of errors that are reported 14 | * during validation. Errors are pushed on to the back of an internal 15 | * queue, and can retrieved by popping them from the front of the queue. 16 | */ 17 | class ValidationResults 18 | { 19 | public: 20 | 21 | /** 22 | * @brief Describes a validation error. 23 | * 24 | * This struct is used to pass around the context and description of a 25 | * validation error. 26 | */ 27 | struct Error 28 | { 29 | /// Path to the node that failed validation. 30 | std::vector context; 31 | 32 | /// A detailed description of the validation error. 33 | std::string description; 34 | }; 35 | 36 | /** 37 | * @brief Return begin iterator for results in the queue. 38 | */ 39 | std::deque::const_iterator begin() const 40 | { 41 | return m_errors.begin(); 42 | } 43 | 44 | /** 45 | * @brief Return end iterator for results in the queue. 46 | */ 47 | std::deque::const_iterator end() const 48 | { 49 | return m_errors.end(); 50 | } 51 | 52 | /** 53 | * @brief Return the number of errors in the queue. 54 | */ 55 | size_t numErrors() const 56 | { 57 | return m_errors.size(); 58 | } 59 | 60 | /** 61 | * @brief Copy an Error and push it on to the back of the queue. 62 | * 63 | * @param error Reference to an Error object to be copied. 64 | */ 65 | void pushError(const Error &error) 66 | { 67 | m_errors.push_back(error); 68 | } 69 | 70 | /** 71 | * @brief Push an error onto the back of the queue. 72 | * 73 | * @param context Context of the validation error. 74 | * @param description Description of the validation error. 75 | */ 76 | void 77 | pushError(const std::vector &context, const std::string &description) 78 | { 79 | m_errors.push_back({context, description}); 80 | } 81 | 82 | /** 83 | * @brief Pop an error from the front of the queue. 84 | * 85 | * @param error Reference to an Error object to populate. 86 | * 87 | * @returns true if an Error was popped, false otherwise. 88 | */ 89 | bool 90 | popError(Error &error) 91 | { 92 | if (m_errors.empty()) { 93 | return false; 94 | } 95 | 96 | error = m_errors.front(); 97 | m_errors.pop_front(); 98 | return true; 99 | } 100 | 101 | private: 102 | 103 | /// FIFO queue of validation errors that have been reported 104 | std::deque m_errors; 105 | }; 106 | 107 | } // namespace valijson 108 | -------------------------------------------------------------------------------- /include/valijson/validator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace valijson { 7 | 8 | class Schema; 9 | class ValidationResults; 10 | 11 | 12 | /** 13 | * @brief Class that provides validation functionality. 14 | * 15 | * @tparam RegexEngine regular expression engine used for pattern constraint validation. 16 | 17 | */ 18 | template 19 | class ValidatorT 20 | { 21 | public: 22 | enum TypeCheckingMode 23 | { 24 | kStrongTypes, 25 | kWeakTypes 26 | }; 27 | 28 | enum DateTimeMode 29 | { 30 | kStrictDateTime, 31 | kPermissiveDateTime 32 | }; 33 | 34 | /** 35 | * @brief Construct a Validator that uses strong type checking by default 36 | */ 37 | ValidatorT() 38 | : strictTypes(true) 39 | , strictDateTime(true) 40 | { } 41 | 42 | /** 43 | * @brief Construct a Validator using a specific type checking mode 44 | * 45 | * @param typeCheckingMode choice of strong or weak type checking 46 | */ 47 | ValidatorT(TypeCheckingMode typeCheckingMode, DateTimeMode dateTimeMode = kStrictDateTime) 48 | : strictTypes(typeCheckingMode == kStrongTypes) 49 | , strictDateTime(dateTimeMode == kStrictDateTime) 50 | { } 51 | 52 | /** 53 | * @brief Validate a JSON document and optionally return the results. 54 | * 55 | * When a ValidationResults object is provided via the \c results parameter, 56 | * validation will be performed against each constraint defined by the 57 | * schema, even if validation fails for some or all constraints. 58 | * 59 | * If a pointer to a ValidationResults instance is not provided, validation 60 | * will only continue for as long as the constraints are validated 61 | * successfully. 62 | * 63 | * @param schema The schema to validate against 64 | * @param target A rapidjson::Value to be validated 65 | * 66 | * @param results An optional pointer to a ValidationResults instance that 67 | * will be used to report validation errors 68 | * 69 | * @returns true if validation succeeds, false otherwise 70 | */ 71 | template 72 | bool validate(const Subschema &schema, const AdapterType &target, 73 | ValidationResults *results) 74 | { 75 | // Construct a ValidationVisitor to perform validation at the root level 76 | ValidationVisitor v( 77 | target, 78 | std::vector(1, ""), 79 | strictTypes, 80 | strictDateTime, 81 | results, 82 | regexesCache); 83 | 84 | return v.validateSchema(schema); 85 | } 86 | 87 | private: 88 | 89 | /// Flag indicating that strict type comparisons should be used 90 | bool strictTypes; 91 | 92 | /// Parse date/time values strictly, according to RFC-3999 93 | bool strictDateTime; 94 | 95 | /// Cached regex objects for pattern constraint. Key - pattern. 96 | std::unordered_map regexesCache; 97 | }; 98 | 99 | /** 100 | * @brief Struct that provides a default Regular Expression Engine using std::regex 101 | */ 102 | struct DefaultRegexEngine 103 | { 104 | DefaultRegexEngine(const std::string& pattern) 105 | : regex(pattern) { } 106 | 107 | static bool search(const std::string& s, const DefaultRegexEngine& r) 108 | { 109 | return internal::regex_search(s, r.regex); 110 | } 111 | 112 | private: 113 | internal::regex regex; 114 | }; 115 | 116 | using Validator = ValidatorT; 117 | 118 | } // namespace valijson 119 | -------------------------------------------------------------------------------- /inspector/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | cmake-build-*/ 3 | CMakeFiles/ 4 | -------------------------------------------------------------------------------- /inspector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | # Add folder where are supportive functions 4 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | # Include Qt basic functions 8 | include(QtCommon) 9 | 10 | # Basic information about project 11 | 12 | project(inspector VERSION 1.0) 13 | 14 | # Set PROJECT_VERSION_PATCH and PROJECT_VERSION_TWEAK to 0 if not present, needed by add_project_meta 15 | fix_project_version() 16 | 17 | # Set additional project information 18 | set(COPYRIGHT "Copyright (c) 2021 Tristan Penman. All rights reserved.") 19 | set(IDENTIFIER "com.tristanpenman.valijson.inspector") 20 | 21 | set(SOURCE_FILES 22 | src/highlighter.cpp 23 | src/main.cpp 24 | src/window.cpp 25 | ) 26 | 27 | include_directories(SYSTEM ../include) 28 | 29 | add_project_meta(META_FILES_TO_INCLUDE) 30 | 31 | find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Widgets REQUIRED) 32 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Widgets REQUIRED) 33 | 34 | add_definitions(-DVALIJSON_USE_EXCEPTIONS=1) 35 | 36 | add_executable(${PROJECT_NAME} ${OS_BUNDLE} # Expands to WIN32 or MACOS_BUNDLE depending on OS 37 | ${SOURCE_FILES} ${META_FILES_TO_INCLUDE} ${RESOURCE_FILES} 38 | ) 39 | 40 | target_link_libraries(${PROJECT_NAME} PRIVATE 41 | Qt${QT_VERSION_MAJOR}::Core 42 | Qt${QT_VERSION_MAJOR}::Widgets 43 | ) 44 | -------------------------------------------------------------------------------- /inspector/cmake/MacOSXBundleInfo.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${MACOSX_BUNDLE_EXECUTABLE_NAME} 9 | CFBundleGetInfoString 10 | ${MACOSX_BUNDLE_INFO_STRING} 11 | CFBundleIconFile 12 | ${MACOSX_BUNDLE_ICON_FILE} 13 | CFBundleIdentifier 14 | ${MACOSX_BUNDLE_GUI_IDENTIFIER} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleLongVersionString 18 | ${MACOSX_BUNDLE_LONG_VERSION_STRING} 19 | CFBundleName 20 | JSON Inspector 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | ${MACOSX_BUNDLE_SHORT_VERSION_STRING} 25 | CFBundleSignature 26 | ???? 27 | CFBundleVersion 28 | ${MACOSX_BUNDLE_BUNDLE_VERSION} 29 | CSResourcesFileMapped 30 | 31 | LSRequiresCarbon 32 | 33 | NSHumanReadableCopyright 34 | ${MACOSX_BUNDLE_COPYRIGHT} 35 | NSPrincipalClass 36 | NSApplication 37 | 38 | 39 | -------------------------------------------------------------------------------- /inspector/cmake/QtCommon.cmake: -------------------------------------------------------------------------------- 1 | macro(fix_project_version) 2 | if (NOT PROJECT_VERSION_PATCH) 3 | set(PROJECT_VERSION_PATCH 0) 4 | endif() 5 | 6 | if (NOT PROJECT_VERSION_TWEAK) 7 | set(PROJECT_VERSION_TWEAK 0) 8 | endif() 9 | endmacro() 10 | 11 | macro(add_project_meta FILES_TO_INCLUDE) 12 | if (NOT RESOURCE_FOLDER) 13 | set(RESOURCE_FOLDER res) 14 | endif() 15 | 16 | if (NOT ICON_NAME) 17 | set(ICON_NAME AppIcon) 18 | endif() 19 | 20 | if (APPLE) 21 | set(ICON_FILE ${RESOURCE_FOLDER}/${ICON_NAME}.icns) 22 | elseif (WIN32) 23 | set(ICON_FILE ${RESOURCE_FOLDER}/${ICON_NAME}.ico) 24 | endif() 25 | 26 | if (WIN32) 27 | configure_file("${PROJECT_SOURCE_DIR}/cmake/windows_metafile.rc.in" 28 | "windows_metafile.rc" 29 | ) 30 | set(RES_FILES "windows_metafile.rc") 31 | set(CMAKE_RC_COMPILER_INIT windres) 32 | ENABLE_LANGUAGE(RC) 33 | SET(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") 34 | endif() 35 | 36 | if (APPLE) 37 | set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) 38 | 39 | # Identify macOS bundle 40 | set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) 41 | set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) 42 | set(MACOSX_BUNDLE_LONG_VERSION_STRING ${PROJECT_VERSION}) 43 | set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") 44 | set(MACOSX_BUNDLE_COPYRIGHT ${COPYRIGHT}) 45 | set(MACOSX_BUNDLE_GUI_IDENTIFIER ${IDENTIFIER}) 46 | set(MACOSX_BUNDLE_ICON_FILE ${ICON_NAME}) 47 | endif() 48 | 49 | if (APPLE) 50 | set(${FILES_TO_INCLUDE} ${ICON_FILE}) 51 | elseif (WIN32) 52 | set(${FILES_TO_INCLUDE} ${RES_FILES}) 53 | endif() 54 | endmacro() 55 | 56 | macro(init_os_bundle) 57 | if (APPLE) 58 | set(OS_BUNDLE MACOSX_BUNDLE) 59 | elseif (WIN32) 60 | set(OS_BUNDLE WIN32) 61 | endif() 62 | endmacro() 63 | 64 | macro(fix_win_compiler) 65 | if (MSVC) 66 | set_target_properties(${PROJECT_NAME} PROPERTIES 67 | WIN32_EXECUTABLE YES 68 | LINK_FLAGS "/ENTRY:mainCRTStartup" 69 | ) 70 | endif() 71 | endmacro() 72 | 73 | macro(init_qt) 74 | # Let's do the CMake job for us 75 | set(CMAKE_AUTOMOC ON) # For meta object compiler 76 | set(CMAKE_AUTORCC ON) # Resource files 77 | set(CMAKE_AUTOUIC ON) # UI files 78 | endmacro() 79 | 80 | init_os_bundle() 81 | init_qt() 82 | fix_win_compiler() 83 | -------------------------------------------------------------------------------- /inspector/cmake/windows_metafile.rc.in: -------------------------------------------------------------------------------- 1 | #include "winver.h" 2 | 3 | IDI_ICON1 ICON DISCARDABLE "@ICON_FILE@" 4 | 5 | VS_VERSION_INFO VERSIONINFO 6 | FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@ 7 | PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@ 8 | FILEFLAGS 0x0L 9 | FILEFLAGSMASK 0x3fL 10 | FILEOS 0x00040004L 11 | FILETYPE 0x1L 12 | FILESUBTYPE 0x0L 13 | BEGIN 14 | BLOCK "StringFileInfo" 15 | BEGIN 16 | BLOCK "000004b0" 17 | BEGIN 18 | VALUE "CompanyName", "@COMPANY@" 19 | VALUE "FileDescription", "@PROJECT_NAME@" 20 | VALUE "FileVersion", "@PROJECT_VERSION@" 21 | VALUE "LegalCopyright", "@COPYRIGHT@" 22 | VALUE "InternalName", "@PROJECT_NAME@" 23 | VALUE "OriginalFilename", "@PROJECT_NAME@.exe" 24 | VALUE "ProductName", "@PROJECT_NAME@" 25 | VALUE "ProductVersion", "@PROJECT_VERSION@" 26 | END 27 | END 28 | END 29 | -------------------------------------------------------------------------------- /inspector/inspector.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /inspector/res/AppIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tristanpenman/valijson/4edda758546436462da479bb8c8514f8a95c35ad/inspector/res/AppIcon.icns -------------------------------------------------------------------------------- /inspector/res/AppIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tristanpenman/valijson/4edda758546436462da479bb8c8514f8a95c35ad/inspector/res/AppIcon.ico -------------------------------------------------------------------------------- /inspector/src/highlighter.cpp: -------------------------------------------------------------------------------- 1 | #include "highlighter.h" 2 | 3 | Highlighter::Highlighter(QTextDocument *parent) 4 | : QSyntaxHighlighter(parent) 5 | { 6 | // TODO 7 | } 8 | 9 | void Highlighter::highlightBlock(const QString &text) 10 | { 11 | // TODO 12 | } 13 | -------------------------------------------------------------------------------- /inspector/src/highlighter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Highlighter : public QSyntaxHighlighter 6 | { 7 | Q_OBJECT 8 | 9 | public: 10 | Highlighter(QTextDocument * parent = 0); 11 | 12 | protected: 13 | void highlightBlock(const QString & text) override; 14 | }; 15 | -------------------------------------------------------------------------------- /inspector/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "window.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #if QT_VERSION < 0x060000 7 | QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 8 | #endif 9 | 10 | QApplication app(argc, argv); 11 | Window window; 12 | window.show(); 13 | return app.exec(); 14 | } 15 | -------------------------------------------------------------------------------- /inspector/src/window.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "highlighter.h" 22 | #include "window.h" 23 | 24 | Window::Window(QWidget * parent) 25 | : QMainWindow(parent) 26 | , m_schema(nullptr) 27 | { 28 | setWindowTitle("JSON Inspector"); 29 | 30 | m_documentEditor = createEditor(false); 31 | m_schemaEditor = createEditor(false); 32 | m_errors = createEditor(true); 33 | 34 | auto documentTabWidget = createTabWidget(m_documentEditor, "Document"); 35 | auto schemaTabWidget = createTabWidget(m_schemaEditor, "Schema"); 36 | auto horizontalSplitter = createSplitter(schemaTabWidget, documentTabWidget, true); 37 | 38 | auto errorsTabWidget = createTabWidget(m_errors, "Errors"); 39 | auto verticalSplitter = createSplitter(horizontalSplitter, errorsTabWidget, false); 40 | verticalSplitter->setStretchFactor(0, 2); 41 | verticalSplitter->setStretchFactor(1, 1); 42 | 43 | auto toolBar = createToolBar(); 44 | auto statusBar = createStatusBar(); 45 | 46 | addToolBar(toolBar); 47 | setCentralWidget(verticalSplitter); 48 | setStatusBar(statusBar); 49 | 50 | connect(m_documentEditor, SIGNAL(textChanged()), this, SLOT(refreshJson())); 51 | connect(m_schemaEditor, SIGNAL(textChanged()), this, SLOT(refreshJson())); 52 | 53 | refreshJson(); 54 | } 55 | 56 | QTextEdit * Window::createEditor(bool readOnly) 57 | { 58 | QFont font; 59 | font.setFamily("Courier"); 60 | font.setFixedPitch(true); 61 | font.setPointSize(12); 62 | 63 | auto editor = new QTextEdit(); 64 | editor->setFont(font); 65 | editor->setReadOnly(readOnly); 66 | 67 | auto highlighter = new Highlighter(editor->document()); 68 | 69 | return editor; 70 | } 71 | 72 | QSplitter * Window::createSplitter(QWidget * left, QWidget * right, bool horizontal) 73 | { 74 | auto splitter = new QSplitter(horizontal ? Qt::Horizontal : Qt::Vertical); 75 | splitter->setChildrenCollapsible(false); 76 | splitter->insertWidget(0, left); 77 | splitter->insertWidget(1, right); 78 | 79 | return splitter; 80 | } 81 | 82 | QStatusBar * Window::createStatusBar() 83 | { 84 | return new QStatusBar(); 85 | } 86 | 87 | QTabWidget * Window::createTabWidget(QWidget * child, const QString & name) 88 | { 89 | auto tabWidget = new QTabWidget(); 90 | tabWidget->addTab(child, name); 91 | tabWidget->setDocumentMode(true); 92 | 93 | return tabWidget; 94 | } 95 | 96 | QToolBar * Window::createToolBar() 97 | { 98 | auto toolbar = new QToolBar(); 99 | toolbar->setMovable(false); 100 | 101 | auto openMenu = new QMenu("Open"); 102 | auto openSchemaAction = openMenu->addAction("Open Schema..."); 103 | auto openDocumentAction = openMenu->addAction("Open Document..."); 104 | 105 | auto openButton = new QToolButton(); 106 | openButton->setMenu(openMenu); 107 | openButton->setPopupMode(QToolButton::MenuButtonPopup); 108 | openButton->setText("Open"); 109 | openButton->setToolButtonStyle(Qt::ToolButtonTextOnly); 110 | toolbar->addWidget(openButton); 111 | 112 | connect(openButton, &QToolButton::clicked, openButton, &QToolButton::showMenu); 113 | connect(openDocumentAction, SIGNAL(triggered()), this, SLOT(showOpenDocumentDialog())); 114 | connect(openSchemaAction, SIGNAL(triggered()), this, SLOT(showOpenSchemaDialog())); 115 | 116 | return toolbar; 117 | } 118 | 119 | void Window::refreshJson() 120 | { 121 | QString errors; 122 | m_errors->setText(""); 123 | 124 | const auto schema = m_schemaEditor->toPlainText().toUtf8(); 125 | const auto doc = m_documentEditor->toPlainText().toUtf8(); 126 | 127 | if (schema.isEmpty()) { 128 | if (doc.isEmpty()) { 129 | m_errors->setText( 130 | "Please provide a schema and a document to be validated.\n\n" 131 | "Note that this example uses QtJson, which does not consider non-array and " 132 | "non-object values to be valid JSON documents."); 133 | return; 134 | } else { 135 | errors += "Schema error: must not be empty\n\n"; 136 | } 137 | } else { 138 | QJsonParseError error; 139 | m_schemaJson = QJsonDocument::fromJson(schema, &error); 140 | if (m_schemaJson.isNull()) { 141 | errors += QString("Schema error: ") + error.errorString() + "\n\n"; 142 | } 143 | } 144 | 145 | if (doc.isEmpty()) { 146 | if (!schema.isEmpty()) { 147 | errors += "Document error: must not be empty\n\n"; 148 | } 149 | } else { 150 | QJsonParseError error; 151 | m_documentJson = QJsonDocument::fromJson(doc, &error); 152 | if (m_documentJson.isNull()) { 153 | errors += QString("Document error: ") + error.errorString() + "\n\n"; 154 | } 155 | } 156 | 157 | if (!errors.isEmpty()) { 158 | m_errors->setText(errors); 159 | return; 160 | } 161 | 162 | try { 163 | valijson::adapters::QtJsonAdapter adapter(m_schemaJson.object()); 164 | valijson::SchemaParser parser; 165 | delete m_schema; 166 | m_schema = new valijson::Schema(); 167 | parser.populateSchema(adapter, *m_schema); 168 | validate(); 169 | } catch (std::runtime_error & error) { 170 | delete m_schema; 171 | m_schema = nullptr; 172 | m_errors->setText(QString("Schema error: ") + error.what()); 173 | } 174 | } 175 | 176 | void Window::showOpenDocumentDialog() 177 | { 178 | const QString fileName = QFileDialog::getOpenFileName(this, "Open Document", QString(), QString("*.json")); 179 | if (!fileName.isEmpty()) { 180 | QFile file(fileName); 181 | file.open(QFile::ReadOnly | QFile::Text); 182 | m_documentEditor->setText(file.readAll()); 183 | } 184 | } 185 | 186 | void Window::showOpenSchemaDialog() 187 | { 188 | const QString fileName = QFileDialog::getOpenFileName(this, "Open Schema", QString(), QString("*.json")); 189 | if (!fileName.isEmpty()) { 190 | QFile file(fileName); 191 | file.open(QFile::ReadOnly | QFile::Text); 192 | m_schemaEditor->setText(file.readAll()); 193 | } 194 | } 195 | 196 | void Window::validate() 197 | { 198 | valijson::ValidationResults results; 199 | valijson::Validator validator; 200 | valijson::adapters::QtJsonAdapter adapter(m_documentJson.object()); 201 | 202 | if (validator.validate(*m_schema, adapter, &results)) { 203 | m_errors->setText("Document is valid."); 204 | return; 205 | } 206 | 207 | valijson::ValidationResults::Error error; 208 | unsigned int errorNum = 1; 209 | std::stringstream ss; 210 | while (results.popError(error)) { 211 | std::string context; 212 | for (auto & itr : error.context) { 213 | context += itr; 214 | } 215 | 216 | ss << "Validation error #" << errorNum << std::endl 217 | << " context: " << context << std::endl 218 | << " desc: " << error.description << std::endl; 219 | ++errorNum; 220 | } 221 | 222 | m_errors->setText(QString::fromStdString(ss.str())); 223 | } -------------------------------------------------------------------------------- /inspector/src/window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class QJsonDocument; 7 | class QSplitter; 8 | class QStatusBar; 9 | class QTabWidget; 10 | class QTextEdit; 11 | class QToolBar; 12 | 13 | namespace valijson { 14 | class Schema; 15 | } 16 | 17 | class Window : public QMainWindow 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit Window(QWidget * parent = 0); 23 | 24 | public slots: 25 | void refreshJson(); 26 | 27 | void showOpenDocumentDialog(); 28 | void showOpenSchemaDialog(); 29 | 30 | private: 31 | QTextEdit * createEditor(bool readOnly); 32 | QSplitter * createSplitter(QWidget * left, QWidget * right, bool horizontal); 33 | QStatusBar * createStatusBar(); 34 | QTabWidget * createTabWidget(QWidget * child, const QString & name); 35 | QToolBar * createToolBar(); 36 | 37 | void validate(); 38 | 39 | QTextEdit * m_documentEditor; 40 | QTextEdit * m_schemaEditor; 41 | 42 | QTextEdit * m_errors; 43 | 44 | QJsonDocument m_documentJson; 45 | QJsonDocument m_schemaJson; 46 | 47 | valijson::Schema * m_schema; 48 | }; 49 | -------------------------------------------------------------------------------- /shellcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Shellcheck is a static analyzer for shell scripts: https://shellcheck.net/ 5 | # It is available in several operating systems and also as a docker image. 6 | # 7 | # If it finds any issues, it will output a small blurb describing the affected 8 | # line(s) and will have a generic issue ID. The issue ID can be opened on its 9 | # website to learn more about what the underlying problem is, why it's a 10 | # problem, and (usually) suggests a way to fix. 11 | # Specific shellcheck issues can be disabled (aka silenced). Doing so is 12 | # usually pretty loud during code review. 13 | # https://github.com/koalaman/shellcheck/wiki/Directive 14 | 15 | # https://stackoverflow.com/a/2871034/1111557 16 | set -euo pipefail 17 | 18 | HERE="$(cd "$(dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 19 | SHELLCHECK="${SHELLCHECK:-"/usr/bin/shellcheck"}" 20 | SEARCH_DIR="${SEARCH_DIR:-"$HERE"}" 21 | cd "${SEARCH_DIR}" #so that we can call git 22 | 23 | # 24 | # This block will: 25 | # 1) `find` files under `SEARCH_DIR` 26 | # 2) skip anything under `/thirdparty/`, `/.git/` 27 | # 3) in a loop reading each path: 28 | # 3a) ignore files that git also ignores 29 | # 3b) use `file` to filter only script files 30 | # 3c) run shellcheck against that script 31 | # 4) if any paths are found to have an error, their paths are collated. 32 | FAILED_PATHS=() 33 | while read -r file_path 34 | do 35 | if git rev-parse --git-dir > /dev/null 2>&1; 36 | then 37 | git check-ignore --quiet "${file_path}" && continue 38 | fi 39 | file "${file_path}" | grep -q 'shell script' || continue 40 | SCRIPT_PATH="${file_path}" 41 | echo "Checking: ${SCRIPT_PATH}" 42 | "${SHELLCHECK}" \ 43 | "${SCRIPT_PATH}" \ 44 | || FAILED_PATHS+=( "${SCRIPT_PATH}" ) 45 | done < <( 46 | find "${SEARCH_DIR}" -type f \ 47 | | grep -v '/\.git/\|/thirdparty/' 48 | ) 49 | 50 | # 51 | # If there are any failed paths, summarize them here. 52 | # Then report a failing status to our caller. 53 | if [[ 0 -lt "${#FAILED_PATHS[@]}" ]]; then 54 | >&2 echo "These scripts aren't shellcheck-clean:" 55 | for path in "${FAILED_PATHS[@]}"; do 56 | >&2 echo "${path}" 57 | done 58 | exit 1 59 | fi 60 | 61 | # If we get here, then none of the scripts had any warnings. 62 | echo "All scripts found (listed above) passed shellcheck" 63 | -------------------------------------------------------------------------------- /tests/data/documents/array_doubles_10_20_30_40.json: -------------------------------------------------------------------------------- 1 | [10.0, 20.0, 30.0, 40.0] -------------------------------------------------------------------------------- /tests/data/documents/array_doubles_1_2_3.json: -------------------------------------------------------------------------------- 1 | [1.0, 2.0, 3.0] -------------------------------------------------------------------------------- /tests/data/documents/array_doubles_1_2_3_4.json: -------------------------------------------------------------------------------- 1 | [1.0, 2.0, 3.0, 4.0] -------------------------------------------------------------------------------- /tests/data/documents/array_empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/data/documents/array_integers_10_20_30_40.json: -------------------------------------------------------------------------------- 1 | [10, 20, 30, 40] -------------------------------------------------------------------------------- /tests/data/documents/array_integers_1_2_3.json: -------------------------------------------------------------------------------- 1 | [1, 2, 3] -------------------------------------------------------------------------------- /tests/data/documents/array_integers_1_2_3_4.json: -------------------------------------------------------------------------------- 1 | [1, 2, 3, 4] -------------------------------------------------------------------------------- /tests/data/documents/array_strings_10_20_30_40.json: -------------------------------------------------------------------------------- 1 | ["10", "20", "30", "40"] -------------------------------------------------------------------------------- /tests/data/documents/array_strings_1_2_3.json: -------------------------------------------------------------------------------- 1 | ["1", "2", "3"] -------------------------------------------------------------------------------- /tests/data/documents/array_strings_1_2_3_4.json: -------------------------------------------------------------------------------- 1 | ["1", "2", "3", "4"] -------------------------------------------------------------------------------- /tests/data/documents/date_time_format.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": "AAA", 4 | "validity": "invalid" 5 | }, 6 | { 7 | "timestamp": "2000", 8 | "validity": "invalid" 9 | }, 10 | { 11 | "timestamp": "2000-01-01T00:00:00", 12 | "validity": "permissive" 13 | }, 14 | { 15 | "timestamp": "2000-01-01T00:00:00Z", 16 | "validity": "strict" 17 | }, 18 | { 19 | "timestamp": "2000-01-01T00:00:00+02:00", 20 | "validity": "strict" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /tests/data/documents/object_empty.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tests/data/schemas/allof_integers_and_numbers.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "Document must contain an array of integers, all unique.", 3 | "allOf": [ 4 | { 5 | "items": { 6 | "type": "integer" 7 | }, 8 | "additionalItems": false, 9 | "type": "array" 10 | }, 11 | { 12 | "items": { 13 | "type": "number" 14 | }, 15 | "additionalItems": false, 16 | "type": "array", 17 | "uniqueItems": true 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /tests/data/schemas/circular_reference.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Circular reference when parsing properties keyword", 3 | "properties": { 4 | "foo": {"$ref": "#/properties/bar"}, 5 | "bar": {"$ref": "#/properties/baz"}, 6 | "baz": {"$ref": "#/properties/foo"} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/data/schemas/date_time_format.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "timestamp": { 4 | "format": "date-time" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/fuzzing/fuzzer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using valijson::Schema; 6 | using valijson::SchemaParser; 7 | using valijson::ValidationResults; 8 | using valijson::Validator; 9 | using valijson::adapters::AdapterTraits; 10 | using valijson::adapters::RapidJsonAdapter; 11 | using AdapterType = RapidJsonAdapter; 12 | 13 | void runOneTest(const AdapterType &test, const Schema &schema, 14 | Validator::TypeCheckingMode mode) 15 | { 16 | try { 17 | if (!test.isObject()) { 18 | return; 19 | } 20 | 21 | const AdapterType::Object testObject = test.getObject(); 22 | const auto dataItr = testObject.find("data"); 23 | 24 | if (dataItr == testObject.end()) { 25 | return; 26 | } 27 | 28 | Validator validator(mode); 29 | ValidationResults results; 30 | validator.validate(schema, dataItr->second, &results); 31 | } catch (const std::exception &) { 32 | } 33 | } 34 | 35 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) 36 | { 37 | AdapterTraits::DocumentType document; 38 | document.template Parse(reinterpret_cast(data), size); 39 | 40 | if (document.HasParseError() || !document.IsArray()) { 41 | return 0; 42 | } 43 | 44 | for (const auto &testCase : AdapterType(document).getArray()) { 45 | if (!testCase.isObject()) { 46 | continue; 47 | } 48 | 49 | const AdapterType::Object object = testCase.getObject(); 50 | const auto schemaItr = object.find("schema"); 51 | const auto testsItr = object.find("tests"); 52 | 53 | if (schemaItr == object.end() || testsItr == object.end() || 54 | !testsItr->second.isArray()) { 55 | continue; 56 | } 57 | 58 | Schema schema; 59 | SchemaParser parser(size % 2 ? SchemaParser::kDraft4 60 | : SchemaParser::kDraft7); 61 | 62 | try { 63 | parser.populateSchema(schemaItr->second, schema); 64 | } catch (const std::exception &) { 65 | continue; 66 | } 67 | 68 | const auto mode = testsItr->second.hasStrictTypes() 69 | ? Validator::kStrongTypes 70 | : Validator::kWeakTypes; 71 | 72 | for (const AdapterType test : testsItr->second.getArray()) { 73 | runOneTest(test, schema, mode); 74 | } 75 | } 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /tests/fuzzing/oss-fuzz-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | git submodule update --init --depth 1 thirdparty 4 | 5 | mkdir build 6 | cd build 7 | cmake \ 8 | -Dvalijson_BUILD_TESTS=FALSE \ 9 | -Dvalijson_BUILD_EXAMPLES=FALSE \ 10 | -Dvalijson_EXCLUDE_BOOST=TRUE \ 11 | .. 12 | 13 | make -j"$(nproc)" 14 | 15 | cd ../tests/fuzzing 16 | 17 | # CXXFLAGS may contain spaces 18 | # shellcheck disable=SC2086 19 | "$CXX" $CXXFLAGS "$LIB_FUZZING_ENGINE" \ 20 | -DVALIJSON_USE_EXCEPTIONS=1 \ 21 | -I/src/valijson/thirdparty/rapidjson/include \ 22 | -I/src/valijson/include \ 23 | fuzzer.cpp -o "${OUT}/fuzzer" 24 | 25 | mkdir seed_corpus 26 | 27 | find "${SRC}/valijson/thirdparty/JSON-Schema-Test-Suite/tests" -name "*.json" | while read file; do 28 | sha1=$(sha1sum "$file" | awk '{print $1}') 29 | cp "$file" seed_corpus/"${sha1}" 30 | done 31 | 32 | zip -j -r "${OUT}/fuzzer_seed_corpus.zip" seed_corpus 33 | -------------------------------------------------------------------------------- /tests/test_boost_json_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include // Needs to be included exactly once in the code to use header-only version of Boost.JSON 4 | 5 | #include 6 | 7 | class TestBoostJsonAdapter : public testing::Test 8 | { 9 | 10 | }; 11 | 12 | TEST_F(TestBoostJsonAdapter, BasicArrayIteration) 13 | { 14 | const unsigned int numElements = 10; 15 | 16 | // Create a Json document that consists of an array of numbers 17 | boost::json::array array; 18 | for (unsigned int i = 0; i < numElements; i++) { 19 | // Boost.JSON differs from some other libraries in offering emplace_back() 20 | // as well as push_back(). Using the former here saves us having to create 21 | // a temporary. 22 | array.emplace_back(static_cast(i)); 23 | } 24 | boost::json::value document(array); 25 | 26 | // Ensure that wrapping the document preserves the array and does not allow 27 | // it to be cast to other types 28 | valijson::adapters::BoostJsonAdapter adapter(document); 29 | #if VALIJSON_USE_EXCEPTIONS 30 | ASSERT_NO_THROW( adapter.getArray() ); 31 | ASSERT_ANY_THROW( adapter.getBool() ); 32 | ASSERT_ANY_THROW( adapter.getDouble() ); 33 | ASSERT_ANY_THROW( adapter.getObject() ); 34 | ASSERT_ANY_THROW( adapter.getString() ); 35 | #endif 36 | 37 | // Ensure that the array contains the expected number of elements 38 | EXPECT_EQ( numElements, adapter.getArray().size() ); 39 | 40 | // Ensure that the elements are returned in the order they were inserted 41 | unsigned int expectedValue = 0; 42 | for (const valijson::adapters::BoostJsonAdapter value : adapter.getArray()) { 43 | ASSERT_TRUE( value.isNumber() ); 44 | EXPECT_EQ( double(expectedValue), value.getDouble() ); 45 | expectedValue++; 46 | } 47 | 48 | // Ensure that the correct number of elements were iterated over 49 | EXPECT_EQ(numElements, expectedValue); 50 | } 51 | 52 | TEST_F(TestBoostJsonAdapter, BasicObjectIteration) 53 | { 54 | const unsigned int numElements = 10; 55 | 56 | // Create a DropBoxJson document that consists of an object that maps numeric 57 | // strings their corresponding numeric values 58 | boost::json::object object; 59 | for (uint32_t i = 0; i < numElements; i++) { 60 | object[std::to_string(i)] = static_cast(i); 61 | } 62 | boost::json::value document(object); 63 | 64 | // Ensure that wrapping the document preserves the object and does not 65 | // allow it to be cast to other types 66 | valijson::adapters::BoostJsonAdapter adapter(document); 67 | #if VALIJSON_USE_EXCEPTIONS 68 | ASSERT_NO_THROW( adapter.getObject() ); 69 | ASSERT_ANY_THROW( adapter.getArray() ); 70 | ASSERT_ANY_THROW( adapter.getBool() ); 71 | ASSERT_ANY_THROW( adapter.getDouble() ); 72 | ASSERT_ANY_THROW( adapter.getString() ); 73 | #endif 74 | 75 | // Ensure that the object contains the expected number of members 76 | EXPECT_EQ( numElements, adapter.getObject().size() ); 77 | 78 | // Ensure that the members are returned in the order they were inserted 79 | unsigned int expectedValue = 0; 80 | for (const valijson::adapters::BoostJsonAdapter::ObjectMember member : adapter.getObject()) { 81 | ASSERT_TRUE( member.second.isNumber() ); 82 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 83 | EXPECT_EQ( double(expectedValue), member.second.getDouble() ); 84 | expectedValue++; 85 | } 86 | 87 | // Ensure that the correct number of elements were iterated over 88 | EXPECT_EQ( numElements, expectedValue ); 89 | } 90 | -------------------------------------------------------------------------------- /tests/test_date_time_format.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define TEST_DATA_DIR "../tests/data" 13 | 14 | using std::string; 15 | 16 | using valijson::adapters::AdapterTraits; 17 | using valijson::adapters::RapidJsonAdapter; 18 | using valijson::utils::loadDocument; 19 | using valijson::Schema; 20 | using valijson::SchemaParser; 21 | using valijson::Validator; 22 | using valijson::ValidationResults; 23 | 24 | class TestDateTimeFormat : public ::testing::Test 25 | { 26 | 27 | }; 28 | 29 | TEST_F(TestDateTimeFormat, StrictAndPermissiveDateTimes) 30 | { 31 | // Load schema document 32 | rapidjson::Document schemaDocument; 33 | ASSERT_TRUE( loadDocument(TEST_DATA_DIR "/schemas/date_time_format.schema.json", schemaDocument) ); 34 | RapidJsonAdapter schemaAdapter(schemaDocument); 35 | 36 | // Parse schema document 37 | Schema schema; 38 | SchemaParser schemaParser; 39 | #if VALIJSON_USE_EXCEPTIONS 40 | ASSERT_NO_THROW(schemaParser.populateSchema(schemaAdapter, schema)); 41 | #else 42 | schemaParser.populateSchema(schemaAdapter, schema); 43 | #endif 44 | 45 | // Load test document 46 | rapidjson::Document testDocument; 47 | ASSERT_TRUE( loadDocument(TEST_DATA_DIR "/documents/date_time_format.json", testDocument) ); 48 | RapidJsonAdapter testAdapter(testDocument); 49 | 50 | // Setup validators 51 | Validator strictValidator(Validator::kStrongTypes, Validator::kStrictDateTime); 52 | Validator permissiveValidator(Validator::kStrongTypes, Validator::kPermissiveDateTime); 53 | 54 | const RapidJsonAdapter::Array examples = testAdapter.asArray(); 55 | for (auto &&example : examples) { 56 | 57 | auto validity = example.asObject().find("validity")->second.asString(); 58 | if (validity == "strict") { 59 | EXPECT_TRUE( strictValidator.validate(schema, example, NULL) ); 60 | EXPECT_TRUE( permissiveValidator.validate(schema, example, NULL) ); 61 | } else if (validity == "permissive") { 62 | EXPECT_FALSE( strictValidator.validate(schema, example, NULL) ); 63 | EXPECT_TRUE( permissiveValidator.validate(schema, example, NULL) ); 64 | } else { 65 | EXPECT_FALSE( strictValidator.validate(schema, example, NULL) ); 66 | EXPECT_FALSE( permissiveValidator.validate(schema, example, NULL) ); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/test_fetch_absolute_uri_document_callback.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using valijson::Schema; 11 | using valijson::SchemaParser; 12 | using valijson::adapters::RapidJsonAdapter; 13 | using valijson::Validator; 14 | 15 | class TestFetchAbsoluteUriDocumentCallback : public ::testing::Test 16 | { 17 | 18 | }; 19 | 20 | const rapidjson::Document * fetchAbsoluteUriDocument(const std::string &uri) 21 | { 22 | EXPECT_STREQ("http://localhost:1234/", uri.c_str()); 23 | 24 | rapidjson::Document *fetchedRoot = new rapidjson::Document(); 25 | fetchedRoot->SetObject(); 26 | 27 | rapidjson::Value valueOfTypeAttribute; 28 | valueOfTypeAttribute.SetString("string", fetchedRoot->GetAllocator()); 29 | 30 | rapidjson::Value schemaOfTestProperty; 31 | schemaOfTestProperty.SetObject(); 32 | schemaOfTestProperty.AddMember("type", valueOfTypeAttribute, 33 | fetchedRoot->GetAllocator()); 34 | 35 | rapidjson::Value propertiesConstraint; 36 | propertiesConstraint.SetObject(); 37 | propertiesConstraint.AddMember("test", schemaOfTestProperty, 38 | fetchedRoot->GetAllocator()); 39 | 40 | fetchedRoot->AddMember("properties", propertiesConstraint, 41 | fetchedRoot->GetAllocator()); 42 | 43 | return fetchedRoot; 44 | } 45 | 46 | void freeAbsoluteUriDocument(const rapidjson::Document *adapter) 47 | { 48 | delete adapter; 49 | } 50 | 51 | TEST_F(TestFetchAbsoluteUriDocumentCallback, Basics) 52 | { 53 | // Define schema 54 | rapidjson::Document schemaDocument; 55 | RapidJsonAdapter schemaDocumentAdapter(schemaDocument); 56 | schemaDocument.SetObject(); 57 | schemaDocument.AddMember("$ref", "http://localhost:1234/#/", 58 | schemaDocument.GetAllocator()); 59 | 60 | // Parse schema document 61 | Schema schema; 62 | SchemaParser schemaParser; 63 | schemaParser.populateSchema(schemaDocumentAdapter, schema, fetchAbsoluteUriDocument, 64 | freeAbsoluteUriDocument); 65 | 66 | // Test resulting schema with a valid document 67 | rapidjson::Document validDocument; 68 | validDocument.SetObject(); 69 | validDocument.AddMember("test", "valid", schemaDocument.GetAllocator()); 70 | Validator validator; 71 | EXPECT_TRUE(validator.validate(schema, RapidJsonAdapter(validDocument), 72 | NULL)); 73 | 74 | // Test resulting schema with an invalid document 75 | rapidjson::Document invalidDocument; 76 | invalidDocument.SetObject(); 77 | invalidDocument.AddMember("test", 123, schemaDocument.GetAllocator()); 78 | EXPECT_FALSE(validator.validate(schema, RapidJsonAdapter(invalidDocument), 79 | NULL)); 80 | } 81 | -------------------------------------------------------------------------------- /tests/test_fetch_urn_document_callback.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using valijson::Schema; 11 | using valijson::SchemaParser; 12 | using valijson::adapters::RapidJsonAdapter; 13 | using valijson::Validator; 14 | 15 | class TestFetchUrnDocumentCallback : public ::testing::Test 16 | { 17 | 18 | }; 19 | 20 | const rapidjson::Document * fetchUrnDocument(const std::string &uri) 21 | { 22 | EXPECT_STREQ("urn:mvn:example.schema.common:status:1.1.0", uri.c_str()); 23 | 24 | rapidjson::Document *fetchedRoot = new rapidjson::Document(); 25 | fetchedRoot->SetObject(); 26 | 27 | rapidjson::Value valueOfTypeAttribute; 28 | valueOfTypeAttribute.SetString("string", fetchedRoot->GetAllocator()); 29 | 30 | rapidjson::Value schemaOfTestProperty; 31 | schemaOfTestProperty.SetObject(); 32 | schemaOfTestProperty.AddMember("type", valueOfTypeAttribute, 33 | fetchedRoot->GetAllocator()); 34 | 35 | rapidjson::Value propertiesConstraint; 36 | propertiesConstraint.SetObject(); 37 | propertiesConstraint.AddMember("test", schemaOfTestProperty, 38 | fetchedRoot->GetAllocator()); 39 | 40 | fetchedRoot->AddMember("properties", propertiesConstraint, 41 | fetchedRoot->GetAllocator()); 42 | 43 | return fetchedRoot; 44 | } 45 | 46 | void freeUrnDocument(const rapidjson::Document *adapter) 47 | { 48 | delete adapter; 49 | } 50 | 51 | TEST_F(TestFetchUrnDocumentCallback, Basics) 52 | { 53 | // Define schema 54 | rapidjson::Document schemaDocument; 55 | RapidJsonAdapter schemaDocumentAdapter(schemaDocument); 56 | schemaDocument.SetObject(); 57 | schemaDocument.AddMember("$ref", "urn:mvn:example.schema.common:status:1.1.0", 58 | schemaDocument.GetAllocator()); 59 | 60 | // Parse schema document 61 | Schema schema; 62 | SchemaParser schemaParser; 63 | schemaParser.populateSchema(schemaDocumentAdapter, schema, fetchUrnDocument, 64 | freeUrnDocument); 65 | 66 | // Test resulting schema with a valid document 67 | rapidjson::Document validDocument; 68 | validDocument.SetObject(); 69 | validDocument.AddMember("test", "valid", schemaDocument.GetAllocator()); 70 | Validator validator; 71 | EXPECT_TRUE(validator.validate(schema, RapidJsonAdapter(validDocument), 72 | NULL)); 73 | 74 | // Test resulting schema with an invalid document 75 | rapidjson::Document invalidDocument; 76 | invalidDocument.SetObject(); 77 | invalidDocument.AddMember("test", 123, schemaDocument.GetAllocator()); 78 | EXPECT_FALSE(validator.validate(schema, RapidJsonAdapter(invalidDocument), 79 | NULL)); 80 | } 81 | -------------------------------------------------------------------------------- /tests/test_json11_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | class TestJson11Adapter : public testing::Test 6 | { 7 | 8 | }; 9 | 10 | TEST_F(TestJson11Adapter, BasicArrayIteration) 11 | { 12 | const unsigned int numElements = 10; 13 | 14 | // Create a Json11 document that consists of an array of numbers 15 | json11::Json::array array; 16 | for (unsigned int i = 0; i < numElements; i++) { 17 | json11::Json value(static_cast(i)); 18 | array.push_back(value); 19 | } 20 | json11::Json document(array); 21 | 22 | // Ensure that wrapping the document preserves the array and does not allow 23 | // it to be cast to other types 24 | valijson::adapters::Json11Adapter adapter(document); 25 | #if VALIJSON_USE_EXCEPTIONS 26 | ASSERT_NO_THROW( adapter.getArray() ); 27 | ASSERT_ANY_THROW( adapter.getBool() ); 28 | ASSERT_ANY_THROW( adapter.getDouble() ); 29 | ASSERT_ANY_THROW( adapter.getObject() ); 30 | ASSERT_ANY_THROW( adapter.getString() ); 31 | #endif 32 | // Ensure that the array contains the expected number of elements 33 | EXPECT_EQ( numElements, adapter.getArray().size() ); 34 | 35 | // Ensure that the elements are returned in the order they were inserted 36 | unsigned int expectedValue = 0; 37 | for (const valijson::adapters::Json11Adapter value : adapter.getArray()) { 38 | ASSERT_TRUE( value.isNumber() ); 39 | EXPECT_EQ( double(expectedValue), value.getDouble() ); 40 | expectedValue++; 41 | } 42 | 43 | // Ensure that the correct number of elements were iterated over 44 | EXPECT_EQ(numElements, expectedValue); 45 | } 46 | 47 | TEST_F(TestJson11Adapter, BasicObjectIteration) 48 | { 49 | const unsigned int numElements = 10; 50 | 51 | // Create a DropBoxJson11 document that consists of an object that maps numeric 52 | // strings their corresponding numeric values 53 | json11::Json::object object; 54 | for (unsigned int i = 0; i < numElements; i++) { 55 | std::string name(std::to_string(i)); 56 | object[name] = json11::Json(static_cast(i)); 57 | } 58 | json11::Json document(object); 59 | 60 | // Ensure that wrapping the document preserves the object and does not 61 | // allow it to be cast to other types 62 | valijson::adapters::Json11Adapter adapter(document); 63 | #if VALIJSON_USE_EXCEPTIONS 64 | ASSERT_NO_THROW( adapter.getObject() ); 65 | ASSERT_ANY_THROW( adapter.getArray() ); 66 | ASSERT_ANY_THROW( adapter.getBool() ); 67 | ASSERT_ANY_THROW( adapter.getDouble() ); 68 | ASSERT_ANY_THROW( adapter.getString() ); 69 | #endif 70 | // Ensure that the object contains the expected number of members 71 | EXPECT_EQ( numElements, adapter.getObject().size() ); 72 | 73 | // Ensure that the members are returned in the order they were inserted 74 | unsigned int expectedValue = 0; 75 | for (const valijson::adapters::Json11Adapter::ObjectMember member : adapter.getObject()) { 76 | ASSERT_TRUE( member.second.isNumber() ); 77 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 78 | EXPECT_EQ( double(expectedValue), member.second.getDouble() ); 79 | expectedValue++; 80 | } 81 | 82 | // Ensure that the correct number of elements were iterated over 83 | EXPECT_EQ( numElements, expectedValue ); 84 | } 85 | -------------------------------------------------------------------------------- /tests/test_jsoncpp_adapter.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | class TestJsonCppAdapter : public testing::Test 7 | { 8 | 9 | }; 10 | 11 | TEST_F(TestJsonCppAdapter, BasicArrayIteration) 12 | { 13 | const unsigned int numElements = 10; 14 | 15 | // Create a jsoncpp document that consists of an array of numbers 16 | Json::Value document(Json::arrayValue); 17 | for (unsigned int i = 0; i < numElements; i++) { 18 | document.append(Json::Value(i)); 19 | } 20 | 21 | // Ensure that wrapping the document preserves the array and does not allow 22 | // it to be cast to other types 23 | valijson::adapters::JsonCppAdapter adapter(document); 24 | #if VALIJSON_USE_EXCEPTIONS 25 | ASSERT_NO_THROW( adapter.getArray() ); 26 | ASSERT_ANY_THROW( adapter.getBool() ); 27 | ASSERT_ANY_THROW( adapter.getDouble() ); 28 | ASSERT_ANY_THROW( adapter.getObject() ); 29 | ASSERT_ANY_THROW( adapter.getString() ); 30 | #endif 31 | // Ensure that the array contains the expected number of elements 32 | EXPECT_EQ( numElements, adapter.getArray().size() ); 33 | 34 | // Ensure that the elements are returned in the order they were inserted 35 | unsigned int expectedValue = 0; 36 | for (const valijson::adapters::JsonCppAdapter value : adapter.getArray()) { 37 | ASSERT_TRUE( value.isNumber() ); 38 | EXPECT_EQ( double(expectedValue), value.getNumber() ); 39 | expectedValue++; 40 | } 41 | 42 | // Ensure that the correct number of elements were iterated over 43 | EXPECT_EQ(numElements, expectedValue); 44 | } 45 | 46 | TEST_F(TestJsonCppAdapter, BasicObjectIteration) 47 | { 48 | const unsigned int numElements = 10; 49 | 50 | // Create a jsoncpp document that consists of an object that maps numeric 51 | // strings their corresponding numeric values 52 | Json::Value document(Json::objectValue); 53 | for (unsigned int i = 0; i < numElements; i++) { 54 | std::string name(std::to_string(i)); 55 | document[name] = Json::Value(double(i)); 56 | } 57 | 58 | // Ensure that wrapping the document preserves the object and does not 59 | // allow it to be cast to other types 60 | valijson::adapters::JsonCppAdapter adapter(document); 61 | #if VALIJSON_USE_EXCEPTIONS 62 | ASSERT_NO_THROW( adapter.getObject() ); 63 | ASSERT_ANY_THROW( adapter.getArray() ); 64 | ASSERT_ANY_THROW( adapter.getBool() ); 65 | ASSERT_ANY_THROW( adapter.getDouble() ); 66 | ASSERT_ANY_THROW( adapter.getString() ); 67 | #endif 68 | 69 | // Ensure that the object contains the expected number of members 70 | EXPECT_EQ( numElements, adapter.getObject().size() ); 71 | 72 | // Ensure that the members are returned in the order they were inserted 73 | unsigned int expectedValue = 0; 74 | for (const valijson::adapters::JsonCppAdapter::ObjectMember member : adapter.getObject()) { 75 | ASSERT_TRUE( member.second.isNumber() ); 76 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 77 | EXPECT_EQ( double(expectedValue), member.second.getDouble() ); 78 | expectedValue++; 79 | } 80 | 81 | // Ensure that the correct number of elements were iterated over 82 | EXPECT_EQ( numElements, expectedValue ); 83 | } 84 | -------------------------------------------------------------------------------- /tests/test_nlohmann_json_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | class TestNlohmannJsonAdapter : public testing::Test 6 | { 7 | 8 | }; 9 | 10 | TEST_F(TestNlohmannJsonAdapter, BasicArrayIteration) 11 | { 12 | const unsigned int numElements = 10; 13 | 14 | // Create a Json document that consists of an array of numbers 15 | nlohmann::json document; 16 | 17 | for (unsigned int i = 0; i < numElements; i++) { 18 | document.push_back(static_cast(i)); 19 | } 20 | 21 | // Ensure that wrapping the document preserves the array and does not allow 22 | // it to be cast to other types 23 | valijson::adapters::NlohmannJsonAdapter adapter(document); 24 | #if VALIJSON_USE_EXCEPTIONS 25 | ASSERT_NO_THROW( adapter.getArray() ); 26 | ASSERT_ANY_THROW( adapter.getBool() ); 27 | ASSERT_ANY_THROW( adapter.getDouble() ); 28 | ASSERT_ANY_THROW( adapter.getObject() ); 29 | ASSERT_ANY_THROW( adapter.getString() ); 30 | #endif 31 | 32 | // Ensure that the array contains the expected number of elements 33 | EXPECT_EQ( numElements, adapter.getArray().size() ); 34 | 35 | // Ensure that the elements are returned in the order they were inserted 36 | unsigned int expectedValue = 0; 37 | for (const valijson::adapters::NlohmannJsonAdapter value : adapter.getArray()) { 38 | ASSERT_TRUE( value.isNumber() ); 39 | EXPECT_EQ( double(expectedValue), value.getDouble() ); 40 | expectedValue++; 41 | } 42 | 43 | // Ensure that the correct number of elements were iterated over 44 | EXPECT_EQ(numElements, expectedValue); 45 | } 46 | 47 | TEST_F(TestNlohmannJsonAdapter, BasicObjectIteration) 48 | { 49 | const unsigned int numElements = 10; 50 | 51 | // Create a DropBoxJson document that consists of an object that maps numeric 52 | // strings their corresponding numeric values 53 | nlohmann::json document; 54 | for (uint32_t i = 0; i < numElements; i++) { 55 | document[std::to_string(i)] = static_cast(i); 56 | } 57 | 58 | // Ensure that wrapping the document preserves the object and does not 59 | // allow it to be cast to other types 60 | valijson::adapters::NlohmannJsonAdapter adapter(document); 61 | #if VALIJSON_USE_EXCEPTIONS 62 | ASSERT_NO_THROW( adapter.getObject() ); 63 | ASSERT_ANY_THROW( adapter.getArray() ); 64 | ASSERT_ANY_THROW( adapter.getBool() ); 65 | ASSERT_ANY_THROW( adapter.getDouble() ); 66 | ASSERT_ANY_THROW( adapter.getString() ); 67 | #endif 68 | 69 | // Ensure that the object contains the expected number of members 70 | EXPECT_EQ( numElements, adapter.getObject().size() ); 71 | 72 | // Ensure that the members are returned in the order they were inserted 73 | unsigned int expectedValue = 0; 74 | for (const valijson::adapters::NlohmannJsonAdapter::ObjectMember member : adapter.getObject()) { 75 | ASSERT_TRUE( member.second.isNumber() ); 76 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 77 | EXPECT_EQ( double(expectedValue), member.second.getDouble() ); 78 | expectedValue++; 79 | } 80 | 81 | // Ensure that the correct number of elements were iterated over 82 | EXPECT_EQ( numElements, expectedValue ); 83 | } 84 | -------------------------------------------------------------------------------- /tests/test_picojson_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | class TestPicoJsonAdapter : public testing::Test 8 | { 9 | 10 | }; 11 | 12 | TEST_F(TestPicoJsonAdapter, BasicArrayIteration) 13 | { 14 | const unsigned int numElements = 10; 15 | 16 | // Create a picojson document that consists of an array of numbers 17 | picojson::array array; 18 | for (unsigned int i = 0; i < numElements; i++) { 19 | picojson::value value(static_cast(i)); 20 | array.push_back(value); 21 | } 22 | picojson::value document(array); 23 | 24 | // Ensure that wrapping the document preserves the array and does not allow 25 | // it to be cast to other types 26 | valijson::adapters::PicoJsonAdapter adapter(document); 27 | #if VALIJSON_USE_EXCEPTIONS 28 | ASSERT_NO_THROW( adapter.getArray() ); 29 | ASSERT_ANY_THROW( adapter.getBool() ); 30 | ASSERT_ANY_THROW( adapter.getDouble() ); 31 | ASSERT_ANY_THROW( adapter.getObject() ); 32 | ASSERT_ANY_THROW( adapter.getString() ); 33 | #endif 34 | 35 | // Ensure that the array contains the expected number of elements 36 | EXPECT_EQ( numElements, adapter.getArray().size() ); 37 | 38 | // Ensure that the elements are returned in the order they were inserted 39 | unsigned int expectedValue = 0; 40 | for (const valijson::adapters::PicoJsonAdapter value : adapter.getArray()) { 41 | ASSERT_TRUE( value.isNumber() ); 42 | EXPECT_EQ( double(expectedValue), value.getDouble() ); 43 | expectedValue++; 44 | } 45 | 46 | // Ensure that the correct number of elements were iterated over 47 | EXPECT_EQ(numElements, expectedValue); 48 | } 49 | 50 | TEST_F(TestPicoJsonAdapter, BasicObjectIteration) 51 | { 52 | const unsigned int numElements = 10; 53 | 54 | // Create a picojson document that consists of an object that maps numeric 55 | // strings their corresponding numeric values 56 | picojson::object object; 57 | for (unsigned int i = 0; i < numElements; i++) { 58 | std::string name(std::to_string(i)); 59 | object[name] = picojson::value(static_cast(i)); 60 | } 61 | picojson::value document(object); 62 | 63 | // Ensure that wrapping the document preserves the object and does not 64 | // allow it to be cast to other types 65 | valijson::adapters::PicoJsonAdapter adapter(document); 66 | #if VALIJSON_USE_EXCEPTIONS 67 | ASSERT_NO_THROW( adapter.getObject() ); 68 | ASSERT_ANY_THROW( adapter.getArray() ); 69 | ASSERT_ANY_THROW( adapter.getBool() ); 70 | ASSERT_ANY_THROW( adapter.getDouble() ); 71 | ASSERT_ANY_THROW( adapter.getString() ); 72 | #endif 73 | 74 | // Ensure that the object contains the expected number of members 75 | EXPECT_EQ( numElements, adapter.getObject().size() ); 76 | 77 | // Ensure that the members are returned in the order they were inserted 78 | unsigned int expectedValue = 0; 79 | for (const valijson::adapters::PicoJsonAdapter::ObjectMember member : adapter.getObject()) { 80 | ASSERT_TRUE( member.second.isNumber() ); 81 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 82 | EXPECT_EQ( double(expectedValue), member.second.getDouble() ); 83 | expectedValue++; 84 | } 85 | 86 | // Ensure that the correct number of elements were iterated over 87 | EXPECT_EQ( numElements, expectedValue ); 88 | } 89 | -------------------------------------------------------------------------------- /tests/test_poco_json_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | class TestPocoJsonAdapter : public testing::Test 6 | { 7 | 8 | }; 9 | 10 | TEST_F(TestPocoJsonAdapter, BasicArrayIteration) 11 | { 12 | const unsigned int numElements = 10; 13 | 14 | // Create a Json document that consists of an array of numbers 15 | Poco::JSON::Array::Ptr documentImpl = new Poco::JSON::Array(); 16 | 17 | for (unsigned int i = 0; i < numElements; i++) { 18 | documentImpl->set(i, static_cast(i)); 19 | } 20 | 21 | Poco::Dynamic::Var document = documentImpl; 22 | 23 | // Ensure that wrapping the document preserves the array and does not allow 24 | // it to be cast to other types 25 | valijson::adapters::PocoJsonAdapter adapter(document); 26 | ASSERT_NO_THROW( adapter.getArray() ); 27 | ASSERT_ANY_THROW( adapter.getBool() ); 28 | ASSERT_ANY_THROW( adapter.getDouble() ); 29 | ASSERT_ANY_THROW( adapter.getObject() ); 30 | ASSERT_ANY_THROW( adapter.getString() ); 31 | 32 | // Ensure that the array contains the expected number of elements 33 | EXPECT_EQ( numElements, adapter.getArray().size() ); 34 | 35 | // Ensure that the elements are returned in the order they were inserted 36 | unsigned int expectedValue = 0; 37 | for (const valijson::adapters::PocoJsonAdapter value : adapter.getArray()) { 38 | ASSERT_TRUE( value.isNumber() ); 39 | EXPECT_EQ( double(expectedValue), value.getDouble() ); 40 | expectedValue++; 41 | } 42 | 43 | // Ensure that the correct number of elements were iterated over 44 | EXPECT_EQ(numElements, expectedValue); 45 | } 46 | 47 | TEST_F(TestPocoJsonAdapter, BasicObjectIteration) 48 | { 49 | const unsigned int numElements = 10; 50 | 51 | // Create a DropBoxJson document that consists of an object that maps numeric 52 | // strings their corresponding numeric values 53 | Poco::JSON::Object::Ptr documentImpl = new Poco::JSON::Object; 54 | 55 | for (uint32_t i = 0; i < numElements; i++) { 56 | documentImpl->set(std::to_string(i), static_cast(i)); 57 | } 58 | 59 | Poco::Dynamic::Var document = documentImpl; 60 | 61 | // Ensure that wrapping the document preserves the object and does not 62 | // allow it to be cast to other types 63 | valijson::adapters::PocoJsonAdapter adapter(document); 64 | ASSERT_NO_THROW( adapter.getObject() ); 65 | ASSERT_ANY_THROW( adapter.getArray() ); 66 | ASSERT_ANY_THROW( adapter.getBool() ); 67 | ASSERT_ANY_THROW( adapter.getDouble() ); 68 | ASSERT_ANY_THROW( adapter.getString() ); 69 | 70 | // Ensure that the object contains the expected number of members 71 | EXPECT_EQ( numElements, adapter.getObject().size() ); 72 | 73 | // Ensure that the members are returned in the order they were inserted 74 | unsigned int expectedValue = 0; 75 | for (const valijson::adapters::PocoJsonAdapter::ObjectMember member : adapter.getObject()) { 76 | ASSERT_TRUE( member.second.isNumber() ); 77 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 78 | EXPECT_EQ( double(expectedValue), member.second.getDouble() ); 79 | expectedValue++; 80 | } 81 | 82 | // Ensure that the correct number of elements were iterated over 83 | EXPECT_EQ( numElements, expectedValue ); 84 | } 85 | -------------------------------------------------------------------------------- /tests/test_poly_constraint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace { 11 | 12 | using valijson::adapters::Adapter; 13 | using valijson::Schema; 14 | using valijson::Validator; 15 | using valijson::ValidationResults; 16 | using valijson::adapters::RapidJsonAdapter; 17 | 18 | class StubPolyConstraint : public valijson::constraints::PolyConstraint 19 | { 20 | bool m_shouldValidate; 21 | 22 | public: 23 | explicit StubPolyConstraint(bool shouldValidate) 24 | : m_shouldValidate(shouldValidate) { } 25 | 26 | Constraint * cloneInto(void *ptr) const override 27 | { 28 | return new (ptr) StubPolyConstraint(m_shouldValidate); 29 | } 30 | 31 | size_t sizeOf() const override 32 | { 33 | return sizeof(StubPolyConstraint); 34 | } 35 | 36 | bool validate( 37 | const Adapter &, 38 | const std::vector &context, 39 | ValidationResults *results) const override 40 | { 41 | if (m_shouldValidate) { 42 | return true; 43 | } 44 | 45 | if (results) { 46 | results->pushError(context, 47 | "StubPolyConstraint intentionally failed validation"); 48 | } 49 | 50 | return false; 51 | } 52 | }; 53 | 54 | } // end anonymous namespace 55 | 56 | class TestPolyConstraint : public testing::Test 57 | { 58 | 59 | }; 60 | 61 | TEST_F(TestPolyConstraint, ValidationCanPass) 62 | { 63 | StubPolyConstraint stubPolyConstraint(true); 64 | 65 | Schema schema; 66 | schema.addConstraintToSubschema(stubPolyConstraint, schema.root()); 67 | 68 | rapidjson::Document document; 69 | RapidJsonAdapter adapter(document); 70 | 71 | ValidationResults results; 72 | 73 | Validator validator; 74 | EXPECT_TRUE(validator.validate(schema, adapter, &results)); 75 | EXPECT_EQ(size_t(0), results.numErrors()); 76 | } 77 | 78 | TEST_F(TestPolyConstraint, ValidationCanFail) 79 | { 80 | StubPolyConstraint stubPolyConstraint(false); 81 | 82 | Schema schema; 83 | schema.addConstraintToSubschema(stubPolyConstraint, schema.root()); 84 | 85 | rapidjson::Document document; 86 | RapidJsonAdapter adapter(document); 87 | 88 | ValidationResults results; 89 | 90 | Validator validator; 91 | EXPECT_FALSE(validator.validate(schema, adapter, &results)); 92 | EXPECT_EQ(size_t(1), results.numErrors()); 93 | 94 | ValidationResults::Error error; 95 | EXPECT_TRUE(results.popError(error)); 96 | EXPECT_STREQ("StubPolyConstraint intentionally failed validation", error.description.c_str()); 97 | } 98 | -------------------------------------------------------------------------------- /tests/test_property_tree_adapter.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | class TestPropertyTreeAdapter : public testing::Test 7 | { 8 | 9 | }; 10 | 11 | TEST_F(TestPropertyTreeAdapter, BasicArrayIteration) 12 | { 13 | const unsigned int numElements = 10; 14 | 15 | // Create a boost property tree that is equivalent to a JSON array containing a 16 | // list of numbers. 17 | boost::property_tree::ptree document; 18 | for (unsigned int i = 0; i < numElements; i++) { 19 | document.push_back(std::make_pair(std::string(), 20 | boost::property_tree::ptree(std::to_string(i)))); 21 | } 22 | 23 | // Ensure that wrapping the document preserves the array and does not allow 24 | // it to be cast to other types 25 | valijson::adapters::PropertyTreeAdapter adapter(document); 26 | #if VALIJSON_USE_EXCEPTIONS 27 | ASSERT_NO_THROW( adapter.getArray() ); 28 | ASSERT_ANY_THROW( adapter.getBool() ); 29 | ASSERT_ANY_THROW( adapter.getDouble() ); 30 | ASSERT_ANY_THROW( adapter.getObject() ); 31 | ASSERT_ANY_THROW( adapter.getString() ); 32 | #endif 33 | 34 | // Ensure that the array contains the expected number of elements 35 | EXPECT_EQ( numElements, adapter.getArray().size() ); 36 | 37 | // Ensure that the elements are returned in the order they were inserted 38 | unsigned int expectedValue = 0; 39 | for (const valijson::adapters::PropertyTreeAdapter value : adapter.getArray()) { 40 | ASSERT_TRUE( value.isString() ); 41 | ASSERT_FALSE( value.isNumber() ); 42 | ASSERT_TRUE( value.maybeDouble() ); 43 | EXPECT_EQ( double(expectedValue), value.asDouble() ); 44 | expectedValue++; 45 | } 46 | 47 | // Ensure that the correct number of elements were iterated over 48 | EXPECT_EQ(numElements, expectedValue); 49 | } 50 | 51 | TEST_F(TestPropertyTreeAdapter, BasicObjectIteration) 52 | { 53 | const unsigned int numElements = 10; 54 | 55 | // Create a boost property tree that consists of an object that maps numeric 56 | // strings their corresponding numeric values 57 | boost::property_tree::ptree document; 58 | for (unsigned int i = 0; i < numElements; i++) { 59 | std::string name(std::to_string(i)); 60 | document.push_back(std::make_pair(name, boost::property_tree::ptree( 61 | std::to_string(double(i))))); 62 | } 63 | 64 | // Ensure that wrapping the document preserves the object and does not 65 | // allow it to be cast to other types 66 | valijson::adapters::PropertyTreeAdapter adapter(document); 67 | #if VALIJSON_USE_EXCEPTIONS 68 | ASSERT_NO_THROW( adapter.getObject() ); 69 | ASSERT_ANY_THROW( adapter.getArray() ); 70 | ASSERT_ANY_THROW( adapter.getBool() ); 71 | ASSERT_ANY_THROW( adapter.getDouble() ); 72 | ASSERT_ANY_THROW( adapter.getString() ); 73 | #endif 74 | 75 | // Ensure that the object contains the expected number of members 76 | EXPECT_EQ( numElements, adapter.getObject().size() ); 77 | 78 | // Ensure that the members are returned in the order they were inserted 79 | unsigned int expectedValue = 0; 80 | for (const valijson::adapters::PropertyTreeAdapter::ObjectMember member : adapter.getObject()) { 81 | ASSERT_TRUE( member.second.isString() ); 82 | ASSERT_FALSE( member.second.isNumber() ); 83 | ASSERT_TRUE( member.second.maybeDouble() ); 84 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 85 | EXPECT_EQ( double(expectedValue), member.second.asDouble() ); 86 | expectedValue++; 87 | } 88 | 89 | // Ensure that the correct number of elements were iterated over 90 | EXPECT_EQ( numElements, expectedValue ); 91 | } 92 | -------------------------------------------------------------------------------- /tests/test_qtjson_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | class TestQtJsonAdapter : public testing::Test 8 | { 9 | 10 | }; 11 | 12 | TEST_F(TestQtJsonAdapter, BasicArrayIteration) 13 | { 14 | const unsigned int numElements = 10; 15 | 16 | // Create a picojson document that consists of an array of numbers 17 | QJsonArray array; 18 | for (unsigned int i = 0; i < numElements; i++) { 19 | QJsonValue value(static_cast(i)); 20 | array.push_back(value); 21 | } 22 | QJsonValue document(array); 23 | 24 | // Ensure that wrapping the document preserves the array and does not allow 25 | // it to be cast to other types 26 | valijson::adapters::QtJsonAdapter adapter(document); 27 | ASSERT_NO_THROW( adapter.getArray() ); 28 | ASSERT_ANY_THROW( adapter.getBool() ); 29 | ASSERT_ANY_THROW( adapter.getDouble() ); 30 | ASSERT_ANY_THROW( adapter.getObject() ); 31 | ASSERT_ANY_THROW( adapter.getString() ); 32 | 33 | // Ensure that the array contains the expected number of elements 34 | EXPECT_EQ( numElements, adapter.getArray().size() ); 35 | 36 | // Ensure that the elements are returned in the order they were inserted 37 | unsigned int expectedValue = 0; 38 | for (const valijson::adapters::QtJsonAdapter value : adapter.getArray()) { 39 | ASSERT_TRUE( value.isNumber() ); 40 | EXPECT_EQ( double(expectedValue), value.getDouble() ); 41 | expectedValue++; 42 | } 43 | 44 | // Ensure that the correct number of elements were iterated over 45 | EXPECT_EQ(numElements, expectedValue); 46 | } 47 | 48 | TEST_F(TestQtJsonAdapter, BasicObjectIteration) 49 | { 50 | const unsigned int numElements = 10; 51 | 52 | // Create a picojson document that consists of an object that maps numeric 53 | // strings their corresponding numeric values 54 | QJsonObject object; 55 | for (unsigned int i = 0; i < numElements; i++) { 56 | QString name(QString::number(i)); 57 | object[name] = QJsonValue(static_cast(i)); 58 | } 59 | QJsonValue document(object); 60 | 61 | // Ensure that wrapping the document preserves the object and does not 62 | // allow it to be cast to other types 63 | valijson::adapters::QtJsonAdapter adapter(document); 64 | ASSERT_NO_THROW( adapter.getObject() ); 65 | ASSERT_ANY_THROW( adapter.getArray() ); 66 | ASSERT_ANY_THROW( adapter.getBool() ); 67 | ASSERT_ANY_THROW( adapter.getDouble() ); 68 | ASSERT_ANY_THROW( adapter.getString() ); 69 | 70 | // Ensure that the object contains the expected number of members 71 | EXPECT_EQ( numElements, adapter.getObject().size() ); 72 | 73 | // Ensure that the members are returned in the order they were inserted 74 | unsigned int expectedValue = 0; 75 | for (const valijson::adapters::QtJsonAdapter::ObjectMember member : adapter.getObject()) { 76 | ASSERT_TRUE( member.second.isNumber() ); 77 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 78 | EXPECT_EQ( double(expectedValue), member.second.getDouble() ); 79 | expectedValue++; 80 | } 81 | 82 | // Ensure that the correct number of elements were iterated over 83 | EXPECT_EQ( numElements, expectedValue ); 84 | } 85 | -------------------------------------------------------------------------------- /tests/test_rapidjson_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | class TestRapidJsonAdapter : public testing::Test 8 | { 9 | 10 | }; 11 | 12 | template 13 | void testBasicArrayIteration() 14 | { 15 | const unsigned int numElements = 10; 16 | 17 | // Create a rapidjson document that consists of an array of numbers 18 | rapidjson::Document document; 19 | document.SetArray(); 20 | for (unsigned int i = 0; i < numElements; i++) { 21 | rapidjson::Value value; 22 | value.SetDouble(i); 23 | document.PushBack(value, document.GetAllocator()); 24 | } 25 | 26 | // Ensure that wrapping the document preserves the array and does not allow 27 | // it to be cast to other types 28 | valijson::adapters::RapidJsonAdapter adapter(document); 29 | #if VALIJSON_USE_EXCEPTIONS 30 | ASSERT_NO_THROW( adapter.getArray() ); 31 | ASSERT_ANY_THROW( adapter.getBool() ); 32 | ASSERT_ANY_THROW( adapter.getDouble() ); 33 | ASSERT_ANY_THROW( adapter.getObject() ); 34 | ASSERT_ANY_THROW( adapter.getString() ); 35 | #endif 36 | 37 | // Ensure that the array contains the expected number of elements 38 | EXPECT_EQ( numElements, adapter.getArray().size() ); 39 | 40 | // Ensure that the elements are returned in the order they were inserted 41 | unsigned int expectedValue = 0; 42 | for (const valijson::adapters::RapidJsonAdapter value : adapter.getArray()) { 43 | ASSERT_TRUE( value.isNumber() ); 44 | EXPECT_EQ( double(expectedValue), value.getDouble() ); 45 | expectedValue++; 46 | } 47 | 48 | // Check that plain iterators work too 49 | expectedValue = 0; 50 | for (auto itr = adapter.getArray().begin(); itr != adapter.getArray().end(); itr++) { 51 | ASSERT_TRUE( itr->isNumber() ); 52 | EXPECT_EQ( double(expectedValue), itr->getDouble() ); 53 | expectedValue++; 54 | } 55 | 56 | // Check difference 57 | auto a = adapter.getArray().begin(); 58 | auto b = a; 59 | const auto c = b.difference(a); 60 | EXPECT_EQ( 0, c ); 61 | b++; 62 | const auto d = a.difference(b); 63 | EXPECT_EQ( 1, d ); 64 | 65 | // Ensure that the correct number of elements were iterated over 66 | EXPECT_EQ(numElements, expectedValue); 67 | } 68 | 69 | template 70 | void testBasicObjectIteration() 71 | { 72 | const unsigned int numElements = 10; 73 | 74 | // Create a rapidjson document that consists of an object that maps numeric 75 | // strings their corresponding numeric values 76 | rapidjson::Document document; 77 | document.SetObject(); 78 | for (unsigned int i = 0; i < numElements; i++) { 79 | rapidjson::Value name, value; 80 | name.SetString(std::to_string(i).c_str(), document.GetAllocator()); 81 | value.SetDouble(i); 82 | document.AddMember(name, value, document.GetAllocator()); 83 | } 84 | 85 | // Ensure that wrapping the document preserves the object and does not 86 | // allow it to be cast to other types 87 | valijson::adapters::RapidJsonAdapter adapter(document); 88 | #if VALIJSON_USE_EXCEPTIONS 89 | ASSERT_NO_THROW( adapter.getObject() ); 90 | ASSERT_ANY_THROW( adapter.getArray() ); 91 | ASSERT_ANY_THROW( adapter.getBool() ); 92 | ASSERT_ANY_THROW( adapter.getDouble() ); 93 | ASSERT_ANY_THROW( adapter.getString() ); 94 | #endif 95 | 96 | // Ensure that the object contains the expected number of members 97 | EXPECT_EQ( numElements, adapter.getObject().size() ); 98 | 99 | // Ensure that the members are returned in the order they were inserted 100 | unsigned int expectedValue = 0; 101 | for (const valijson::adapters::RapidJsonAdapter::ObjectMember member : adapter.getObject()) { 102 | ASSERT_TRUE( member.second.isNumber() ); 103 | EXPECT_EQ( std::to_string(expectedValue), member.first ); 104 | EXPECT_EQ( double(expectedValue), member.second.getDouble() ); 105 | expectedValue++; 106 | } 107 | 108 | // Check difference 109 | auto a = adapter.getObject().begin(); 110 | auto b = a; 111 | const auto c = b.difference(a); 112 | EXPECT_EQ( 0, c ); 113 | b++; 114 | const auto d = a.difference(b); 115 | EXPECT_EQ( 1, d ); 116 | 117 | // Ensure that the correct number of elements were iterated over 118 | EXPECT_EQ( numElements, expectedValue ); 119 | } 120 | 121 | TEST_F(TestRapidJsonAdapter, BasicArrayIteration) 122 | { 123 | // Test using default RapidJson value type, which uses MemoryPoolAllocator 124 | testBasicArrayIteration(); 125 | 126 | // Test using value type based on CrtAllocator 127 | testBasicArrayIteration, 128 | rapidjson::CrtAllocator> >(); 129 | } 130 | 131 | TEST_F(TestRapidJsonAdapter, BasicObjectIteration) 132 | { 133 | // Test using default RapidJson value type, which uses MemoryPoolAllocator 134 | testBasicObjectIteration(); 135 | 136 | // Test using value type based on CrtAllocator 137 | testBasicObjectIteration, 138 | rapidjson::CrtAllocator> >(); 139 | } 140 | -------------------------------------------------------------------------------- /tests/test_utf8_utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class TestUtf8Utils : public testing::Test 5 | { 6 | }; 7 | 8 | TEST_F(TestUtf8Utils, Utf8StringLength) 9 | { 10 | using valijson::utils::u8_strlen; 11 | 12 | EXPECT_EQ(u8_strlen(""), 0); 13 | EXPECT_EQ(u8_strlen("a"), 1); 14 | EXPECT_EQ(u8_strlen("abc"), 3); 15 | 16 | // U+0416 17 | EXPECT_EQ(u8_strlen("\xD0\x96"), 1); 18 | 19 | // U+0915 20 | EXPECT_EQ(u8_strlen("\xE0\xA4\x95"), 1); 21 | 22 | // U+10348 23 | EXPECT_EQ(u8_strlen("\xF0\x90\x8D\x88"), 1); 24 | 25 | // U+0915 + U+0416 26 | EXPECT_EQ(u8_strlen("\xE0\xA4\x95\xD0\x96"), 2); 27 | 28 | // incomplete U+0416 at the end 29 | EXPECT_EQ(u8_strlen("\xD0"), 1); 30 | 31 | // incomplete U+0416 in the middle 32 | EXPECT_EQ(u8_strlen("\320abc"), 4); 33 | 34 | // incomplete U+0915 at the end 35 | EXPECT_EQ(u8_strlen("\xE0\xA4"), 1); 36 | 37 | // incomplete U+0915 at the end 38 | EXPECT_EQ(u8_strlen("\xE0\244abc"), 4); 39 | 40 | // U+DFFF 41 | EXPECT_EQ(u8_strlen("\xED\xBF\xBF"), 1); 42 | 43 | // Overlong encoding for U+0000 44 | EXPECT_EQ(u8_strlen("\xC0\x80"), 1); 45 | 46 | // U+110000 (out of Unicode range) 47 | EXPECT_EQ(u8_strlen("\xF5\x80\x80\x80"), 1); 48 | 49 | // 0xE0 + 0xA4 repeating 9 times 50 | EXPECT_EQ(u8_strlen("\340\244\244\244\244\244\244\244\244\244"), 5); 51 | } 52 | -------------------------------------------------------------------------------- /tests/test_validation_errors.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define TEST_DATA_DIR "../tests/data" 13 | 14 | using std::string; 15 | 16 | using valijson::adapters::AdapterTraits; 17 | using valijson::adapters::RapidJsonAdapter; 18 | using valijson::utils::loadDocument; 19 | using valijson::Schema; 20 | using valijson::SchemaParser; 21 | using valijson::Validator; 22 | using valijson::ValidationResults; 23 | 24 | class TestValidationErrors : public ::testing::Test 25 | { 26 | 27 | }; 28 | 29 | TEST_F(TestValidationErrors, AllOfConstraintFailure) 30 | { 31 | // Load schema document 32 | rapidjson::Document schemaDocument; 33 | ASSERT_TRUE( loadDocument(TEST_DATA_DIR "/schemas/allof_integers_and_numbers.schema.json", schemaDocument) ); 34 | RapidJsonAdapter schemaAdapter(schemaDocument); 35 | 36 | // Parse schema document 37 | Schema schema; 38 | SchemaParser schemaParser; 39 | #if VALIJSON_USE_EXCEPTIONS 40 | ASSERT_NO_THROW(schemaParser.populateSchema(schemaAdapter, schema)); 41 | #else 42 | schemaParser.populateSchema(schemaAdapter, schema); 43 | #endif 44 | 45 | // Load test document 46 | rapidjson::Document testDocument; 47 | ASSERT_TRUE( loadDocument(TEST_DATA_DIR "/documents/array_doubles_1_2_3.json", testDocument) ); 48 | RapidJsonAdapter testAdapter(testDocument); 49 | 50 | Validator validator; 51 | ValidationResults results; 52 | EXPECT_FALSE( validator.validate(schema, testAdapter, &results) ); 53 | 54 | ValidationResults::Error error; 55 | 56 | EXPECT_TRUE( results.popError(error) ); 57 | EXPECT_EQ( size_t(2), error.context.size() ); 58 | EXPECT_EQ( "", error.context[0] ); 59 | EXPECT_EQ( "[0]", error.context[1] ); 60 | EXPECT_EQ( "Value type not permitted by 'type' constraint.", error.description ); 61 | 62 | EXPECT_TRUE( results.popError(error) ); 63 | EXPECT_EQ( size_t(1), error.context.size() ); 64 | EXPECT_EQ( "", error.context[0] ); 65 | EXPECT_EQ( "Failed to validate item #0 in array.", error.description ); 66 | 67 | EXPECT_TRUE( results.popError(error) ); 68 | EXPECT_EQ( size_t(2), error.context.size() ); 69 | EXPECT_EQ( "", error.context[0] ); 70 | EXPECT_EQ( "[1]", error.context[1] ); 71 | EXPECT_EQ( "Value type not permitted by 'type' constraint.", error.description ); 72 | 73 | EXPECT_TRUE( results.popError(error) ); 74 | EXPECT_EQ( size_t(1), error.context.size() ); 75 | EXPECT_EQ( "", error.context[0] ); 76 | EXPECT_EQ( "Failed to validate item #1 in array.", error.description ); 77 | 78 | EXPECT_TRUE( results.popError(error) ); 79 | EXPECT_EQ( size_t(2), error.context.size() ); 80 | EXPECT_EQ( "", error.context[0] ); 81 | EXPECT_EQ( "[2]", error.context[1] ); 82 | EXPECT_EQ( "Value type not permitted by 'type' constraint.", error.description ); 83 | 84 | EXPECT_TRUE( results.popError(error) ); 85 | EXPECT_EQ( size_t(1), error.context.size() ); 86 | EXPECT_EQ( "", error.context[0] ); 87 | EXPECT_EQ( "Failed to validate item #2 in array.", error.description ); 88 | 89 | EXPECT_TRUE( results.popError(error) ); 90 | EXPECT_EQ( size_t(1), error.context.size() ); 91 | EXPECT_EQ( "", error.context[0] ); 92 | EXPECT_EQ( "Failed to validate against child schema #0.", error.description ); 93 | 94 | EXPECT_FALSE( results.popError(error) ); 95 | 96 | while (results.popError(error)) { 97 | //std::cerr << error.context << std::endl; 98 | std::cerr << error.description << std::endl; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tests/test_validator_with_custom_regular_expression_engine.cpp: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | #pragma warning(disable: 4706) 3 | #include 4 | #pragma warning(default: 4706) 5 | #else 6 | #include 7 | #endif 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #ifdef VALIJSON_BUILD_POCO_ADAPTER 29 | #include 30 | #include 31 | #endif 32 | 33 | using valijson::adapters::AdapterTraits; 34 | using valijson::adapters::RapidJsonAdapter; 35 | using valijson::Schema; 36 | using valijson::SchemaParser; 37 | using valijson::Validator; 38 | 39 | static void createFileFromContent(const std::string& filename, const std::string& content) 40 | { 41 | std::ofstream outfile(filename, std::ofstream::out | std::ofstream::trunc); 42 | outfile << content << std::endl; 43 | outfile.close(); 44 | }; 45 | 46 | //Potentially : 47 | // Define a struct CustomRegexEngine that handle both problem and use it as replacement of Validator. 48 | //using CustomValidator = ValidatorT; 49 | 50 | TEST(valijson, valijson_be_robust_against_bad_regular_expression) 51 | { 52 | GTEST_SKIP() << "Skipping: causes segmentation fault with default Validator"; 53 | 54 | static const std::string schema = R"( 55 | { 56 | "properties": { 57 | "text": { 58 | "pattern": "^[\\s\\S]+$", 59 | "type": "string" 60 | } 61 | } 62 | } 63 | )"; 64 | 65 | createFileFromContent("schema.json", schema); 66 | rapidjson::Document mySchemaDoc; 67 | ASSERT_TRUE(valijson::utils::loadDocument("schema.json", mySchemaDoc)); 68 | 69 | Schema mySchema; 70 | SchemaParser parser; 71 | RapidJsonAdapter mySchemaAdapter(mySchemaDoc); 72 | parser.populateSchema(mySchemaAdapter, mySchema); 73 | rapidjson::Document myTargetDoc; 74 | 75 | std::string payload = "{ \"text\" : \""; 76 | for (int i = 0; i< 100000; ++i) { 77 | payload += 'A'; 78 | } 79 | payload += "\"}"; 80 | 81 | createFileFromContent("payload.json", payload); 82 | 83 | ASSERT_TRUE(valijson::utils::loadDocument("payload.json", myTargetDoc)); 84 | 85 | // This test crash (segfault) is validator is not customized with custom RegexpEngine 86 | Validator validator; 87 | RapidJsonAdapter myTargetAdapter(myTargetDoc); 88 | ASSERT_TRUE(validator.validate(mySchema, myTargetAdapter, nullptr)); 89 | } 90 | 91 | TEST(valijson, valijson_be_robust_against_catastrophic_backtracking_regular_expression) 92 | { 93 | GTEST_SKIP() << "Skipping: hangs due to non management of catastrophic backtracking with default Validator"; 94 | 95 | static const std::string schema = R"( 96 | { 97 | "properties": { 98 | "text": { 99 | "pattern": "((A+)*)+$", 100 | "type": "string" 101 | } 102 | } 103 | } 104 | )"; 105 | 106 | createFileFromContent("schema.json", schema); 107 | rapidjson::Document mySchemaDoc; 108 | ASSERT_TRUE(valijson::utils::loadDocument("schema.json", mySchemaDoc)); 109 | 110 | Schema mySchema; 111 | SchemaParser parser; 112 | RapidJsonAdapter mySchemaAdapter(mySchemaDoc); 113 | parser.populateSchema(mySchemaAdapter, mySchema); 114 | rapidjson::Document myTargetDoc; 115 | 116 | std::string payload = "{ \"text\" : \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC\"}"; 117 | createFileFromContent("payload.json", payload); 118 | 119 | ASSERT_TRUE(valijson::utils::loadDocument("payload.json", myTargetDoc)); 120 | 121 | //This test takes endless time if validator is not customized with custom RegexpEngine 122 | Validator validator; 123 | RapidJsonAdapter myTargetAdapter(myTargetDoc); 124 | 125 | //payload is correct regarding the regexp but evaluation is impossible due to catastrophic regexp backtracking. so we return false. 126 | ASSERT_FALSE(validator.validate(mySchema, myTargetAdapter, nullptr)); 127 | } 128 | -------------------------------------------------------------------------------- /tests/test_yaml_cpp_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | class TestYamlCppAdapter : public testing::Test 6 | { 7 | }; 8 | 9 | TEST_F(TestYamlCppAdapter, BasicArrayIteration) 10 | { 11 | const unsigned int numElements = 10; 12 | 13 | // Create a Json document that consists of an array of numbers 14 | YAML::Node document; 15 | 16 | for (unsigned int i = 0; i < numElements; i++) { 17 | document.push_back(static_cast(i)); 18 | } 19 | 20 | // Ensure that wrapping the document preserves the array and does not allow 21 | // it to be cast to other types 22 | valijson::adapters::YamlCppAdapter adapter(document); 23 | #if VALIJSON_USE_EXCEPTIONS 24 | ASSERT_NO_THROW(adapter.getArray()); 25 | ASSERT_ANY_THROW(adapter.getBool()); 26 | ASSERT_ANY_THROW(adapter.getDouble()); 27 | ASSERT_ANY_THROW(adapter.getObject()); 28 | ASSERT_ANY_THROW(adapter.getString()); 29 | #endif 30 | 31 | // Ensure that the array contains the expected number of elements 32 | EXPECT_EQ(numElements, adapter.getArray().size()); 33 | 34 | // Ensure that the elements are returned in the order they were inserted 35 | unsigned int expectedValue = 0; 36 | for (const valijson::adapters::YamlCppAdapter value : adapter.getArray()) { 37 | ASSERT_TRUE(value.isString()); 38 | ASSERT_FALSE(value.isNumber()); 39 | ASSERT_TRUE(value.maybeDouble()); 40 | EXPECT_EQ(double(expectedValue), value.getDouble()); 41 | expectedValue++; 42 | } 43 | 44 | // Ensure that the correct number of elements were iterated over 45 | EXPECT_EQ(numElements, expectedValue); 46 | } 47 | 48 | TEST_F(TestYamlCppAdapter, BasicObjectIteration) 49 | { 50 | const unsigned int numElements = 10; 51 | 52 | // Create a document that consists of an object that maps 53 | // numeric strings their corresponding numeric values 54 | YAML::Node document; 55 | for (uint32_t i = 0; i < numElements; i++) { 56 | document[std::to_string(i)] = static_cast(i); 57 | } 58 | 59 | // Ensure that wrapping the document preserves the object and does not 60 | // allow it to be cast to other types 61 | valijson::adapters::YamlCppAdapter adapter(document); 62 | #if VALIJSON_USE_EXCEPTIONS 63 | ASSERT_NO_THROW(adapter.getObject()); 64 | ASSERT_ANY_THROW(adapter.getArray()); 65 | ASSERT_ANY_THROW(adapter.getBool()); 66 | ASSERT_ANY_THROW(adapter.getDouble()); 67 | ASSERT_ANY_THROW(adapter.getString()); 68 | #endif 69 | 70 | // Ensure that the object contains the expected number of members 71 | EXPECT_EQ(numElements, adapter.getObject().size()); 72 | 73 | // Ensure that the members are returned in the order they were inserted 74 | unsigned int expectedValue = 0; 75 | for (const valijson::adapters::YamlCppAdapter::ObjectMember member : 76 | adapter.getObject()) { 77 | ASSERT_TRUE(member.second.isString()); 78 | ASSERT_FALSE(member.second.isNumber()); 79 | ASSERT_TRUE(member.second.maybeDouble()); 80 | EXPECT_EQ(std::to_string(expectedValue), member.first); 81 | EXPECT_EQ(double(expectedValue), member.second.getDouble()); 82 | expectedValue++; 83 | } 84 | 85 | // Ensure that the correct number of elements were iterated over 86 | EXPECT_EQ(numElements, expectedValue); 87 | } 88 | 89 | TEST_F(TestYamlCppAdapter, BasicObjectMemberAccess) 90 | { 91 | const unsigned int numElements = 10; 92 | 93 | // Create a document that consists of an object that maps 94 | // numeric strings their corresponding numeric values 95 | YAML::Node document; 96 | for (uint32_t i = 0; i < numElements; i++) { 97 | document[std::to_string(i)] = static_cast(i); 98 | } 99 | valijson::adapters::YamlCppAdapter adapter(document); 100 | const auto adapterObject = adapter.asObject(); 101 | 102 | // Ensure that accessing an element that exists produces the expected result. 103 | const auto result3 = adapterObject.find("3"); 104 | EXPECT_NE(result3, adapterObject.end()); 105 | EXPECT_EQ(result3->second.asDouble(), 3); 106 | 107 | // Ensure that accessing an element that does not exists. 108 | const auto result12 = adapterObject.find("12"); 109 | EXPECT_EQ(result12, adapterObject.end()); 110 | } 111 | --------------------------------------------------------------------------------