├── .distro ├── .fmf │ └── version ├── tests │ ├── import │ │ ├── test_FetchContent.sh │ │ ├── test_find_package.sh │ │ ├── main.fmf │ │ ├── find_package │ │ │ └── CMakeLists.txt │ │ └── FetchContent │ │ │ └── CMakeLists.txt │ └── smoke.fmf ├── plans │ ├── smoke.fmf │ ├── import.fmf │ └── main.fmf.dist-git └── json-schema-validator.spec ├── test ├── issue-54 │ ├── instance.json │ ├── schema.json │ └── CMakeLists.txt ├── issue-48 │ ├── instance.json │ ├── schema.json │ └── CMakeLists.txt ├── issue-101 │ ├── instance.json │ ├── CMakeLists.txt │ └── schema.json ├── issue-75 │ ├── instance.json │ ├── schema.json │ ├── CMakeLists.txt │ └── TypeId.json ├── issue-27 │ ├── README │ ├── instance.json │ ├── schema.json │ └── CMakeLists.txt ├── issue-96 │ ├── instance.json │ ├── CMakeLists.txt │ └── schema.json ├── issue-9 │ ├── instance.json │ ├── foo │ │ ├── baz │ │ │ ├── baz.json │ │ │ └── qux │ │ │ │ └── qux.json │ │ └── foo.json │ ├── CMakeLists.txt │ ├── base.json │ └── bar.json ├── issue-12 │ ├── instance.json │ ├── CMakeLists.txt │ └── schema.json ├── issue-100 │ ├── instance.json │ ├── CMakeLists.txt │ └── schema.json ├── issue-143 │ ├── instance.json │ ├── instance-fail-1.json │ ├── instance-fail-a.json │ ├── schema.json │ └── CMakeLists.txt ├── JSON-Schema-Test-Suite │ ├── remotes │ │ ├── integer.json │ │ ├── folder │ │ │ └── folderInteger.json │ │ ├── baseUriChange │ │ │ └── folderInteger.json │ │ ├── baseUriChangeFolder │ │ │ └── folderInteger.json │ │ ├── baseUriChangeFolderInSubschema │ │ │ └── folderInteger.json │ │ ├── subSchemas.json │ │ ├── subSchemas-defs.json │ │ ├── ref-and-defs.json │ │ ├── ref-and-definitions.json │ │ ├── name-defs.json │ │ └── name.json │ ├── tests │ │ └── draft7 │ │ │ ├── optional │ │ │ ├── float-overflow.json │ │ │ ├── format │ │ │ │ ├── regex.json │ │ │ │ ├── idn-email.json │ │ │ │ ├── uri-template.json │ │ │ │ ├── iri-reference.json │ │ │ │ ├── uri-reference.json │ │ │ │ ├── relative-json-pointer.json │ │ │ │ ├── email.json │ │ │ │ ├── iri.json │ │ │ │ ├── ipv4.json │ │ │ │ ├── hostname.json │ │ │ │ ├── date-time.json │ │ │ │ ├── uuid.json │ │ │ │ └── uri.json │ │ │ ├── content.json │ │ │ ├── non-bmp-regex.json │ │ │ └── bignum.json │ │ │ ├── minItems.json │ │ │ ├── maxItems.json │ │ │ ├── definitions.json │ │ │ ├── exclusiveMaximum.json │ │ │ ├── exclusiveMinimum.json │ │ │ ├── minLength.json │ │ │ ├── maxLength.json │ │ │ ├── infinite-loop-detection.json │ │ │ ├── minProperties.json │ │ │ ├── maximum.json │ │ │ ├── maxProperties.json │ │ │ ├── pattern.json │ │ │ ├── id.json │ │ │ ├── minimum.json │ │ │ ├── multipleOf.json │ │ │ ├── unknownKeyword.json │ │ │ ├── default.json │ │ │ ├── required.json │ │ │ ├── boolean_schema.json │ │ │ ├── not.json │ │ │ ├── propertyNames.json │ │ │ ├── additionalProperties.json │ │ │ ├── contains.json │ │ │ ├── additionalItems.json │ │ │ └── patternProperties.json │ ├── CMakeLists.txt │ └── json-schema-test.cpp ├── issue-311 │ ├── instance.json │ ├── CMakeLists.txt │ └── schema.json ├── issue-93 │ ├── types │ │ └── color.schema.json │ ├── CMakeLists.txt │ ├── blueprints.schema.json │ ├── components.schema.json │ └── issue-93.cpp ├── issue-209 │ ├── CMakeLists.txt │ ├── instance.json │ ├── color.schema.json │ └── entities.schema.json ├── test-pipe-in.sh ├── issue-98.cpp ├── issue-70.cpp ├── issue-255-error-message-limit-precision.cpp ├── issue-293.cpp ├── issue-243-root-default-values.cpp ├── issue-229-oneof-default-values.cpp ├── json-patch.cpp ├── issue-149-entry-selection.cpp ├── issue-117-format-error.cpp ├── json-schema-validate.cpp ├── issue-189-default-values.cpp ├── uri.cpp ├── id-ref.cpp ├── string-format-check-test.cpp ├── issue-70-root-schema-constructor.cpp ├── errors.cpp ├── CMakeLists.txt └── issue-25-default-values.cpp ├── .gitignore ├── CMakePresets.json ├── cmake ├── nlohmann_json_schema_validatorConfig.cmake.in └── CMakePresets-defaults.json ├── example ├── CMakeLists.txt ├── format.cpp └── readme.cpp ├── .clang-format ├── src ├── json-patch.hpp ├── smtp-address-validator.hpp ├── CMakeLists.txt ├── json-patch.cpp └── json-uri.cpp ├── .pre-commit-config.yaml ├── LICENSE ├── ChangeLog.md ├── .github └── workflows │ ├── release.yaml │ └── test.yaml ├── .packit.yaml └── conanfile.py /.distro/.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /test/issue-54/instance.json: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /test/issue-48/instance.json: -------------------------------------------------------------------------------- 1 | 1.2 2 | -------------------------------------------------------------------------------- /test/issue-101/instance.json: -------------------------------------------------------------------------------- 1 | {"top": 1} 2 | -------------------------------------------------------------------------------- /test/issue-75/instance.json: -------------------------------------------------------------------------------- 1 | ["INT", "LONG"] 2 | -------------------------------------------------------------------------------- /test/issue-27/README: -------------------------------------------------------------------------------- 1 | Numbers higher that UINT32_MAX 2 | -------------------------------------------------------------------------------- /test/issue-27/instance.json: -------------------------------------------------------------------------------- 1 | {"gps_time": 4294967296} 2 | -------------------------------------------------------------------------------- /test/issue-96/instance.json: -------------------------------------------------------------------------------- 1 | {"top": {"value": 101}} 2 | -------------------------------------------------------------------------------- /test/issue-48/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "multipleOf": 0.1 3 | } 4 | -------------------------------------------------------------------------------- /test/issue-9/instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "name" 3 | } 4 | -------------------------------------------------------------------------------- /test/issue-12/instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "x": 1503681668603 3 | } 4 | -------------------------------------------------------------------------------- /test/issue-100/instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "Prop1": 1, 3 | "Prop2": [] 4 | } 5 | -------------------------------------------------------------------------------- /test/issue-143/instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "ref1": 12, 3 | "refa": "a" 4 | } 5 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/integer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "integer" 3 | } 4 | -------------------------------------------------------------------------------- /test/issue-143/instance-fail-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "ref1": "a", 3 | "refa": "a" 4 | } 5 | -------------------------------------------------------------------------------- /test/issue-143/instance-fail-a.json: -------------------------------------------------------------------------------- 1 | { 2 | "ref1": 12, 3 | "refa": 12 4 | } 5 | -------------------------------------------------------------------------------- /test/issue-311/instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "element": [1], 3 | "element2": "test" 4 | } 5 | -------------------------------------------------------------------------------- /test/issue-54/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "integer", 3 | "minimum": -2 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | *.sw? 3 | cmake-build-* 4 | venv 5 | env 6 | compile_commands.json 7 | .vs/* 8 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/folder/folderInteger.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "integer" 3 | } 4 | -------------------------------------------------------------------------------- /test/issue-93/types/color.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"string", 3 | "default":"Black" 4 | } 5 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/baseUriChange/folderInteger.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "integer" 3 | } 4 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolder/folderInteger.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "integer" 3 | } 4 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolderInSubschema/folderInteger.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "integer" 3 | } 4 | -------------------------------------------------------------------------------- /.distro/tests/import/test_FetchContent.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | tmp_dir=$(mktemp -d) 4 | cmake -S ./FetchContent -B ${tmp_dir} 5 | -------------------------------------------------------------------------------- /.distro/tests/import/test_find_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | tmp_dir=$(mktemp -d) 4 | cmake -S ./find_package -B ${tmp_dir} 5 | -------------------------------------------------------------------------------- /test/issue-75/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "array", 3 | "items": { 4 | "$ref": "TypeId.json#/data/TypeId" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.distro/plans/smoke.fmf: -------------------------------------------------------------------------------- 1 | summary: 2 | Basic smoke tests 3 | discover+: 4 | how: fmf 5 | filter: "tag: smoke" 6 | execute: 7 | how: tmt 8 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "include": [ 4 | "cmake/CMakePresets-defaults.json", 5 | "cmake/CMakePresets-CI.json" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test/issue-9/foo/baz/baz.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "Describes baz", 4 | "$ref": "qux/qux.json" 5 | } 6 | -------------------------------------------------------------------------------- /test/issue-9/foo/foo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "Describes foo", 4 | "$ref": "baz/baz.json" 5 | } 6 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/subSchemas.json: -------------------------------------------------------------------------------- 1 | { 2 | "integer": { 3 | "type": "integer" 4 | }, 5 | "refToInteger": { 6 | "$ref": "#/integer" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/issue-9/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::9 2 | ${CMAKE_CURRENT_SOURCE_DIR}/base.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | -------------------------------------------------------------------------------- /.distro/tests/smoke.fmf: -------------------------------------------------------------------------------- 1 | # Common test variables 2 | tag: 3 | - smoke 4 | tier: 0 5 | path: / 6 | 7 | # Define tests 8 | /version: 9 | test: echo "TODO: Write a minimum working example" 10 | -------------------------------------------------------------------------------- /test/issue-12/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::12 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | -------------------------------------------------------------------------------- /test/issue-48/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::48 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | -------------------------------------------------------------------------------- /test/issue-54/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::54 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | -------------------------------------------------------------------------------- /test/issue-75/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::75 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | -------------------------------------------------------------------------------- /test/issue-311/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::311 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | -------------------------------------------------------------------------------- /test/issue-209/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::209 2 | ${CMAKE_CURRENT_SOURCE_DIR}/entities.schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | -------------------------------------------------------------------------------- /test/issue-209/instance.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "player", 4 | "fg": "White" 5 | }, 6 | { 7 | "name": "enemy", 8 | "fg": "Red" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /.distro/tests/import/main.fmf: -------------------------------------------------------------------------------- 1 | # Common test variables 2 | tag: 3 | - import 4 | tier: 0 5 | path: /tests/import 6 | 7 | # Define tests 8 | /find_package: 9 | test: ./test_find_package.sh 10 | /FetchContent: 11 | test: ./test_FetchContent.sh 12 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/subSchemas-defs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$defs": { 3 | "integer": { 4 | "type": "integer" 5 | }, 6 | "refToInteger": { 7 | "$ref": "#/$defs/integer" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/issue-9/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "Describes foo", 4 | "type": "object", 5 | "allOf": [ 6 | { "$ref": "bar.json" }, 7 | { "$ref": "foo/foo.json" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /test/issue-93/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(issue-93 issue-93.cpp) 3 | target_link_libraries(issue-93 nlohmann_json_schema_validator) 4 | 5 | add_test(NAME issue-93 6 | COMMAND issue-93 7 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 8 | -------------------------------------------------------------------------------- /.distro/plans/import.fmf: -------------------------------------------------------------------------------- 1 | summary: 2 | Basic importing tests 3 | prepare+: 4 | - name: Include minimum fetching packages 5 | how: install 6 | package: 7 | - git 8 | discover+: 9 | how: fmf 10 | filter: "tag: import" 11 | execute: 12 | how: tmt 13 | -------------------------------------------------------------------------------- /test/issue-12/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "properties": { 4 | "x": { 5 | "type": "integer", 6 | "minimum": 1000000000000, 7 | "maximum": 2000000000000 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/issue-93/blueprints.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"array", 3 | "items": { 4 | "type":"object", 5 | "properties": { 6 | "renderable": { 7 | "$ref":"/components.schema.json#/Renderable" 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/issue-27/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "gps_time": { 4 | "type": "number", 5 | "minimum": 0, 6 | "maximum": 4294967295 7 | } 8 | }, 9 | "required": [ 10 | "gps_time" 11 | ], 12 | "type": "object" 13 | } 14 | -------------------------------------------------------------------------------- /test/issue-9/bar.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "Describes bar", 4 | "type": "object", 5 | "required": [ 6 | "name" 7 | ], 8 | "properties": { 9 | "name": { 10 | "type": "string" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/issue-96/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::96 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | set_tests_properties(Issue::96 5 | PROPERTIES 6 | WILL_FAIL 1) 7 | -------------------------------------------------------------------------------- /.distro/plans/main.fmf.dist-git: -------------------------------------------------------------------------------- 1 | discover: 2 | how: fmf 3 | path: . 4 | 5 | adjust+: 6 | # Cannot use initiator: fedora-ci reliably yet 7 | when: initiator is not defined or initiator != packit 8 | discover+: 9 | dist-git-source: true 10 | dist-git-extract: json-schema-validator-*/ 11 | -------------------------------------------------------------------------------- /test/issue-100/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::100 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | set_tests_properties(Issue::100 5 | PROPERTIES 6 | WILL_FAIL 1) 7 | -------------------------------------------------------------------------------- /test/issue-101/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::101 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | set_tests_properties(Issue::101 5 | PROPERTIES 6 | WILL_FAIL 1) 7 | -------------------------------------------------------------------------------- /test/issue-27/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::27 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 4 | set_tests_properties(Issue::27 5 | PROPERTIES 6 | WILL_FAIL 1) 7 | -------------------------------------------------------------------------------- /test/test-pipe-in.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # all argument are considered as a program to call (with its arguments), 4 | # the last argument is read from stdin via '<' 5 | 6 | set -e 7 | 8 | arr=( "$@" ) 9 | 10 | input=${arr[${#arr[@]}-1]} 11 | unset 'arr[${#arr[@]}-1]' 12 | 13 | ${arr[@]} < $input 14 | -------------------------------------------------------------------------------- /test/issue-9/foo/baz/qux/qux.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "Describes qux", 4 | "type": "object", 5 | "required": [ 6 | "name" 7 | ], 8 | "properties": { 9 | "name": { 10 | "type": "string" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cmake/nlohmann_json_schema_validatorConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | find_dependency(nlohmann_json) 5 | 6 | include("${CMAKE_CURRENT_LIST_DIR}/nlohmann_json_schema_validatorTargets.cmake") 7 | check_required_components( 8 | "nlohmann_json_schema_validator" 9 | ) 10 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/ref-and-defs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/ref-and-defs.json", 3 | "$defs": { 4 | "inner": { 5 | "properties": { 6 | "bar": { "type": "string" } 7 | } 8 | } 9 | }, 10 | "$ref": "#/$defs/inner" 11 | } 12 | -------------------------------------------------------------------------------- /test/issue-101/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://xxx.local/schemas/mySchema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | 5 | "type": "object", 6 | "properties": { 7 | "top": { 8 | "type": "integer" 9 | }, 10 | "required": ["top"] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/issue-209/color.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.invalid/color.schema.json", 4 | "title": "color", 5 | "description": "X11/HTML/CSS color name as a JSON string", 6 | "type": "string", 7 | "enum": [ "White", "Black", "Red" ] 8 | } 9 | -------------------------------------------------------------------------------- /test/issue-93/components.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "Renderable": { 3 | "type":"object", 4 | "properties": { 5 | "fg":{ 6 | "$ref":"/types/color.schema.json" 7 | }, 8 | "bg":{ 9 | "$ref":"/types/color.schema.json" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/ref-and-definitions.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/ref-and-definitions.json", 3 | "definitions": { 4 | "inner": { 5 | "properties": { 6 | "bar": { "type": "string" } 7 | } 8 | } 9 | }, 10 | "allOf": [ { "$ref": "#/definitions/inner" } ] 11 | } 12 | -------------------------------------------------------------------------------- /test/issue-75/TypeId.json: -------------------------------------------------------------------------------- 1 | { 2 | "data" : { 3 | "TypeId" : { 4 | "type" : "string", 5 | "description" : "POD type of data matching bmf::data::TypeId enum", 6 | "enum" : [ "CHAR", "UCHAR", "SHORT", "USHORT", "INT", "UINT", 7 | "LONG", "ULONG", "FLOAT", "DOUBLE" ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/name-defs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$defs": { 3 | "orNull": { 4 | "anyOf": [ 5 | { 6 | "type": "null" 7 | }, 8 | { 9 | "$ref": "#" 10 | } 11 | ] 12 | } 13 | }, 14 | "type": "string" 15 | } 16 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/remotes/name.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "orNull": { 4 | "anyOf": [ 5 | { 6 | "type": "null" 7 | }, 8 | { 9 | "$ref": "#" 10 | } 11 | ] 12 | } 13 | }, 14 | "type": "string" 15 | } 16 | -------------------------------------------------------------------------------- /test/issue-100/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://xxx.local/schemas/mySchema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | 5 | "type": "object", 6 | "properties": { 7 | "Prop1": { 8 | "type": "integer", 9 | "$comment": "Just a comment" 10 | }, 11 | "Prop2": { 12 | "$ref": "#random_ref" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(readme-json-schema readme.cpp) 2 | target_link_libraries(readme-json-schema nlohmann_json_schema_validator) 3 | 4 | add_executable(format-json-schema format.cpp) 5 | target_link_libraries(format-json-schema nlohmann_json_schema_validator) 6 | 7 | if (JSON_VALIDATOR_INSTALL) 8 | install(TARGETS readme-json-schema format-json-schema 9 | DESTINATION ${CMAKE_INSTALL_BINDIR}) 10 | endif () 11 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | #AlignConsecutiveAssignments: true 3 | #AlignConsecutiveDeclarations: true 4 | AllowShortFunctionsOnASingleLine: Inline 5 | BreakBeforeBraces: Linux 6 | ColumnLimit: 0 7 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 8 | IndentWidth: 4 9 | IndentPPDirectives: AfterHash 10 | ObjCBlockIndentWidth: 0 11 | SpaceAfterCStyleCast: true 12 | TabWidth: 4 13 | AccessModifierOffset: -4 14 | UseTab: ForIndentation 15 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/float-overflow.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "all integers are multiples of 0.5, if overflow is handled", 4 | "schema": {"type": "integer", "multipleOf": 0.5}, 5 | "tests": [ 6 | { 7 | "description": "valid if optional overflow handling is implemented", 8 | "data": 1e308, 9 | "valid": true 10 | } 11 | ] 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /.distro/tests/import/find_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is a simple project that tests using cmake to load the installed libraries 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | project(test_find_package LANGUAGES CXX) 5 | 6 | set(CMAKE_FIND_DEBUG_MODE ON) 7 | find_package(nlohmann_json_schema_validator REQUIRED) 8 | 9 | if (NOT TARGET nlohmann_json_schema_validator::validator) 10 | message(FATAL_ERROR "Missing target nlohmann_json_schema_validator::validator") 11 | endif () 12 | -------------------------------------------------------------------------------- /test/issue-98.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | nlohmann::json nlBase{{"$ref", "#/unknown/keywords"}}; 6 | nlohmann::json_schema::json_validator validator; 7 | 8 | try { 9 | validator.set_root_schema(nlBase); // this line will log the caught exception 10 | } catch (const std::exception &e) { 11 | 12 | if (std::string("after all files have been parsed, '' has still the following undefined references: [/unknown/keywords]") == e.what()) 13 | return EXIT_SUCCESS; 14 | } 15 | return EXIT_FAILURE; 16 | } 17 | -------------------------------------------------------------------------------- /test/issue-143/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | 4 | "properties": { 5 | "unknown_keyword_storage": { 6 | "1": { 7 | "type": "number" 8 | }, 9 | "a": { 10 | "type": "string" 11 | } 12 | }, 13 | "ref1": { 14 | "$ref": "#/properties/unknown_keyword_storage/1" 15 | }, 16 | "refa": { 17 | "$ref": "#/properties/unknown_keyword_storage/a" 18 | } 19 | }, 20 | "additionalProperties": false 21 | } 22 | -------------------------------------------------------------------------------- /test/issue-311/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "element": { 5 | "$ref": "#/$defs/element" 6 | }, 7 | "element2": { 8 | "$ref": "#/$defs/element/items/0/$defs/element2" 9 | } 10 | }, 11 | "$defs": { 12 | "element": { 13 | "type": "array", 14 | "items": [ 15 | { 16 | "$defs": { 17 | "element2": { 18 | "type": "string" 19 | } 20 | }, 21 | "type": "number" 22 | } 23 | ] 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.distro/tests/import/FetchContent/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is a simple project that tests using cmake to load the installed libraries 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | project(test_fetch_content LANGUAGES CXX) 5 | 6 | 7 | FetchContent_Declare(nlohmann_json_schema_validator 8 | GIT_REPOSITORY https://github.com/pboettch/json-schema-validator 9 | GIT_TAG main 10 | ) 11 | FetchContent_MakeAvailable(nlohmann_json_schema_validator) 12 | 13 | if (NOT TARGET nlohmann_json_schema_validator::validator) 14 | message(FATAL_ERROR "Missing target nlohmann_json_schema_validator::validator") 15 | endif () 16 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/regex.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of regular expressions", 4 | "schema": {"format": "regex"}, 5 | "tests": [ 6 | { 7 | "description": "a valid regular expression", 8 | "data": "([abc])+\\s+$", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a regular expression with unclosed parens is invalid", 13 | "data": "^(abc]", 14 | "valid": false 15 | } 16 | ] 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /test/issue-209/entities.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.invalid/entities.schema.json", 4 | "title": "Entities", 5 | "type": "array", 6 | "items": { 7 | "type": "object", 8 | "required": [ "name" ], 9 | "properties": { 10 | "name": { 11 | "type": "string" 12 | }, 13 | "fg": { "$ref": "color.schema.json", "default": "Black" } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/issue-96/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://xxx.local/schemas/mySchema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "definitions": { 5 | "topDef": { 6 | "$id": "#topDef_ref", 7 | "type": "object", 8 | "properties": { 9 | "value": { 10 | "type": "integer", 11 | "maximum": 100 12 | } 13 | } 14 | } 15 | }, 16 | 17 | "type": "object", 18 | "properties": { 19 | "top": { 20 | "$ref": "#/definitions/topDef" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/issue-143/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_test_simple_schema(Issue::143-1 2 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 3 | ${CMAKE_CURRENT_SOURCE_DIR}/instance-fail-1.json) 4 | add_test_simple_schema(Issue::143-a 5 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 6 | ${CMAKE_CURRENT_SOURCE_DIR}/instance-fail-a.json) 7 | add_test_simple_schema(Issue::143-ok 8 | ${CMAKE_CURRENT_SOURCE_DIR}/schema.json 9 | ${CMAKE_CURRENT_SOURCE_DIR}/instance.json) 10 | 11 | set_tests_properties(Issue::143-1 Issue::143-a 12 | PROPERTIES 13 | WILL_FAIL 1) 14 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/minItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "minItems validation", 4 | "schema": {"minItems": 1}, 5 | "tests": [ 6 | { 7 | "description": "longer is valid", 8 | "data": [1, 2], 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": [1], 14 | "valid": true 15 | }, 16 | { 17 | "description": "too short is invalid", 18 | "data": [], 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-arrays", 23 | "data": "", 24 | "valid": true 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/maxItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "maxItems validation", 4 | "schema": {"maxItems": 2}, 5 | "tests": [ 6 | { 7 | "description": "shorter is valid", 8 | "data": [1], 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": [1, 2], 14 | "valid": true 15 | }, 16 | { 17 | "description": "too long is invalid", 18 | "data": [1, 2, 3], 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-arrays", 23 | "data": "foobar", 24 | "valid": true 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/definitions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validate definition against metaschema", 4 | "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, 5 | "tests": [ 6 | { 7 | "description": "valid definition schema", 8 | "data": { 9 | "definitions": { 10 | "foo": {"type": "integer"} 11 | } 12 | }, 13 | "valid": true 14 | }, 15 | { 16 | "description": "invalid definition schema", 17 | "data": { 18 | "definitions": { 19 | "foo": {"type": 1} 20 | } 21 | }, 22 | "valid": false 23 | } 24 | ] 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMaximum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "exclusiveMaximum validation", 4 | "schema": { 5 | "exclusiveMaximum": 3.0 6 | }, 7 | "tests": [ 8 | { 9 | "description": "below the exclusiveMaximum is valid", 10 | "data": 2.2, 11 | "valid": true 12 | }, 13 | { 14 | "description": "boundary point is invalid", 15 | "data": 3.0, 16 | "valid": false 17 | }, 18 | { 19 | "description": "above the exclusiveMaximum is invalid", 20 | "data": 3.5, 21 | "valid": false 22 | }, 23 | { 24 | "description": "ignores non-numbers", 25 | "data": "x", 26 | "valid": true 27 | } 28 | ] 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMinimum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "exclusiveMinimum validation", 4 | "schema": { 5 | "exclusiveMinimum": 1.1 6 | }, 7 | "tests": [ 8 | { 9 | "description": "above the exclusiveMinimum is valid", 10 | "data": 1.2, 11 | "valid": true 12 | }, 13 | { 14 | "description": "boundary point is invalid", 15 | "data": 1.1, 16 | "valid": false 17 | }, 18 | { 19 | "description": "below the exclusiveMinimum is invalid", 20 | "data": 0.6, 21 | "valid": false 22 | }, 23 | { 24 | "description": "ignores non-numbers", 25 | "data": "x", 26 | "valid": true 27 | } 28 | ] 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-email.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of an internationalized e-mail addresses", 4 | "schema": {"format": "idn-email"}, 5 | "tests": [ 6 | { 7 | "description": "a valid idn e-mail (example@example.test in Hangul)", 8 | "data": "실례@실례.테스트", 9 | "valid": true 10 | }, 11 | { 12 | "description": "an invalid idn e-mail address", 13 | "data": "2962", 14 | "valid": false 15 | }, 16 | { 17 | "description": "a valid e-mail address", 18 | "data": "joe.bloggs@example.com", 19 | "valid": true 20 | }, 21 | { 22 | "description": "an invalid e-mail address", 23 | "data": "2962", 24 | "valid": false 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /src/json-patch.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace nlohmann 7 | { 8 | class JsonPatchFormatException : public std::exception 9 | { 10 | public: 11 | explicit JsonPatchFormatException(std::string msg) 12 | : ex_{std::move(msg)} {} 13 | 14 | inline const char *what() const noexcept override final { return ex_.c_str(); } 15 | 16 | private: 17 | std::string ex_; 18 | }; 19 | 20 | class json_patch 21 | { 22 | public: 23 | json_patch() = default; 24 | json_patch(json &&patch); 25 | json_patch(const json &patch); 26 | 27 | json_patch &add(const json::json_pointer &, json value); 28 | json_patch &replace(const json::json_pointer &, json value); 29 | json_patch &remove(const json::json_pointer &); 30 | 31 | json &get_json() { return j_; } 32 | const json &get_json() const { return j_; } 33 | 34 | operator json() const { return j_; } 35 | 36 | private: 37 | json j_ = nlohmann::json::array(); 38 | 39 | static void validateJsonPatch(json const &patch); 40 | }; 41 | } // namespace nlohmann 42 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-template.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "format: uri-template", 4 | "schema": {"format": "uri-template"}, 5 | "tests": [ 6 | { 7 | "description": "a valid uri-template", 8 | "data": "http://example.com/dictionary/{term:1}/{term}", 9 | "valid": true 10 | }, 11 | { 12 | "description": "an invalid uri-template", 13 | "data": "http://example.com/dictionary/{term:1}/{term", 14 | "valid": false 15 | }, 16 | { 17 | "description": "a valid uri-template without variables", 18 | "data": "http://example.com/dictionary", 19 | "valid": true 20 | }, 21 | { 22 | "description": "a valid relative uri-template", 23 | "data": "dictionary/{term:1}/{term}", 24 | "valid": true 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/minLength.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "minLength validation", 4 | "schema": {"minLength": 2}, 5 | "tests": [ 6 | { 7 | "description": "longer is valid", 8 | "data": "foo", 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": "fo", 14 | "valid": true 15 | }, 16 | { 17 | "description": "too short is invalid", 18 | "data": "f", 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-strings", 23 | "data": 1, 24 | "valid": true 25 | }, 26 | { 27 | "description": "one supplementary Unicode code point is not long enough", 28 | "data": "\uD83D\uDCA9", 29 | "valid": false 30 | } 31 | ] 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/maxLength.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "maxLength validation", 4 | "schema": {"maxLength": 2}, 5 | "tests": [ 6 | { 7 | "description": "shorter is valid", 8 | "data": "f", 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": "fo", 14 | "valid": true 15 | }, 16 | { 17 | "description": "too long is invalid", 18 | "data": "foo", 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-strings", 23 | "data": 100, 24 | "valid": true 25 | }, 26 | { 27 | "description": "two supplementary Unicode code points is long enough", 28 | "data": "\uD83D\uDCA9\uD83D\uDCA9", 29 | "valid": true 30 | } 31 | ] 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/Takishima/cmake-pre-commit-hooks 3 | rev: v1.9.6 4 | hooks: 5 | - id: clang-format 6 | args: 7 | - '-i' 8 | - id: clang-tidy 9 | args: 10 | # TODO: Remove when upstream issue is fixed 11 | # https://gitlab.kitware.com/cmake/cmake/-/issues/24827 12 | # https://github.com/Takishima/cmake-pre-commit-hooks/issues/63 13 | - '-Bcmake-build-pre-commit' 14 | - '--preset' 15 | - 'pre-commit' 16 | stages: [ manual ] 17 | - repo: https://github.com/pre-commit/pre-commit-hooks 18 | rev: v6.0.0 19 | hooks: 20 | - id: trailing-whitespace 21 | - id: end-of-file-fixer 22 | - id: check-yaml 23 | - repo: https://github.com/executablebooks/mdformat 24 | rev: 0.7.22 25 | hooks: 26 | - id: mdformat 27 | additional_dependencies: 28 | - mdformat-gfm 29 | - mdformat-tables 30 | - repo: https://github.com/python-jsonschema/check-jsonschema 31 | rev: 0.34.0 32 | hooks: 33 | - id: check-github-workflows 34 | -------------------------------------------------------------------------------- /test/issue-70.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using nlohmann::json; 4 | using nlohmann::json_schema::json_validator; 5 | 6 | static const json person_schema = R"( 7 | { 8 | "$schema": "http://json-schema.org/draft-07/schema#", 9 | "title": "A person", 10 | "properties": { 11 | "name": { 12 | "description": "Name", 13 | "type": "string" 14 | }, 15 | "age": { 16 | "description": "Age of the person", 17 | "type": "number", 18 | "minimum": 2, 19 | "maximum": 200 20 | }, 21 | "phones": { 22 | "type": "array", 23 | "items": { 24 | "type": "number" 25 | } 26 | } 27 | }, 28 | "required": [ 29 | "name", 30 | "age" 31 | ], 32 | "additionalProperties": false, 33 | "type": "object" 34 | })"_json; 35 | 36 | int main(void) 37 | { 38 | json_validator validator; 39 | 40 | validator.set_root_schema(person_schema); 41 | validator.set_root_schema(person_schema); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /cmake/CMakePresets-defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "configurePresets": [ 4 | { 5 | "name": "default", 6 | "displayName": "Default configuration preset", 7 | "binaryDir": "cmake-build-release", 8 | "cacheVariables": { 9 | "CMAKE_BUILD_TYPE": { 10 | "type": "STRING", 11 | "value": "Release" 12 | } 13 | } 14 | } 15 | ], 16 | "buildPresets": [ 17 | { 18 | "name": "default", 19 | "displayName": "Default build preset", 20 | "configurePreset": "default" 21 | } 22 | ], 23 | "testPresets": [ 24 | { 25 | "name": "default", 26 | "displayName": "Default test preset", 27 | "configurePreset": "default" 28 | } 29 | ], 30 | "workflowPresets": [ 31 | { 32 | "name": "default", 33 | "displayName": "Default workflow", 34 | "steps": [ 35 | { 36 | "type": "configure", 37 | "name": "default" 38 | }, 39 | { 40 | "type": "build", 41 | "name": "default" 42 | }, 43 | { 44 | "type": "test", 45 | "name": "default" 46 | } 47 | ] 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/infinite-loop-detection.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop", 4 | "schema": { 5 | "definitions": { 6 | "int": { "type": "integer" } 7 | }, 8 | "allOf": [ 9 | { 10 | "properties": { 11 | "foo": { 12 | "$ref": "#/definitions/int" 13 | } 14 | } 15 | }, 16 | { 17 | "additionalProperties": { 18 | "$ref": "#/definitions/int" 19 | } 20 | } 21 | ] 22 | }, 23 | "tests": [ 24 | { 25 | "description": "passing case", 26 | "data": { "foo": 1 }, 27 | "valid": true 28 | }, 29 | { 30 | "description": "failing case", 31 | "data": { "foo": "a string" }, 32 | "valid": false 33 | } 34 | ] 35 | } 36 | ] 37 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/minProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "minProperties validation", 4 | "schema": {"minProperties": 1}, 5 | "tests": [ 6 | { 7 | "description": "longer is valid", 8 | "data": {"foo": 1, "bar": 2}, 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": {"foo": 1}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "too short is invalid", 18 | "data": {}, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores arrays", 23 | "data": [], 24 | "valid": true 25 | }, 26 | { 27 | "description": "ignores strings", 28 | "data": "", 29 | "valid": true 30 | }, 31 | { 32 | "description": "ignores other non-objects", 33 | "data": 12, 34 | "valid": true 35 | } 36 | ] 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /test/issue-255-error-message-limit-precision.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using nlohmann::json; 5 | using nlohmann::json_schema::json_validator; 6 | 7 | static const json schema = R"( 8 | { 9 | "$schema": "http://json-schema.org/draft-07/schema#", 10 | "$id": "arc.schema.json", 11 | "properties": { 12 | "angle": { 13 | "type": "number", 14 | "description": "Radians, from -π to π.", 15 | "minimum": -3.14159265358979323846, 16 | "maximum": 3.14159265358979323846 17 | } 18 | } 19 | })"_json; 20 | 21 | class custom_error_handler : public nlohmann::json_schema::basic_error_handler 22 | { 23 | void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override 24 | { 25 | if (message != "instance exceeds maximum of 3.141592653589793") 26 | throw std::invalid_argument("Precision print does not work."); 27 | } 28 | }; 29 | 30 | int main(void) 31 | { 32 | json_validator validator; 33 | 34 | auto instance = R"({ "angle": 3.1415927410125732 })"_json; 35 | 36 | validator.set_root_schema(schema); 37 | custom_error_handler err; 38 | validator.validate(instance, err); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Modern C++ JSON schema validator is licensed under the MIT License 2 | : 3 | 4 | Copyright (c) 2016 Patrick Boettcher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | of the Software, and to permit persons to whom the Software is furnished to do 11 | so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | ## Release 2.4.0 2 | 3 | - Added CI job to publish GitHub release by @JohanMabille in 4 | - Maintenance to Fedora CI infrastructure by @LecrisUT and @JohanMabille in 5 | - Reference validation using contains() result rather than exception handling by @BalrogOfHell in 6 | - add support for $defs instead of definitions by rpatters1 in 7 | - Apply clang-format / fix "test / Check pre-commit" failures by @serge-s in 8 | - Adding verbose error messages for logical combinations by Csaba Imre Zempleni in 9 | - fix: issue-311 by andrejlevkovitch 10 | - Fix cmake install target on windows by @barts-of in 11 | - error-messages: Numeric limit errors should show maximum precision by @pboettch 12 | - Add Fedora packaging by @LecrisUT in 13 | - Improve and fix bugs in Conanfile by Jacob Crabill 14 | -------------------------------------------------------------------------------- /test/issue-293.cpp: -------------------------------------------------------------------------------- 1 | #include "nlohmann/json-schema.hpp" 2 | 3 | using nlohmann::json_schema::json_validator; 4 | 5 | template 6 | int should_throw(const nlohmann::json &schema, T value) 7 | { 8 | try { 9 | json_validator(schema).validate(value); 10 | } catch (const std::exception &ex) { 11 | return 0; 12 | } 13 | return 1; 14 | } 15 | 16 | int main(void) 17 | { 18 | 19 | json_validator({{"type", "number"}, {"multipleOf", 0.001}}).validate(0.3 - 0.2); 20 | json_validator({{"type", "number"}, {"multipleOf", 3.3}}).validate(8.0 - 1.4); 21 | json_validator({{"type", "number"}, {"multipleOf", 1000.01}}).validate((1000.03 - 0.02) * 15.0); 22 | json_validator({{"type", "number"}, {"multipleOf", 0.001}}).validate(0.030999999999999993); 23 | json_validator({{"type", "number"}, {"multipleOf", 0.100000}}).validate(1.9); 24 | json_validator({{"type", "number"}, {"multipleOf", 100000.1}}).validate(9000009); 25 | 26 | int exc_count = 0; 27 | exc_count += should_throw({{"type", "number"}, {"multipleOf", 0.001}}, 0.3 - 0.2005); 28 | exc_count += should_throw({{"type", "number"}, {"multipleOf", 1000.02}}, (1000.03 - 0.02) * 15.0); 29 | exc_count += should_throw({{"type", "number"}, {"multipleOf", 100000.11}}, 9000009); 30 | 31 | return exc_count; 32 | } 33 | -------------------------------------------------------------------------------- /test/issue-243-root-default-values.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using nlohmann::json; 5 | using nlohmann::json_uri; 6 | using nlohmann::json_schema::json_validator; 7 | 8 | static const json root_default = R"( 9 | { 10 | "$schema": "http://json-schema.org/draft-07/schema#", 11 | "properties": { 12 | "width": { 13 | "type": "integer" 14 | } 15 | }, 16 | "default": { 17 | "width": 42 18 | } 19 | })"_json; 20 | 21 | int main(void) 22 | { 23 | json_validator validator{}; 24 | 25 | validator.set_root_schema(root_default); 26 | 27 | { 28 | json nul_json; 29 | if (!nul_json.is_null()) { 30 | return 1; 31 | } 32 | 33 | const auto default_patch = validator.validate(nul_json); 34 | 35 | if (default_patch.is_null()) { 36 | std::cerr << "Patch is null but should contain operation to add defaults to root" << std::endl; 37 | return 1; 38 | } 39 | 40 | const auto actual = nul_json.patch(default_patch); 41 | const auto expected = R"({"width": 42})"_json; 42 | if (actual != expected) { 43 | std::cerr << "Patch of defaults is wrong for root schema: '" << actual.dump() << "' instead of expected '" << expected.dump() << "'" << std::endl; 44 | } 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /test/issue-93/issue-93.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using nlohmann::json; 7 | using nlohmann::json_uri; 8 | using nlohmann::json_schema::json_validator; 9 | 10 | static const auto expected_patch = R"( 11 | [{"op":"add","path":"/0/renderable/bg","value":"Black"}] 12 | )"_json; 13 | 14 | static const auto instance = R"( 15 | [ 16 | { 17 | "name":"player", 18 | "renderable": { 19 | "fg":"White" 20 | } 21 | } 22 | ] 23 | )"_json; 24 | 25 | static void loader(const json_uri &uri, json &schema) 26 | { 27 | std::string filename = "./" + uri.path(); 28 | std::ifstream lf(filename); 29 | if (!lf.good()) 30 | throw std::invalid_argument("could not open " + uri.url() + " tried with " + filename); 31 | try { 32 | lf >> schema; 33 | } catch (const std::exception &e) { 34 | throw e; 35 | } 36 | } 37 | 38 | int main(void) 39 | { 40 | json_validator validator(loader); 41 | 42 | std::fstream f("blueprints.schema.json"); 43 | 44 | json schema; 45 | f >> schema; 46 | 47 | validator.set_root_schema(schema); 48 | 49 | auto missing_default_patch = validator.validate(instance); 50 | 51 | std::cerr << missing_default_patch << "\n"; 52 | std::cerr << expected_patch << "\n"; 53 | 54 | return missing_default_patch != expected_patch; 55 | } 56 | -------------------------------------------------------------------------------- /src/smtp-address-validator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SMTP_ADDRESS_PARSER_HPP_INCLUDED 2 | #define SMTP_ADDRESS_PARSER_HPP_INCLUDED 3 | 4 | /* 5 | 6 | Snarfed from 7 | 8 | : 9 | 10 | Copyright (c) 2021 Gene Hightower 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | this software and associated documentation files (the "Software"), to deal in 14 | the Software without restriction, including without limitation the rights to 15 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 16 | of the Software, and to permit persons to whom the Software is furnished to do 17 | so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in all 20 | copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | SOFTWARE. 29 | 30 | */ 31 | 32 | bool is_address(const char *p, const char *pe); 33 | 34 | #endif // SMTP_ADDRESS_PARSER_HPP_INCLUDED 35 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri-reference.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of IRI References", 4 | "schema": {"format": "iri-reference"}, 5 | "tests": [ 6 | { 7 | "description": "a valid IRI", 8 | "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a valid protocol-relative IRI Reference", 13 | "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx", 14 | "valid": true 15 | }, 16 | { 17 | "description": "a valid relative IRI Reference", 18 | "data": "/âππ", 19 | "valid": true 20 | }, 21 | { 22 | "description": "an invalid IRI Reference", 23 | "data": "\\\\WINDOWS\\filëßåré", 24 | "valid": false 25 | }, 26 | { 27 | "description": "a valid IRI Reference", 28 | "data": "âππ", 29 | "valid": true 30 | }, 31 | { 32 | "description": "a valid IRI fragment", 33 | "data": "#ƒrägmênt", 34 | "valid": true 35 | }, 36 | { 37 | "description": "an invalid IRI fragment", 38 | "data": "#ƒräg\\mênt", 39 | "valid": false 40 | } 41 | ] 42 | } 43 | ] 44 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-reference.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of URI References", 4 | "schema": {"format": "uri-reference"}, 5 | "tests": [ 6 | { 7 | "description": "a valid URI", 8 | "data": "http://foo.bar/?baz=qux#quux", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a valid protocol-relative URI Reference", 13 | "data": "//foo.bar/?baz=qux#quux", 14 | "valid": true 15 | }, 16 | { 17 | "description": "a valid relative URI Reference", 18 | "data": "/abc", 19 | "valid": true 20 | }, 21 | { 22 | "description": "an invalid URI Reference", 23 | "data": "\\\\WINDOWS\\fileshare", 24 | "valid": false 25 | }, 26 | { 27 | "description": "a valid URI Reference", 28 | "data": "abc", 29 | "valid": true 30 | }, 31 | { 32 | "description": "a valid URI fragment", 33 | "data": "#fragment", 34 | "valid": true 35 | }, 36 | { 37 | "description": "an invalid URI fragment", 38 | "data": "#frag\\ment", 39 | "valid": false 40 | } 41 | ] 42 | } 43 | ] 44 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | run-name: Release 3 | 4 | on: 5 | push: 6 | tags: 7 | - "[0-9]+.[0-9]+.[0-9]+" 8 | 9 | jobs: 10 | tests: 11 | uses: ./.github/workflows/test.yaml 12 | secrets: inherit 13 | 14 | publish-release: 15 | needs: [tests] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - name: Create GitHub Release 21 | uses: softprops/action-gh-release@v2 22 | with: 23 | name: "json-schema-validator ${{ github.ref }}" 24 | prerelease: false 25 | draft: false 26 | generate_release_notes: true 27 | 28 | build_conan: 29 | needs: [tests] 30 | runs-on: ubuntu-latest 31 | container: ghcr.io/nlohmann/json-ci:v2.4.0 32 | steps: 33 | - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." 34 | - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub." 35 | - run: echo "🔎 Branch name is ${{ github.ref }} and repository is ${{ github.repository }}." 36 | - name: Clone json-schema-validator 37 | uses: actions/checkout@v3 38 | - uses: actions/setup-python@v4 39 | with: 40 | python-version: '3.10' 41 | - run: python -m pip install --upgrade conan 42 | - run: conan config init 43 | - run: conan profile update settings.compiler.libcxx=libstdc++11 default 44 | - name: conan create package 45 | run: conan create . 46 | -------------------------------------------------------------------------------- /example/format.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using nlohmann::json; 6 | using nlohmann::json_schema::json_validator; 7 | 8 | // The schema is defined based upon a string literal 9 | static json uri_schema = R"( 10 | { 11 | "$schema": "http://json-schema.org/draft-07/schema#", 12 | "type": "object", 13 | "properties": { 14 | "myUri": { 15 | "type":"string", 16 | "format": "uri" 17 | } 18 | } 19 | })"_json; 20 | 21 | // The people are defined with brace initialization 22 | static json good_uri = {{"myUri", "http://hostname.com/"}}; 23 | static json bad_uri = {{"myUri", "http:/hostname.com/"}}; 24 | 25 | static void uri_format_checker(const std::string &format, const std::string &value) 26 | { 27 | if (format == "uri") { 28 | if (value.find("://") == std::string::npos) 29 | throw std::invalid_argument("URI does not contain :// - invalid"); 30 | } else 31 | throw std::logic_error("Don't know how to validate " + format); 32 | } 33 | 34 | int main() 35 | { 36 | json_validator validator(nullptr, uri_format_checker); // create validator 37 | 38 | try { 39 | validator.set_root_schema(uri_schema); // insert root-schema 40 | } catch (const std::exception &e) { 41 | std::cerr << "Validation of schema failed, here is why: " << e.what() << "\n"; 42 | return EXIT_FAILURE; 43 | } 44 | 45 | validator.validate(good_uri); 46 | 47 | try { 48 | validator.validate(bad_uri); 49 | } catch (const std::exception &e) { 50 | std::cerr << "Validation expectedly failed, here is why: " << e.what() << "\n"; 51 | } 52 | 53 | return EXIT_SUCCESS; 54 | } 55 | -------------------------------------------------------------------------------- /.distro/json-schema-validator.spec: -------------------------------------------------------------------------------- 1 | Name: json-schema-validator 2 | Summary: JSON schema validator for JSON for Modern C++ 3 | Version: 0.0.0 4 | Release: %autorelease 5 | License: MIT 6 | URL: https://github.com/pboettch/json-schema-validator 7 | 8 | Source: https://github.com/pboettch/json-schema-validator/archive/refs/tags/v%{version}.tar.gz 9 | 10 | BuildRequires: ninja-build 11 | BuildRequires: cmake 12 | BuildRequires: gcc-c++ 13 | BuildRequires: json-devel 14 | 15 | %description 16 | Json schema validator library for C++ projects using nlohmann/json 17 | 18 | %package devel 19 | Summary: Development files for JSON schema validator 20 | Requires: json-schema-validator%{?_isa} = %{version}-%{release} 21 | Requires: json-devel 22 | 23 | %description devel 24 | Json schema validator development files for C++ projects using nlohmann/json 25 | 26 | 27 | %prep 28 | %autosetup -n json-schema-validator-%{version} 29 | 30 | 31 | %build 32 | %cmake \ 33 | -DJSON_VALIDATOR_SHARED_LIBS=ON \ 34 | -DJSON_VALIDATOR_INSTALL=ON \ 35 | -DJSON_VALIDATOR_BUILD_EXAMPLES=OFF \ 36 | -DJSON_VALIDATOR_BUILD_TESTS=ON 37 | 38 | %cmake_build 39 | 40 | 41 | %install 42 | %cmake_install 43 | 44 | 45 | %check 46 | %ctest 47 | 48 | 49 | %files 50 | %doc README.md 51 | %license LICENSE 52 | %{_libdir}/libnlohmann_json_schema_validator.so.* 53 | 54 | %files devel 55 | %{_libdir}/libnlohmann_json_schema_validator.so 56 | %{_includedir}/nlohmann/json-schema.hpp 57 | %{_libdir}/cmake/nlohmann_json_schema_validator 58 | 59 | 60 | %changelog 61 | %autochangelog 62 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/maximum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "maximum validation", 4 | "schema": {"maximum": 3.0}, 5 | "tests": [ 6 | { 7 | "description": "below the maximum is valid", 8 | "data": 2.6, 9 | "valid": true 10 | }, 11 | { 12 | "description": "boundary point is valid", 13 | "data": 3.0, 14 | "valid": true 15 | }, 16 | { 17 | "description": "above the maximum is invalid", 18 | "data": 3.5, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-numbers", 23 | "data": "x", 24 | "valid": true 25 | } 26 | ] 27 | }, 28 | { 29 | "description": "maximum validation with unsigned integer", 30 | "schema": {"maximum": 300}, 31 | "tests": [ 32 | { 33 | "description": "below the maximum is invalid", 34 | "data": 299.97, 35 | "valid": true 36 | }, 37 | { 38 | "description": "boundary point integer is valid", 39 | "data": 300, 40 | "valid": true 41 | }, 42 | { 43 | "description": "boundary point float is valid", 44 | "data": 300.00, 45 | "valid": true 46 | }, 47 | { 48 | "description": "above the maximum is invalid", 49 | "data": 300.5, 50 | "valid": false 51 | } 52 | ] 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/maxProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "maxProperties validation", 4 | "schema": {"maxProperties": 2}, 5 | "tests": [ 6 | { 7 | "description": "shorter is valid", 8 | "data": {"foo": 1}, 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": {"foo": 1, "bar": 2}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "too long is invalid", 18 | "data": {"foo": 1, "bar": 2, "baz": 3}, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores arrays", 23 | "data": [1, 2, 3], 24 | "valid": true 25 | }, 26 | { 27 | "description": "ignores strings", 28 | "data": "foobar", 29 | "valid": true 30 | }, 31 | { 32 | "description": "ignores other non-objects", 33 | "data": 12, 34 | "valid": true 35 | } 36 | ] 37 | }, 38 | { 39 | "description": "maxProperties = 0 means the object is empty", 40 | "schema": { "maxProperties": 0 }, 41 | "tests": [ 42 | { 43 | "description": "no properties is valid", 44 | "data": {}, 45 | "valid": true 46 | }, 47 | { 48 | "description": "one property is invalid", 49 | "data": { "foo": 1 }, 50 | "valid": false 51 | } 52 | ] 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /.packit.yaml: -------------------------------------------------------------------------------- 1 | files_to_sync: 2 | - src: .distro/ 3 | dest: ./ 4 | delete: true 5 | filters: 6 | - "protect .git*" 7 | - "protect sources" 8 | - "protect changelog" 9 | - "protect gating.yaml" 10 | # Temporary workaround until 11 | # https://github.com/packit/packit/pull/2573 12 | - "- json-schema-validator.spec" 13 | - .packit.yaml 14 | 15 | upstream_package_name: json-schema-validator 16 | specfile_path: .distro/json-schema-validator.spec 17 | downstream_package_name: json-schema-validator 18 | update_release: false 19 | 20 | targets: &targets 21 | - fedora-all-x86_64 22 | # TODO: aarch64 is failing at test 23 | # JSON-Suite::Optional::Format::idn-email 24 | # - fedora-all-aarch64 25 | 26 | _: 27 | # Job templates 28 | - &build-in-packit 29 | job: copr_build 30 | - &build-in-lecris 31 | <<: *build-in-packit 32 | owner: "@scikit-build" 33 | - &tests 34 | job: tests 35 | fmf_path: .distro 36 | identifier: downstream 37 | 38 | jobs: 39 | # Upstream jobs 40 | - <<: *build-in-lecris 41 | trigger: release 42 | project: release 43 | # - <<: *tests 44 | # trigger: release 45 | - <<: *build-in-lecris 46 | trigger: commit 47 | branch: main 48 | project: nightly 49 | # - <<: *tests 50 | # trigger: commit 51 | # branch: main 52 | - <<: *build-in-packit 53 | trigger: pull_request 54 | # - <<: *tests 55 | # trigger: pull_request 56 | # Downstream jobs 57 | - job: propose_downstream 58 | trigger: release 59 | dist_git_branches: 60 | - fedora-rawhide 61 | - job: koji_build 62 | trigger: commit 63 | dist_git_branches: 64 | - fedora-all 65 | - job: bodhi_update 66 | trigger: commit 67 | dist_git_branches: 68 | - fedora-branched 69 | -------------------------------------------------------------------------------- /test/issue-229-oneof-default-values.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using nlohmann::json; 5 | using nlohmann::json_uri; 6 | using nlohmann::json_schema::json_validator; 7 | 8 | static const json default_schema = R"( 9 | { 10 | "$schema": "http://json-schema.org/draft-07/schema#", 11 | "type": "object", 12 | "oneOf": [ 13 | { 14 | "type": "object", 15 | "properties": { 16 | "name": { 17 | "enum": "foo" 18 | }, 19 | "code": { 20 | "const": 1, 21 | "default": 1 22 | } 23 | } 24 | }, 25 | { 26 | "type": "object", 27 | "properties": { 28 | "name": { 29 | "enum": "bar" 30 | }, 31 | "code": { 32 | "const": 2, 33 | "default": 2 34 | } 35 | } 36 | } 37 | ] 38 | })"_json; 39 | 40 | static void loader(const json_uri &uri, json &schema) 41 | { 42 | schema = default_schema; 43 | } 44 | 45 | int main(void) 46 | { 47 | json_validator validator(loader); 48 | 49 | validator.set_root_schema(default_schema); 50 | 51 | json data = R"({"name": "bar"})"_json; 52 | json expected = R"( 53 | [ 54 | { 55 | "op": "add", 56 | "path": "/code", 57 | "value": 2 58 | } 59 | ] 60 | )"_json; 61 | 62 | json patch = validator.validate(data); 63 | if (patch != expected) { 64 | std::cerr << "Patch contains wrong operation: '" << patch.dump() << "' instead of expected '" << expected.dump() << "'" << std::endl; 65 | return 1; 66 | } 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/pattern.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "pattern validation", 4 | "schema": {"pattern": "^a*$"}, 5 | "tests": [ 6 | { 7 | "description": "a matching pattern is valid", 8 | "data": "aaa", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a non-matching pattern is invalid", 13 | "data": "abc", 14 | "valid": false 15 | }, 16 | { 17 | "description": "ignores booleans", 18 | "data": true, 19 | "valid": true 20 | }, 21 | { 22 | "description": "ignores integers", 23 | "data": 123, 24 | "valid": true 25 | }, 26 | { 27 | "description": "ignores floats", 28 | "data": 1.0, 29 | "valid": true 30 | }, 31 | { 32 | "description": "ignores objects", 33 | "data": {}, 34 | "valid": true 35 | }, 36 | { 37 | "description": "ignores arrays", 38 | "data": [], 39 | "valid": true 40 | }, 41 | { 42 | "description": "ignores null", 43 | "data": null, 44 | "valid": true 45 | } 46 | ] 47 | }, 48 | { 49 | "description": "pattern is not anchored", 50 | "schema": {"pattern": "a+"}, 51 | "tests": [ 52 | { 53 | "description": "matches a substring", 54 | "data": "xxaayy", 55 | "valid": true 56 | } 57 | ] 58 | } 59 | ] 60 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/relative-json-pointer.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of Relative JSON Pointers (RJP)", 4 | "schema": {"format": "relative-json-pointer"}, 5 | "tests": [ 6 | { 7 | "description": "a valid upwards RJP", 8 | "data": "1", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a valid downwards RJP", 13 | "data": "0/foo/bar", 14 | "valid": true 15 | }, 16 | { 17 | "description": "a valid up and then down RJP, with array index", 18 | "data": "2/0/baz/1/zip", 19 | "valid": true 20 | }, 21 | { 22 | "description": "a valid RJP taking the member or index name", 23 | "data": "0#", 24 | "valid": true 25 | }, 26 | { 27 | "description": "an invalid RJP that is a valid JSON Pointer", 28 | "data": "/foo/bar", 29 | "valid": false 30 | }, 31 | { 32 | "description": "negative prefix", 33 | "data": "-1/foo/bar", 34 | "valid": false 35 | }, 36 | { 37 | "description": "## is not a valid json-pointer", 38 | "data": "0##", 39 | "valid": false 40 | }, 41 | { 42 | "description": "zero cannot be followed by other digits, plus json-pointer", 43 | "data": "01/a", 44 | "valid": false 45 | }, 46 | { 47 | "description": "zero cannot be followed by other digits, plus octothorpe", 48 | "data": "01#", 49 | "valid": false 50 | } 51 | ] 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/email.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of e-mail addresses", 4 | "schema": {"format": "email"}, 5 | "tests": [ 6 | { 7 | "description": "a valid e-mail address", 8 | "data": "joe.bloggs@example.com", 9 | "valid": true 10 | }, 11 | { 12 | "description": "an invalid e-mail address", 13 | "data": "2962", 14 | "valid": false 15 | }, 16 | { 17 | "description": "tilde in local part is valid", 18 | "data": "te~st@example.com", 19 | "valid": true 20 | }, 21 | { 22 | "description": "tilde before local part is valid", 23 | "data": "~test@example.com", 24 | "valid": true 25 | }, 26 | { 27 | "description": "tilde after local part is valid", 28 | "data": "test~@example.com", 29 | "valid": true 30 | }, 31 | { 32 | "description": "dot before local part is not valid", 33 | "data": ".test@example.com", 34 | "valid": false 35 | }, 36 | { 37 | "description": "dot after local part is not valid", 38 | "data": "test.@example.com", 39 | "valid": false 40 | }, 41 | { 42 | "description": "two separated dots inside local part are valid", 43 | "data": "te.s.t@example.com", 44 | "valid": true 45 | }, 46 | { 47 | "description": "two subsequent dots inside local part are not valid", 48 | "data": "te..st@example.com", 49 | "valid": false 50 | } 51 | ] 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/id.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "id inside an enum is not a real identifier", 4 | "comment": "the implementation must not be confused by an id buried in the enum", 5 | "schema": { 6 | "definitions": { 7 | "id_in_enum": { 8 | "enum": [ 9 | { 10 | "$id": "https://localhost:1234/id/my_identifier.json", 11 | "type": "null" 12 | } 13 | ] 14 | }, 15 | "real_id_in_schema": { 16 | "$id": "https://localhost:1234/id/my_identifier.json", 17 | "type": "string" 18 | }, 19 | "zzz_id_in_const": { 20 | "const": { 21 | "$id": "https://localhost:1234/id/my_identifier.json", 22 | "type": "null" 23 | } 24 | } 25 | }, 26 | "anyOf": [ 27 | { "$ref": "#/definitions/id_in_enum" }, 28 | { "$ref": "https://localhost:1234/id/my_identifier.json" } 29 | ] 30 | }, 31 | "tests": [ 32 | { 33 | "description": "exact match to enum, and type matches", 34 | "data": { 35 | "$id": "https://localhost:1234/id/my_identifier.json", 36 | "type": "null" 37 | }, 38 | "valid": true 39 | }, 40 | { 41 | "description": "match $ref to id", 42 | "data": "a string to match #/definitions/id_in_enum", 43 | "valid": true 44 | }, 45 | { 46 | "description": "no match on enum or $ref to id", 47 | "data": 1, 48 | "valid": false 49 | } 50 | ] 51 | } 52 | 53 | ] 54 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of IRIs", 4 | "schema": {"format": "iri"}, 5 | "tests": [ 6 | { 7 | "description": "a valid IRI with anchor tag", 8 | "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a valid IRI with anchor tag and parentheses", 13 | "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1", 14 | "valid": true 15 | }, 16 | { 17 | "description": "a valid IRI with URL-encoded stuff", 18 | "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff", 19 | "valid": true 20 | }, 21 | { 22 | "description": "a valid IRI with many special characters", 23 | "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", 24 | "valid": true 25 | }, 26 | { 27 | "description": "a valid IRI based on IPv6", 28 | "data": "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", 29 | "valid": true 30 | }, 31 | { 32 | "description": "an invalid IRI based on IPv6", 33 | "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", 34 | "valid": false 35 | }, 36 | { 37 | "description": "an invalid relative IRI Reference", 38 | "data": "/abc", 39 | "valid": false 40 | }, 41 | { 42 | "description": "an invalid IRI", 43 | "data": "\\\\WINDOWS\\filëßåré", 44 | "valid": false 45 | }, 46 | { 47 | "description": "an invalid IRI though valid IRI reference", 48 | "data": "âππ", 49 | "valid": false 50 | } 51 | ] 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /test/json-patch.cpp: -------------------------------------------------------------------------------- 1 | #include "../src/json-patch.hpp" 2 | 3 | #include 4 | 5 | using nlohmann::json_patch; 6 | 7 | #define OK(code) \ 8 | do { \ 9 | try { \ 10 | code; \ 11 | } catch (const std::exception &e) { \ 12 | std::cerr << "UNEXPECTED FAILED: " << e.what() << "\n"; \ 13 | return 1; \ 14 | } \ 15 | } while (0) 16 | 17 | #define KO(code) \ 18 | do { \ 19 | try { \ 20 | code; \ 21 | std::cerr << "UNEXPECTED SUCCESS.\n"; \ 22 | return 1; \ 23 | } catch (const std::exception &e) { \ 24 | std::cerr << "EXPECTED FAIL: " << e.what() << "\n"; \ 25 | } \ 26 | } while (0) 27 | 28 | int main(void) 29 | { 30 | OK(json_patch p1(R"([{"op":"add","path":"/0/renderable/bg","value":"Black"}])"_json)); 31 | OK(json_patch p1(R"([{"op":"replace","path":"/0/renderable/bg","value":"Black"}])"_json)); 32 | OK(json_patch p1(R"([{"op":"remove","path":"/0/renderable/bg"}])"_json)); 33 | 34 | // value not needed 35 | KO(json_patch p1(R"([{"op":"remove","path":"/0/renderable/bg", "value":"Black"}])"_json)); 36 | // value missing 37 | KO(json_patch p1(R"([{"op":"add","path":"/0/renderable/bg"}])"_json)); 38 | // value missing 39 | KO(json_patch p1(R"([{"op":"replace","path":"/0/renderable/bg"}])"_json)); 40 | 41 | // wrong op 42 | KO(json_patch p1(R"([{"op":"ad","path":"/0/renderable/bg","value":"Black"}])"_json)); 43 | 44 | // invalid json-pointer 45 | KO(json_patch p1(R"([{"op":"add","path":"0/renderable/bg","value":"Black"}])"_json)); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv4.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of IP addresses", 4 | "schema": {"format": "ipv4"}, 5 | "tests": [ 6 | { 7 | "description": "a valid IP address", 8 | "data": "192.168.0.1", 9 | "valid": true 10 | }, 11 | { 12 | "description": "an IP address with too many components", 13 | "data": "127.0.0.0.1", 14 | "valid": false 15 | }, 16 | { 17 | "description": "an IP address with out-of-range values", 18 | "data": "256.256.256.256", 19 | "valid": false 20 | }, 21 | { 22 | "description": "an IP address without 4 components", 23 | "data": "127.0", 24 | "valid": false 25 | }, 26 | { 27 | "description": "an IP address as an integer", 28 | "data": "0x7f000001", 29 | "valid": false 30 | }, 31 | { 32 | "description": "an IP address as an integer (decimal)", 33 | "data": "2130706433", 34 | "valid": false 35 | }, 36 | { 37 | "description": "leading zeroes should be rejected, as they are treated as octals", 38 | "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/", 39 | "data": "087.10.0.1", 40 | "valid": false 41 | }, 42 | { 43 | "description": "value without leading zero is valid", 44 | "data": "87.10.0.1", 45 | "valid": true 46 | }, 47 | { 48 | "description": "non-ascii digits should be rejected", 49 | "data": "1২7.0.0.1", 50 | "valid": false 51 | } 52 | ] 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/minimum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "minimum validation", 4 | "schema": {"minimum": 1.1}, 5 | "tests": [ 6 | { 7 | "description": "above the minimum is valid", 8 | "data": 2.6, 9 | "valid": true 10 | }, 11 | { 12 | "description": "boundary point is valid", 13 | "data": 1.1, 14 | "valid": true 15 | }, 16 | { 17 | "description": "below the minimum is invalid", 18 | "data": 0.6, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-numbers", 23 | "data": "x", 24 | "valid": true 25 | } 26 | ] 27 | }, 28 | { 29 | "description": "minimum validation with signed integer", 30 | "schema": {"minimum": -2}, 31 | "tests": [ 32 | { 33 | "description": "negative above the minimum is valid", 34 | "data": -1, 35 | "valid": true 36 | }, 37 | { 38 | "description": "positive above the minimum is valid", 39 | "data": 0, 40 | "valid": true 41 | }, 42 | { 43 | "description": "boundary point is valid", 44 | "data": -2, 45 | "valid": true 46 | }, 47 | { 48 | "description": "boundary point with float is valid", 49 | "data": -2.0, 50 | "valid": true 51 | }, 52 | { 53 | "description": "float below the minimum is invalid", 54 | "data": -2.0001, 55 | "valid": false 56 | }, 57 | { 58 | "description": "int below the minimum is invalid", 59 | "data": -3, 60 | "valid": false 61 | }, 62 | { 63 | "description": "ignores non-numbers", 64 | "data": "x", 65 | "valid": true 66 | } 67 | ] 68 | } 69 | ] 70 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/multipleOf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "by int", 4 | "schema": {"multipleOf": 2}, 5 | "tests": [ 6 | { 7 | "description": "int by int", 8 | "data": 10, 9 | "valid": true 10 | }, 11 | { 12 | "description": "int by int fail", 13 | "data": 7, 14 | "valid": false 15 | }, 16 | { 17 | "description": "ignores non-numbers", 18 | "data": "foo", 19 | "valid": true 20 | } 21 | ] 22 | }, 23 | { 24 | "description": "by number", 25 | "schema": {"multipleOf": 1.5}, 26 | "tests": [ 27 | { 28 | "description": "zero is multiple of anything", 29 | "data": 0, 30 | "valid": true 31 | }, 32 | { 33 | "description": "4.5 is multiple of 1.5", 34 | "data": 4.5, 35 | "valid": true 36 | }, 37 | { 38 | "description": "35 is not multiple of 1.5", 39 | "data": 35, 40 | "valid": false 41 | } 42 | ] 43 | }, 44 | { 45 | "description": "by small number", 46 | "schema": {"multipleOf": 0.0001}, 47 | "tests": [ 48 | { 49 | "description": "0.0075 is multiple of 0.0001", 50 | "data": 0.0075, 51 | "valid": true 52 | }, 53 | { 54 | "description": "0.00751 is not multiple of 0.0001", 55 | "data": 0.00751, 56 | "valid": false 57 | } 58 | ] 59 | }, 60 | { 61 | "description": "invalid instance should not raise error when float division = inf", 62 | "schema": {"type": "integer", "multipleOf": 0.123456789}, 63 | "tests": [ 64 | { 65 | "description": "always invalid, but naive implementations may raise an overflow error", 66 | "data": 1e308, 67 | "valid": false 68 | } 69 | ] 70 | } 71 | ] 72 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/unknownKeyword.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "$id inside an unknown keyword is not a real identifier", 4 | "comment": "the implementation must not be confused by an $id in locations we do not know how to parse", 5 | "schema": { 6 | "definitions": { 7 | "id_in_unknown0": { 8 | "not": { 9 | "array_of_schemas": [ 10 | { 11 | "$id": "https://localhost:1234/unknownKeyword/my_identifier.json", 12 | "type": "null" 13 | } 14 | ] 15 | } 16 | }, 17 | "real_id_in_schema": { 18 | "$id": "https://localhost:1234/unknownKeyword/my_identifier.json", 19 | "type": "string" 20 | }, 21 | "id_in_unknown1": { 22 | "not": { 23 | "object_of_schemas": { 24 | "foo": { 25 | "$id": "https://localhost:1234/unknownKeyword/my_identifier.json", 26 | "type": "integer" 27 | } 28 | } 29 | } 30 | } 31 | }, 32 | "anyOf": [ 33 | { "$ref": "#/definitions/id_in_unknown0" }, 34 | { "$ref": "#/definitions/id_in_unknown1" }, 35 | { "$ref": "https://localhost:1234/unknownKeyword/my_identifier.json" } 36 | ] 37 | }, 38 | "tests": [ 39 | { 40 | "description": "type matches second anyOf, which has a real schema in it", 41 | "data": "a string", 42 | "valid": true 43 | }, 44 | { 45 | "description": "type matches non-schema in first anyOf", 46 | "data": null, 47 | "valid": false 48 | }, 49 | { 50 | "description": "type matches non-schema in third anyOf", 51 | "data": 1, 52 | "valid": false 53 | } 54 | ] 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/default.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "invalid type for default", 4 | "schema": { 5 | "properties": { 6 | "foo": { 7 | "type": "integer", 8 | "default": [] 9 | } 10 | } 11 | }, 12 | "tests": [ 13 | { 14 | "description": "valid when property is specified", 15 | "data": {"foo": 13}, 16 | "valid": true 17 | }, 18 | { 19 | "description": "still valid when the invalid default is used", 20 | "data": {}, 21 | "valid": true 22 | } 23 | ] 24 | }, 25 | { 26 | "description": "invalid string value for default", 27 | "schema": { 28 | "properties": { 29 | "bar": { 30 | "type": "string", 31 | "minLength": 4, 32 | "default": "bad" 33 | } 34 | } 35 | }, 36 | "tests": [ 37 | { 38 | "description": "valid when property is specified", 39 | "data": {"bar": "good"}, 40 | "valid": true 41 | }, 42 | { 43 | "description": "still valid when the invalid default is used", 44 | "data": {}, 45 | "valid": true 46 | } 47 | ] 48 | }, 49 | { 50 | "description": "the default keyword does not do anything if the property is missing", 51 | "schema": { 52 | "type": "object", 53 | "properties": { 54 | "alpha": { 55 | "type": "number", 56 | "maximum": 3, 57 | "default": 5 58 | } 59 | } 60 | }, 61 | "tests": [ 62 | { 63 | "description": "an explicit property value is checked against maximum (passing)", 64 | "data": { "alpha": 1 }, 65 | "valid": true 66 | }, 67 | { 68 | "description": "an explicit property value is checked against maximum (failing)", 69 | "data": { "alpha": 5 }, 70 | "valid": false 71 | }, 72 | { 73 | "description": "missing properties are not filled in with the default", 74 | "data": {}, 75 | "valid": true 76 | } 77 | ] 78 | } 79 | ] 80 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/content.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of string-encoded content based on media type", 4 | "schema": { 5 | "contentMediaType": "application/json" 6 | }, 7 | "tests": [ 8 | { 9 | "description": "a valid JSON document", 10 | "data": "{\"foo\": \"bar\"}", 11 | "valid": true 12 | }, 13 | { 14 | "description": "an invalid JSON document", 15 | "data": "{:}", 16 | "valid": false 17 | }, 18 | { 19 | "description": "ignores non-strings", 20 | "data": 100, 21 | "valid": true 22 | } 23 | ] 24 | }, 25 | { 26 | "description": "validation of binary string-encoding", 27 | "schema": { 28 | "contentEncoding": "base64" 29 | }, 30 | "tests": [ 31 | { 32 | "description": "a valid base64 string", 33 | "data": "eyJmb28iOiAiYmFyIn0K", 34 | "valid": true 35 | }, 36 | { 37 | "description": "an invalid base64 string (% is not a valid character)", 38 | "data": "eyJmb28iOi%iYmFyIn0K", 39 | "valid": false 40 | }, 41 | { 42 | "description": "ignores non-strings", 43 | "data": 100, 44 | "valid": true 45 | } 46 | ] 47 | }, 48 | { 49 | "description": "validation of binary-encoded media type documents", 50 | "schema": { 51 | "contentMediaType": "application/json", 52 | "contentEncoding": "base64" 53 | }, 54 | "tests": [ 55 | { 56 | "description": "a valid base64-encoded JSON document", 57 | "data": "eyJmb28iOiAiYmFyIn0K", 58 | "valid": true 59 | }, 60 | { 61 | "description": "a validly-encoded invalid JSON document", 62 | "data": "ezp9Cg==", 63 | "valid": false 64 | }, 65 | { 66 | "description": "an invalid base64 string that is valid JSON", 67 | "data": "{}", 68 | "valid": false 69 | }, 70 | { 71 | "description": "ignores non-strings", 72 | "data": 100, 73 | "valid": true 74 | } 75 | ] 76 | } 77 | ] 78 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/hostname.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of host names", 4 | "schema": {"format": "hostname"}, 5 | "tests": [ 6 | { 7 | "description": "a valid host name", 8 | "data": "www.example.com", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a valid punycoded IDN hostname", 13 | "data": "xn--4gbwdl.xn--wgbh1c", 14 | "valid": true 15 | }, 16 | { 17 | "description": "a host name starting with an illegal character", 18 | "data": "-a-host-name-that-starts-with--", 19 | "valid": false 20 | }, 21 | { 22 | "description": "a host name containing illegal characters", 23 | "data": "not_a_valid_host_name", 24 | "valid": false 25 | }, 26 | { 27 | "description": "a host name with a component too long", 28 | "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", 29 | "valid": false 30 | }, 31 | { 32 | "description": "starts with hyphen", 33 | "data": "-hostname", 34 | "valid": false 35 | }, 36 | { 37 | "description": "ends with hyphen", 38 | "data": "hostname-", 39 | "valid": false 40 | }, 41 | { 42 | "description": "starts with underscore", 43 | "data": "_hostname", 44 | "valid": false 45 | }, 46 | { 47 | "description": "ends with underscore", 48 | "data": "hostname_", 49 | "valid": false 50 | }, 51 | { 52 | "description": "contains underscore", 53 | "data": "host_name", 54 | "valid": false 55 | }, 56 | { 57 | "description": "maximum label length", 58 | "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com", 59 | "valid": true 60 | }, 61 | { 62 | "description": "exceeds maximum label length", 63 | "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com", 64 | "valid": false 65 | } 66 | ] 67 | } 68 | ] 69 | -------------------------------------------------------------------------------- /test/issue-149-entry-selection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using nlohmann::json; 6 | using nlohmann::json_uri; 7 | using nlohmann::json_schema::json_validator; 8 | 9 | namespace 10 | { 11 | 12 | static int error_count; 13 | #define EXPECT_EQ(a, b) \ 14 | do { \ 15 | if (a != b) { \ 16 | std::cerr << "Failed: '" << a << "' != '" << b << "'\n"; \ 17 | error_count++; \ 18 | } \ 19 | } while (0) 20 | 21 | // The schema is defined based upon a string literal 22 | static json person_schema = R"( 23 | { 24 | "$schema": "http://json-schema.org/draft-07/schema#", 25 | "type": "integer", 26 | "definitions": { 27 | "A": { 28 | "type": "object", 29 | "properties": { 30 | "b": { 31 | "$ref": "#/definitions/B" 32 | } 33 | } 34 | }, 35 | "B": { 36 | "type": "integer" 37 | } 38 | } 39 | })"_json; 40 | 41 | class store_err_handler : public nlohmann::json_schema::basic_error_handler 42 | { 43 | void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override 44 | { 45 | nlohmann::json_schema::basic_error_handler::error(ptr, instance, message); 46 | std::cerr << "ERROR: '" << ptr << "' - '" << instance << "': " << message << "\n"; 47 | failed.push_back(ptr); 48 | } 49 | 50 | public: 51 | std::vector failed; 52 | 53 | void reset() override 54 | { 55 | nlohmann::json_schema::basic_error_handler::reset(); 56 | failed.clear(); 57 | } 58 | }; 59 | 60 | } // namespace 61 | 62 | static json_validator validator(person_schema); 63 | 64 | int main(void) 65 | { 66 | store_err_handler err; 67 | 68 | validator.validate(1, err); // OK 69 | EXPECT_EQ(err.failed.size(), 0); 70 | err.reset(); 71 | 72 | validator.validate("1", err); // no name 73 | EXPECT_EQ(err.failed.size(), 1); 74 | err.reset(); 75 | 76 | validator.validate(1, err, json_uri("#/definitions/B")); 77 | EXPECT_EQ(err.failed.size(), 0); 78 | err.reset(); 79 | 80 | validator.validate("1", err, json_uri("#/definitions/B")); 81 | EXPECT_EQ(err.failed.size(), 1); 82 | err.reset(); 83 | 84 | validator.validate({{"b", 1}}, err, json_uri("#/definitions/A")); 85 | EXPECT_EQ(err.failed.size(), 0); 86 | err.reset(); 87 | 88 | validator.validate({{"b", "1"}}, err, json_uri("#/definitions/A")); 89 | EXPECT_EQ(err.failed.size(), 1); 90 | err.reset(); 91 | 92 | return error_count; 93 | } 94 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/non-bmp-regex.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "Proper UTF-16 surrogate pair handling: pattern", 4 | "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", 5 | "schema": { "pattern": "^🐲*$" }, 6 | "tests": [ 7 | { 8 | "description": "matches empty", 9 | "data": "", 10 | "valid": true 11 | }, 12 | { 13 | "description": "matches single", 14 | "data": "🐲", 15 | "valid": true 16 | }, 17 | { 18 | "description": "matches two", 19 | "data": "🐲🐲", 20 | "valid": true 21 | }, 22 | { 23 | "description": "doesn't match one", 24 | "data": "🐉", 25 | "valid": false 26 | }, 27 | { 28 | "description": "doesn't match two", 29 | "data": "🐉🐉", 30 | "valid": false 31 | }, 32 | { 33 | "description": "doesn't match one ASCII", 34 | "data": "D", 35 | "valid": false 36 | }, 37 | { 38 | "description": "doesn't match two ASCII", 39 | "data": "DD", 40 | "valid": false 41 | } 42 | ] 43 | }, 44 | { 45 | "description": "Proper UTF-16 surrogate pair handling: patternProperties", 46 | "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", 47 | "schema": { 48 | "patternProperties": { 49 | "^🐲*$": { 50 | "type": "integer" 51 | } 52 | } 53 | }, 54 | "tests": [ 55 | { 56 | "description": "matches empty", 57 | "data": { "": 1 }, 58 | "valid": true 59 | }, 60 | { 61 | "description": "matches single", 62 | "data": { "🐲": 1 }, 63 | "valid": true 64 | }, 65 | { 66 | "description": "matches two", 67 | "data": { "🐲🐲": 1 }, 68 | "valid": true 69 | }, 70 | { 71 | "description": "doesn't match one", 72 | "data": { "🐲": "hello" }, 73 | "valid": false 74 | }, 75 | { 76 | "description": "doesn't match two", 77 | "data": { "🐲🐲": "hello" }, 78 | "valid": false 79 | } 80 | ] 81 | } 82 | ] 83 | -------------------------------------------------------------------------------- /test/issue-117-format-error.cpp: -------------------------------------------------------------------------------- 1 | // issue-00-format-error.cpp 2 | 3 | #include "nlohmann/json-schema.hpp" 4 | #include "nlohmann/json.hpp" 5 | #include 6 | 7 | static int error_count = 0; 8 | 9 | #define CHECK_THROW(x, msg) \ 10 | { \ 11 | bool fail = false; \ 12 | try { \ 13 | x; \ 14 | } catch (std::exception &) { \ 15 | fail = true; \ 16 | } \ 17 | if (fail == false) { \ 18 | ++error_count; \ 19 | std::cout << msg << std::endl; \ 20 | } \ 21 | } 22 | 23 | #define CHECK_NO_THROW(x, msg) \ 24 | { \ 25 | bool fail = false; \ 26 | std::string exception_error; \ 27 | try { \ 28 | x; \ 29 | } catch (std::exception & e) { \ 30 | fail = true; \ 31 | exception_error = e.what(); \ 32 | } \ 33 | if (fail == true) { \ 34 | ++error_count; \ 35 | std::cout << msg << ": " << exception_error << std::endl; \ 36 | } \ 37 | } 38 | 39 | using json = nlohmann::json; 40 | using validator = nlohmann::json_schema::json_validator; 41 | 42 | json schema_with_format = json::parse(R"( 43 | { 44 | "type": "object", 45 | "properties": { 46 | "str": { 47 | "type": "string", 48 | "format": "placeholder" 49 | } 50 | } 51 | } 52 | )"); 53 | 54 | int main() 55 | { 56 | // check that if we get validator without format checker we get error at schema loading 57 | validator without_format_checker; 58 | 59 | CHECK_THROW(without_format_checker.set_root_schema(schema_with_format), "validator without format checker must fail at schema loading"); 60 | 61 | // check that with format checker all works fine 62 | validator with_format_checker{nullptr, [](const std::string &, const std::string &) {}}; 63 | 64 | CHECK_NO_THROW(with_format_checker.set_root_schema(schema_with_format), "schema must be succesed by validator with format checker"); 65 | 66 | CHECK_NO_THROW(with_format_checker.validate(json{{"str", "placeholder"}}), "validator must not throw while validation schema with format"); 67 | 68 | return error_count; 69 | } 70 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date-time.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of date-time strings", 4 | "schema": {"format": "date-time"}, 5 | "tests": [ 6 | { 7 | "description": "a valid date-time string", 8 | "data": "1963-06-19T08:30:06.283185Z", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a valid date-time string without second fraction", 13 | "data": "1963-06-19T08:30:06Z", 14 | "valid": true 15 | }, 16 | { 17 | "description": "a valid date-time string with plus offset", 18 | "data": "1937-01-01T12:00:27.87+00:20", 19 | "valid": true 20 | }, 21 | { 22 | "description": "a valid date-time string with minus offset", 23 | "data": "1990-12-31T15:59:50.123-08:00", 24 | "valid": true 25 | }, 26 | { 27 | "description": "a invalid day in date-time string", 28 | "data": "1990-02-31T15:59:60.123-08:00", 29 | "valid": false 30 | }, 31 | { 32 | "description": "an invalid offset in date-time string", 33 | "data": "1990-12-31T15:59:60-24:00", 34 | "valid": false 35 | }, 36 | { 37 | "description": "an invalid date-time string", 38 | "data": "06/19/1963 08:30:06 PST", 39 | "valid": false 40 | }, 41 | { 42 | "description": "case-insensitive T and Z", 43 | "data": "1963-06-19t08:30:06.283185z", 44 | "valid": true 45 | }, 46 | { 47 | "description": "only RFC3339 not all of ISO 8601 are valid", 48 | "data": "2013-350T01:01:01", 49 | "valid": false 50 | }, 51 | { 52 | "description": "invalid non-padded month dates", 53 | "data": "1963-6-19T08:30:06.283185Z", 54 | "valid": false 55 | }, 56 | { 57 | "description": "invalid non-padded day dates", 58 | "data": "1963-06-1T08:30:06.283185Z", 59 | "valid": false 60 | }, 61 | { 62 | "description": "non-ascii digits should be rejected in the date portion", 63 | "data": "1963-06-1৪T00:00:00Z", 64 | "valid": false 65 | }, 66 | { 67 | "description": "non-ascii digits should be rejected in the time portion", 68 | "data": "1963-06-11T0৪:00:00Z", 69 | "valid": false 70 | } 71 | ] 72 | } 73 | ] 74 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | from conan import ConanFile 5 | from conan.tools.cmake import cmake_layout, CMake, CMakeToolchain 6 | from conans.tools import load 7 | from conans import tools as ctools 8 | 9 | def get_version(): 10 | try: 11 | version = os.getenv('PROJECT_VERSION', None) 12 | if version: 13 | return version 14 | 15 | content = load('CMakeLists.txt') 16 | version = re.search('set\(PROJECT_VERSION (.*)\)', content).group(1) 17 | return version.strip() 18 | except: 19 | return None 20 | 21 | class JsonSchemaValidatorConan(ConanFile): 22 | name = 'JsonSchemaValidator' 23 | version = get_version() 24 | url = 'https://github.com/pboettch/json-schema-validator' 25 | license = 'MIT' 26 | 27 | settings = 'os', 'compiler', 'build_type', 'arch' 28 | 29 | options = { 30 | 'shared': [True, False], 31 | 'fPIC': [True, False], 32 | 'build_examples': [True, False], 33 | 'build_tests': [True, False], 34 | 'test_coverage': [True, False], 35 | } 36 | 37 | default_options = { 38 | 'shared': False, 39 | 'fPIC': True, 40 | 'build_examples': True, 41 | 'build_tests': False, 42 | 'test_coverage': False, 43 | } 44 | 45 | generators = 'CMakeDeps', 'CMakeToolchain', 'VirtualBuildEnv', 'VirtualRunEnv' 46 | 47 | exports_sources = [ 48 | 'CMakeLists.txt', 49 | 'conanfile.py', 50 | 'cmake/*', 51 | 'src/*', 52 | 'example/*', 53 | 'test/*', 54 | ] 55 | 56 | requires = [ 57 | 'nlohmann_json/3.11.2' 58 | ] 59 | 60 | def generate(self): 61 | tc = CMakeToolchain(self) 62 | tc.variables['JSON_VALIDATOR_BUILD_EXAMPLES'] = self.options.build_examples 63 | tc.variables['JSON_VALIDATOR_BUILD_TESTS'] = self.options.build_tests 64 | tc.variables['JSON_VALIDATOR_SHARED_LIBS '] = self.options.shared 65 | tc.variables['JSON_VALIDATOR_TEST_COVERAGE '] = self.options.test_coverage 66 | tc.generate() 67 | 68 | def layout(self): 69 | cmake_layout(self) 70 | 71 | def build(self): 72 | cmake = CMake(self) 73 | cmake.configure() 74 | cmake.verbose = True 75 | cmake.build() 76 | 77 | def package(self): 78 | cmake = CMake(self) 79 | cmake.install() 80 | 81 | def package_info(self): 82 | includedir = os.path.join(self.package_folder, "include") 83 | self.cpp_info.includedirs = [includedir] 84 | 85 | libdir = os.path.join(self.package_folder, "lib") 86 | self.cpp_info.libdirs = [libdir] 87 | self.cpp_info.libs += ctools.collect_libs(self, libdir) 88 | 89 | bindir = os.path.join(self.package_folder, "bin") 90 | self.output.info("Appending PATH environment variable: {}".format(bindir)) 91 | self.env_info.PATH.append(bindir) 92 | 93 | self.user_info.VERSION = self.version 94 | -------------------------------------------------------------------------------- /test/json-schema-validate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * JSON schema validator for JSON for modern C++ 3 | * 4 | * Copyright (c) 2016-2019 Patrick Boettcher . 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | */ 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | using nlohmann::json; 15 | using nlohmann::json_uri; 16 | using nlohmann::json_schema::json_validator; 17 | 18 | static void usage(const char *name) 19 | { 20 | std::cerr << "Usage: " << name << " < \n"; 21 | exit(EXIT_FAILURE); 22 | } 23 | 24 | static void loader(const json_uri &uri, json &schema) 25 | { 26 | std::string filename = "./" + uri.path(); 27 | std::ifstream lf(filename); 28 | if (!lf.good()) 29 | throw std::invalid_argument("could not open " + uri.url() + " tried with " + filename); 30 | try { 31 | lf >> schema; 32 | } catch (const std::exception &e) { 33 | throw e; 34 | } 35 | } 36 | 37 | class custom_error_handler : public nlohmann::json_schema::basic_error_handler 38 | { 39 | void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override 40 | { 41 | nlohmann::json_schema::basic_error_handler::error(ptr, instance, message); 42 | std::cerr << "ERROR: '" << ptr << "' - '" << instance << "': " << message << "\n"; 43 | } 44 | }; 45 | 46 | int main(int argc, char *argv[]) 47 | { 48 | if (argc != 2) 49 | usage(argv[0]); 50 | 51 | std::ifstream f(argv[1]); 52 | if (!f.good()) { 53 | std::cerr << "could not open " << argv[1] << " for reading\n"; 54 | usage(argv[0]); 55 | } 56 | 57 | // 1) Read the schema for the document you want to validate 58 | json schema; 59 | try { 60 | f >> schema; 61 | } catch (const std::exception &e) { 62 | std::cerr << e.what() << " at " << f.tellg() << " - while parsing the schema\n"; 63 | return EXIT_FAILURE; 64 | } 65 | 66 | // 2) create the validator and 67 | json_validator validator(loader, 68 | nlohmann::json_schema::default_string_format_check); 69 | 70 | try { 71 | // insert this schema as the root to the validator 72 | // this resolves remote-schemas, sub-schemas and references via the given loader-function 73 | validator.set_root_schema(schema); 74 | } catch (const std::exception &e) { 75 | std::cerr << "setting root schema failed\n"; 76 | std::cerr << e.what() << "\n"; 77 | } 78 | 79 | // 3) do the actual validation of the document 80 | json document; 81 | 82 | try { 83 | std::cin >> document; 84 | } catch (const std::exception &e) { 85 | std::cerr << "json parsing failed: " << e.what() << " at offset: " << std::cin.tellg() << "\n"; 86 | return EXIT_FAILURE; 87 | } 88 | 89 | custom_error_handler err; 90 | validator.validate(document, err); 91 | 92 | if (err) { 93 | std::cerr << "schema validation failed\n"; 94 | return EXIT_FAILURE; 95 | } 96 | 97 | std::cerr << "document is valid\n"; 98 | 99 | return EXIT_SUCCESS; 100 | } 101 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uuid.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "uuid format", 4 | "schema": { 5 | "format": "uuid" 6 | }, 7 | "tests": [ 8 | { 9 | "description": "all upper-case", 10 | "data": "2EB8AA08-AA98-11EA-B4AA-73B441D16380", 11 | "valid": true 12 | }, 13 | { 14 | "description": "all lower-case", 15 | "data": "2eb8aa08-aa98-11ea-b4aa-73b441d16380", 16 | "valid": true 17 | }, 18 | { 19 | "description": "mixed case", 20 | "data": "2eb8aa08-AA98-11ea-B4Aa-73B441D16380", 21 | "valid": true 22 | }, 23 | { 24 | "description": "all zeroes is valid", 25 | "data": "00000000-0000-0000-0000-000000000000", 26 | "valid": true 27 | }, 28 | { 29 | "description": "wrong length", 30 | "data": "2eb8aa08-aa98-11ea-b4aa-73b441d1638", 31 | "valid": false 32 | }, 33 | { 34 | "description": "missing section", 35 | "data": "2eb8aa08-aa98-11ea-73b441d16380", 36 | "valid": false 37 | }, 38 | { 39 | "description": "bad characters (not hex)", 40 | "data": "2eb8aa08-aa98-11ea-b4ga-73b441d16380", 41 | "valid": false 42 | }, 43 | { 44 | "description": "no dashes", 45 | "data": "2eb8aa08aa9811eab4aa73b441d16380", 46 | "valid": false 47 | }, 48 | { 49 | "description": "too few dashes", 50 | "data": "2eb8aa08aa98-11ea-b4aa73b441d16380", 51 | "valid": false 52 | }, 53 | { 54 | "description": "too many dashes", 55 | "data": "2eb8-aa08-aa98-11ea-b4aa73b44-1d16380", 56 | "valid": false 57 | }, 58 | { 59 | "description": "dashes in the wrong spot", 60 | "data": "2eb8aa08aa9811eab4aa73b441d16380----", 61 | "valid": false 62 | }, 63 | { 64 | "description": "valid version 4", 65 | "data": "98d80576-482e-427f-8434-7f86890ab222", 66 | "valid": true 67 | }, 68 | { 69 | "description": "valid version 5", 70 | "data": "99c17cbb-656f-564a-940f-1a4568f03487", 71 | "valid": true 72 | }, 73 | { 74 | "description": "hypothetical version 6", 75 | "data": "99c17cbb-656f-664a-940f-1a4568f03487", 76 | "valid": true 77 | }, 78 | { 79 | "description": "hypothetical version 15", 80 | "data": "99c17cbb-656f-f64a-940f-1a4568f03487", 81 | "valid": true 82 | } 83 | ] 84 | } 85 | ] 86 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(nlohmann_json_schema_validator PRIVATE 2 | smtp-address-validator.cpp 3 | json-schema-draft7.json.cpp 4 | json-uri.cpp 5 | json-validator.cpp 6 | json-patch.cpp 7 | string-format-check.cpp 8 | ) 9 | target_include_directories(nlohmann_json_schema_validator PUBLIC 10 | $ 11 | $ 12 | ) 13 | 14 | set_target_properties(nlohmann_json_schema_validator PROPERTIES 15 | PUBLIC_HEADER nlohmann/json-schema.hpp) 16 | 17 | # TODO: Why would this need to be if guarded? 18 | if (JSON_VALIDATOR_SHARED_LIBS) 19 | target_compile_definitions(nlohmann_json_schema_validator PRIVATE 20 | -DJSON_SCHEMA_VALIDATOR_EXPORTS) 21 | endif () 22 | 23 | # TODO: Consider setting minimum cxx standard instead 24 | target_compile_features(nlohmann_json_schema_validator PUBLIC 25 | cxx_range_for) # for C++11 - flags 26 | 27 | # TODO: This should be handled by the CI/presets, not the cmake 28 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR 29 | "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 30 | target_compile_options(nlohmann_json_schema_validator 31 | PRIVATE 32 | -Wall -Wextra -Wshadow) 33 | endif () 34 | 35 | # TODO: gcc support for <4.9 should be removed 36 | # regex with boost if gcc < 4.9 - default is std::regex 37 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 38 | if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9.0") 39 | find_package(Boost COMPONENTS regex) 40 | if (NOT Boost_FOUND) 41 | message(STATUS "GCC less then 4.9 and boost-regex NOT found - no regex used") 42 | target_compile_definitions(nlohmann_json_schema_validator PRIVATE -DJSON_SCHEMA_NO_REGEX) 43 | else () 44 | message(STATUS "GCC less then 4.9 and boost-regex FOUND - using boost::regex") 45 | target_compile_definitions(nlohmann_json_schema_validator PRIVATE -DJSON_SCHEMA_BOOST_REGEX) 46 | target_include_directories(nlohmann_json_schema_validator PRIVATE ${Boost_INCLUDE_DIRS}) 47 | target_link_libraries(nlohmann_json_schema_validator PRIVATE ${Boost_LIBRARIES}) 48 | endif () 49 | endif () 50 | endif () 51 | 52 | target_link_libraries(nlohmann_json_schema_validator PUBLIC 53 | nlohmann_json::nlohmann_json) 54 | 55 | if (JSON_VALIDATOR_INSTALL) 56 | # Normal installation target to system. When using scikit-build check python subdirectory 57 | install(TARGETS nlohmann_json_schema_validator 58 | EXPORT nlohmann_json_schema_validatorTargets 59 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT nlohmann_json_schema_validator_Runtime 60 | NAMELINK_COMPONENT nlohmann_json_schema_validator_Development 61 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT nlohmann_json_schema_validator_Development 62 | PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nlohmann COMPONENT nlohmann_json_schema_validator_Development 63 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT nlohmann_json_schema_validator_Runtime) 64 | endif () 65 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/bignum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "integer", 4 | "schema": { "type": "integer" }, 5 | "tests": [ 6 | { 7 | "description": "a bignum is an integer", 8 | "data": 12345678910111213141516171819202122232425262728293031, 9 | "valid": true 10 | }, 11 | { 12 | "description": "a negative bignum is an integer", 13 | "data": -12345678910111213141516171819202122232425262728293031, 14 | "valid": true 15 | } 16 | ] 17 | }, 18 | { 19 | "description": "number", 20 | "schema": { "type": "number" }, 21 | "tests": [ 22 | { 23 | "description": "a bignum is a number", 24 | "data": 98249283749234923498293171823948729348710298301928331, 25 | "valid": true 26 | }, 27 | { 28 | "description": "a negative bignum is a number", 29 | "data": -98249283749234923498293171823948729348710298301928331, 30 | "valid": true 31 | } 32 | ] 33 | }, 34 | { 35 | "description": "string", 36 | "schema": { "type": "string" }, 37 | "tests": [ 38 | { 39 | "description": "a bignum is not a string", 40 | "data": 98249283749234923498293171823948729348710298301928331, 41 | "valid": false 42 | } 43 | ] 44 | }, 45 | { 46 | "description": "integer comparison", 47 | "schema": { "maximum": 18446744073709551615 }, 48 | "tests": [ 49 | { 50 | "description": "comparison works for high numbers", 51 | "data": 18446744073709551600, 52 | "valid": true 53 | } 54 | ] 55 | }, 56 | { 57 | "description": "float comparison with high precision", 58 | "schema": { 59 | "exclusiveMaximum": 972783798187987123879878123.18878137 60 | }, 61 | "tests": [ 62 | { 63 | "description": "comparison works for high numbers", 64 | "data": 972783798187987123879878123.188781371, 65 | "valid": false 66 | } 67 | ] 68 | }, 69 | { 70 | "description": "integer comparison", 71 | "schema": { "minimum": -18446744073709551615 }, 72 | "tests": [ 73 | { 74 | "description": "comparison works for very negative numbers", 75 | "data": -18446744073709551600, 76 | "valid": true 77 | } 78 | ] 79 | }, 80 | { 81 | "description": "float comparison with high precision on negative numbers", 82 | "schema": { 83 | "exclusiveMinimum": -972783798187987123879878123.18878137 84 | }, 85 | "tests": [ 86 | { 87 | "description": "comparison works for very negative numbers", 88 | "data": -972783798187987123879878123.188781371, 89 | "valid": false 90 | } 91 | ] 92 | } 93 | ] 94 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/required.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "required validation", 4 | "schema": { 5 | "properties": { 6 | "foo": {}, 7 | "bar": {} 8 | }, 9 | "required": ["foo"] 10 | }, 11 | "tests": [ 12 | { 13 | "description": "present required property is valid", 14 | "data": {"foo": 1}, 15 | "valid": true 16 | }, 17 | { 18 | "description": "non-present required property is invalid", 19 | "data": {"bar": 1}, 20 | "valid": false 21 | }, 22 | { 23 | "description": "ignores arrays", 24 | "data": [], 25 | "valid": true 26 | }, 27 | { 28 | "description": "ignores strings", 29 | "data": "", 30 | "valid": true 31 | }, 32 | { 33 | "description": "ignores other non-objects", 34 | "data": 12, 35 | "valid": true 36 | } 37 | ] 38 | }, 39 | { 40 | "description": "required default validation", 41 | "schema": { 42 | "properties": { 43 | "foo": {} 44 | } 45 | }, 46 | "tests": [ 47 | { 48 | "description": "not required by default", 49 | "data": {}, 50 | "valid": true 51 | } 52 | ] 53 | }, 54 | { 55 | "description": "required with empty array", 56 | "schema": { 57 | "properties": { 58 | "foo": {} 59 | }, 60 | "required": [] 61 | }, 62 | "tests": [ 63 | { 64 | "description": "property not required", 65 | "data": {}, 66 | "valid": true 67 | } 68 | ] 69 | }, 70 | { 71 | "description": "required with escaped characters", 72 | "schema": { 73 | "required": [ 74 | "foo\nbar", 75 | "foo\"bar", 76 | "foo\\bar", 77 | "foo\rbar", 78 | "foo\tbar", 79 | "foo\fbar" 80 | ] 81 | }, 82 | "tests": [ 83 | { 84 | "description": "object with all properties present is valid", 85 | "data": { 86 | "foo\nbar": 1, 87 | "foo\"bar": 1, 88 | "foo\\bar": 1, 89 | "foo\rbar": 1, 90 | "foo\tbar": 1, 91 | "foo\fbar": 1 92 | }, 93 | "valid": true 94 | }, 95 | { 96 | "description": "object with some properties missing is invalid", 97 | "data": { 98 | "foo\nbar": "1", 99 | "foo\"bar": "1" 100 | }, 101 | "valid": false 102 | } 103 | ] 104 | } 105 | ] 106 | -------------------------------------------------------------------------------- /test/issue-189-default-values.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using nlohmann::json; 5 | using nlohmann::json_uri; 6 | using nlohmann::json_schema::json_validator; 7 | 8 | static const json rectangle_schema = R"( 9 | { 10 | "$schema": "http://json-schema.org/draft-07/schema#", 11 | "properties": { 12 | "width": { 13 | "$ref": "#/definitions/length", 14 | "default": 20 15 | }, 16 | "height": { 17 | "$ref": "#/definitions/length" 18 | } 19 | }, 20 | "definitions": { 21 | "length": { 22 | "type": "integer", 23 | "default": 10 24 | } 25 | } 26 | })"_json; 27 | 28 | static const json quad_schema = R"( 29 | { 30 | "$schema": "http://json-schema.org/draft-07/schema#", 31 | "properties": { 32 | "width": { 33 | "$ref": "#/properties/height", 34 | "default": 20 35 | }, 36 | "height": { 37 | "$ref": "#/definitions/length" 38 | }, 39 | "depth": { 40 | "$ref": "default_schema#/definitions/defaultLength" 41 | }, 42 | "time": { 43 | "$ref": "#/definitions/time" 44 | } 45 | }, 46 | "definitions": { 47 | "length": { 48 | "$ref": "default_schema#/definitions/defaultLength", 49 | "default": 10 50 | }, 51 | "time": { 52 | "type": "integer", 53 | "default": 15 54 | } 55 | } 56 | })"_json; 57 | 58 | static const json default_schema = R"( 59 | { 60 | "$schema": "http://json-schema.org/draft-07/schema#", 61 | "definitions": { 62 | "defaultLength": { 63 | "default": 5 64 | } 65 | } 66 | })"_json; 67 | 68 | static void loader(const json_uri &uri, json &schema) 69 | { 70 | schema = default_schema; 71 | } 72 | 73 | int main(void) 74 | { 75 | json_validator validator(loader); 76 | 77 | validator.set_root_schema(quad_schema); 78 | 79 | { 80 | json empty_quad = R"({})"_json; 81 | 82 | const auto default_patch = validator.validate(empty_quad); 83 | const auto actual = empty_quad.patch(default_patch); 84 | 85 | const auto expected = R"({"height":10,"width":20,"depth":5,"time":15})"_json; 86 | if (actual != expected) { 87 | std::cerr << "Patch with defaults contains wrong value: '" << actual << "' instead of expected '" << expected.dump() << "'" << std::endl; 88 | return 1; 89 | } 90 | } 91 | 92 | validator.set_root_schema(rectangle_schema); 93 | 94 | { 95 | json empty_rectangle = R"({})"_json; 96 | 97 | const auto default_patch = validator.validate(empty_rectangle); 98 | const auto actual = empty_rectangle.patch(default_patch); 99 | 100 | // height must be 10 according to the default specified in the length definition while width must be 10 overridden by the width element 101 | const auto expected = R"({"height":10,"width":20})"_json; 102 | if (actual != expected) { 103 | std::cerr << "Patch with defaults contains wrong value: '" << actual << "' instead of expected '" << expected.dump() << "'" << std::endl; 104 | return 1; 105 | } 106 | } 107 | 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(JSON_SCHEMA_TEST_PREFIX "JSON-Suite" CACHE STRING "prefix for JSON-tests added to ctest") 2 | 3 | set(DRAFT "draft7") 4 | 5 | # find schema-test-suite 6 | find_path(JSON_SCHEMA_TEST_SUITE_PATH 7 | NAMES 8 | tests/${DRAFT}) 9 | 10 | if (NOT JSON_SCHEMA_TEST_SUITE_PATH) 11 | message(STATUS "Set JSON_SCHEMA_TEST_SUITE_PATH to a path in which JSON-Schema-Test-Suite is located (github.com/json-schema-org/JSON-Schema-Test-Suite). Using internal test-suite which might be out of date.") 12 | set(JSON_SCHEMA_TEST_SUITE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 13 | endif() 14 | 15 | if(JSON_SCHEMA_TEST_SUITE_PATH) 16 | # json-schema-validator-tester 17 | add_executable(json-schema-test json-schema-test.cpp) 18 | target_link_libraries(json-schema-test nlohmann_json_schema_validator) 19 | target_compile_definitions(json-schema-test 20 | PRIVATE 21 | JSON_SCHEMA_TEST_SUITE_PATH="${JSON_SCHEMA_TEST_SUITE_PATH}") 22 | 23 | option(JSON_SCHEMA_ENABLE_OPTIONAL_TESTS "Enable optional tests of the JSONSchema Test Suite" ON) 24 | 25 | # create tests foreach test-file 26 | file(GLOB TEST_FILES ${JSON_SCHEMA_TEST_SUITE_PATH}/tests/${DRAFT}/*.json) 27 | 28 | foreach(TEST_FILE ${TEST_FILES}) 29 | get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) 30 | add_test(NAME "${JSON_SCHEMA_TEST_PREFIX}::${TEST_NAME}" 31 | COMMAND ${PIPE_IN_TEST_SCRIPT} $ ${TEST_FILE}) 32 | endforeach() 33 | 34 | if (JSON_SCHEMA_ENABLE_OPTIONAL_TESTS) 35 | file(GLOB OPT_TEST_FILES ${JSON_SCHEMA_TEST_SUITE_PATH}/tests/${DRAFT}/optional/*.json) 36 | file(GLOB FORMAT_TEST_FILES ${JSON_SCHEMA_TEST_SUITE_PATH}/tests/${DRAFT}/optional/format/*.json) 37 | 38 | foreach(TEST_FILE ${OPT_TEST_FILES}) 39 | get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) 40 | add_test(NAME "${JSON_SCHEMA_TEST_PREFIX}::Optional::${TEST_NAME}" 41 | COMMAND ${PIPE_IN_TEST_SCRIPT} $ ${TEST_FILE}) 42 | endforeach() 43 | 44 | foreach(TEST_FILE ${FORMAT_TEST_FILES}) 45 | get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) 46 | add_test(NAME "${JSON_SCHEMA_TEST_PREFIX}::Optional::Format::${TEST_NAME}" 47 | COMMAND ${PIPE_IN_TEST_SCRIPT} $ ${TEST_FILE}) 48 | endforeach() 49 | 50 | # some optional tests will fail 51 | set_tests_properties( 52 | JSON-Suite::Optional::bignum 53 | JSON-Suite::Optional::non-bmp-regex 54 | JSON-Suite::Optional::float-overflow 55 | 56 | JSON-Suite::Optional::ecmascript-regex 57 | JSON-Suite::Optional::Format::idn-hostname 58 | JSON-Suite::Optional::Format::iri-reference 59 | JSON-Suite::Optional::Format::iri 60 | JSON-Suite::Optional::Format::json-pointer 61 | JSON-Suite::Optional::Format::relative-json-pointer 62 | JSON-Suite::Optional::Format::uri-reference 63 | JSON-Suite::Optional::Format::uri-template 64 | JSON-Suite::Optional::unicode 65 | 66 | PROPERTIES 67 | WILL_FAIL ON) 68 | endif() 69 | else() 70 | endif() 71 | -------------------------------------------------------------------------------- /test/uri.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * JSON schema validator for JSON for modern C++ 3 | * 4 | * Copyright (c) 2016-2019 Patrick Boettcher . 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | */ 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | using nlohmann::json; 15 | using nlohmann::json_uri; 16 | 17 | static int errors; 18 | 19 | static void EXPECT_EQ(const std::string &a, const std::string &b) 20 | { 21 | if (a != b) { 22 | std::cerr << "Failed: '" << a << "' != '" << b << "'\n"; 23 | errors++; 24 | } 25 | } 26 | 27 | static void EXPECT_EQ(const nlohmann::json_uri &a, const std::string &b) 28 | { 29 | EXPECT_EQ(a.to_string(), b); 30 | } 31 | 32 | static void paths(json_uri start, 33 | const std::string &full, 34 | const std::string &full_path, 35 | const std::string &no_path) 36 | { 37 | EXPECT_EQ(start, full + " # "); 38 | 39 | auto a = start.derive("other.json"); 40 | EXPECT_EQ(a, full_path + "/other.json # "); 41 | 42 | auto b = a.derive("base.json"); 43 | EXPECT_EQ(b, full_path + "/base.json # "); 44 | 45 | auto c = b.derive("subdir/base.json"); 46 | EXPECT_EQ(c, full_path + "/subdir/base.json # "); 47 | 48 | auto d = c.derive("subdir2/base.json"); 49 | EXPECT_EQ(d, full_path + "/subdir/subdir2/base.json # "); 50 | 51 | auto e = c.derive("/subdir2/base.json"); 52 | EXPECT_EQ(e, no_path + "/subdir2/base.json # "); 53 | 54 | auto f = c.derive("new.json"); 55 | EXPECT_EQ(f, full_path + "/subdir/new.json # "); 56 | 57 | auto g = c.derive("/new.json"); 58 | EXPECT_EQ(g, no_path + "/new.json # "); 59 | } 60 | 61 | static void pointer_plain_name(json_uri start, 62 | const std::string &full, 63 | const std::string &full_path, 64 | const std::string &no_path) 65 | { 66 | auto a = start.derive("#/json/path"); 67 | EXPECT_EQ(a, full + " # /json/path"); 68 | 69 | a = start.derive("#/json/special_%22"); 70 | EXPECT_EQ(a, full + " # /json/special_\""); 71 | 72 | a = a.derive("#foo"); 73 | EXPECT_EQ(a, full + " # foo"); 74 | 75 | a = a.derive("#foo/looks_like_json/poiner/but/isnt"); 76 | EXPECT_EQ(a, full + " # foo/looks_like_json/poiner/but/isnt"); 77 | EXPECT_EQ(a.identifier(), "foo/looks_like_json/poiner/but/isnt"); 78 | EXPECT_EQ(a.pointer().to_string(), ""); 79 | 80 | a = a.derive("#/looks_like_json/poiner/and/it/is"); 81 | EXPECT_EQ(a, full + " # /looks_like_json/poiner/and/it/is"); 82 | EXPECT_EQ(a.pointer().to_string(), "/looks_like_json/poiner/and/it/is"); 83 | EXPECT_EQ(a.identifier(), ""); 84 | } 85 | 86 | int main(void) 87 | { 88 | json_uri empty(""); 89 | paths(empty, 90 | "", 91 | "", 92 | ""); 93 | 94 | json_uri http("http://json-schema.org/draft-07/schema#"); 95 | paths(http, 96 | "http://json-schema.org/draft-07/schema", 97 | "http://json-schema.org/draft-07", 98 | "http://json-schema.org"); 99 | 100 | pointer_plain_name(http, 101 | "http://json-schema.org/draft-07/schema", 102 | "http://json-schema.org/draft-07", 103 | "http://json-schema.org"); 104 | 105 | return errors; 106 | } 107 | -------------------------------------------------------------------------------- /example/readme.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | using nlohmann::json; 7 | using nlohmann::json_schema::json_validator; 8 | 9 | // The schema is defined based upon a string literal 10 | static json person_schema = R"( 11 | { 12 | "$schema": "http://json-schema.org/draft-07/schema#", 13 | "title": "A person", 14 | "properties": { 15 | "name": { 16 | "description": "Name", 17 | "type": "string" 18 | }, 19 | "age": { 20 | "description": "Age of the person", 21 | "type": "number", 22 | "minimum": 2, 23 | "maximum": 200 24 | }, 25 | "address":{ 26 | "type": "object", 27 | "properties":{ 28 | "street":{ 29 | "type": "string", 30 | "default": "Abbey Road" 31 | } 32 | } 33 | } 34 | }, 35 | "required": [ 36 | "name", 37 | "age" 38 | ], 39 | "type": "object" 40 | } 41 | 42 | )"_json; 43 | 44 | // The people are defined with brace initialization 45 | static json bad_person = {{"age", 42}}; 46 | static json good_person = {{"name", "Albert"}, {"age", 42}, {"address", {{"street", "Main Street"}}}}; 47 | static json good_defaulted_person = {{"name", "Knut"}, {"age", 69}, {"address", {}}}; 48 | 49 | int main() 50 | { 51 | /* json-parse the schema */ 52 | 53 | json_validator validator; // create validator 54 | 55 | try { 56 | validator.set_root_schema(person_schema); // insert root-schema 57 | } catch (const std::exception &e) { 58 | std::cerr << "Validation of schema failed, here is why: " << e.what() << "\n"; 59 | return EXIT_FAILURE; 60 | } 61 | 62 | /* json-parse the people - API of 1.0.0, default throwing error handler */ 63 | 64 | for (auto &person : {bad_person, good_person, good_defaulted_person}) { 65 | std::cout << "About to validate this person:\n" 66 | << std::setw(2) << person << std::endl; 67 | try { 68 | auto defaultPatch = validator.validate(person); // validate the document - uses the default throwing error-handler 69 | std::cout << "Validation succeeded\n"; 70 | std::cout << "Patch with defaults: " << defaultPatch.dump(2) << std::endl; 71 | } catch (const std::exception &e) { 72 | std::cerr << "Validation failed, here is why: " << e.what() << "\n"; 73 | } 74 | } 75 | 76 | /* json-parse the people - with custom error handler */ 77 | class custom_error_handler : public nlohmann::json_schema::basic_error_handler 78 | { 79 | void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override 80 | { 81 | nlohmann::json_schema::basic_error_handler::error(ptr, instance, message); 82 | std::cerr << "ERROR: '" << ptr << "' - '" << instance << "': " << message << "\n"; 83 | } 84 | }; 85 | 86 | for (auto &person : {bad_person, good_person}) { 87 | std::cout << "About to validate this person:\n" 88 | << std::setw(2) << person << std::endl; 89 | 90 | custom_error_handler err; 91 | validator.validate(person, err); // validate the document 92 | 93 | if (err) 94 | std::cerr << "Validation failed\n"; 95 | else 96 | std::cout << "Validation succeeded\n"; 97 | } 98 | 99 | return EXIT_SUCCESS; 100 | } 101 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/boolean_schema.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "boolean schema 'true'", 4 | "schema": true, 5 | "tests": [ 6 | { 7 | "description": "number is valid", 8 | "data": 1, 9 | "valid": true 10 | }, 11 | { 12 | "description": "string is valid", 13 | "data": "foo", 14 | "valid": true 15 | }, 16 | { 17 | "description": "boolean true is valid", 18 | "data": true, 19 | "valid": true 20 | }, 21 | { 22 | "description": "boolean false is valid", 23 | "data": false, 24 | "valid": true 25 | }, 26 | { 27 | "description": "null is valid", 28 | "data": null, 29 | "valid": true 30 | }, 31 | { 32 | "description": "object is valid", 33 | "data": {"foo": "bar"}, 34 | "valid": true 35 | }, 36 | { 37 | "description": "empty object is valid", 38 | "data": {}, 39 | "valid": true 40 | }, 41 | { 42 | "description": "array is valid", 43 | "data": ["foo"], 44 | "valid": true 45 | }, 46 | { 47 | "description": "empty array is valid", 48 | "data": [], 49 | "valid": true 50 | } 51 | ] 52 | }, 53 | { 54 | "description": "boolean schema 'false'", 55 | "schema": false, 56 | "tests": [ 57 | { 58 | "description": "number is invalid", 59 | "data": 1, 60 | "valid": false 61 | }, 62 | { 63 | "description": "string is invalid", 64 | "data": "foo", 65 | "valid": false 66 | }, 67 | { 68 | "description": "boolean true is invalid", 69 | "data": true, 70 | "valid": false 71 | }, 72 | { 73 | "description": "boolean false is invalid", 74 | "data": false, 75 | "valid": false 76 | }, 77 | { 78 | "description": "null is invalid", 79 | "data": null, 80 | "valid": false 81 | }, 82 | { 83 | "description": "object is invalid", 84 | "data": {"foo": "bar"}, 85 | "valid": false 86 | }, 87 | { 88 | "description": "empty object is invalid", 89 | "data": {}, 90 | "valid": false 91 | }, 92 | { 93 | "description": "array is invalid", 94 | "data": ["foo"], 95 | "valid": false 96 | }, 97 | { 98 | "description": "empty array is invalid", 99 | "data": [], 100 | "valid": false 101 | } 102 | ] 103 | } 104 | ] 105 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/not.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "not", 4 | "schema": { 5 | "not": {"type": "integer"} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "allowed", 10 | "data": "foo", 11 | "valid": true 12 | }, 13 | { 14 | "description": "disallowed", 15 | "data": 1, 16 | "valid": false 17 | } 18 | ] 19 | }, 20 | { 21 | "description": "not multiple types", 22 | "schema": { 23 | "not": {"type": ["integer", "boolean"]} 24 | }, 25 | "tests": [ 26 | { 27 | "description": "valid", 28 | "data": "foo", 29 | "valid": true 30 | }, 31 | { 32 | "description": "mismatch", 33 | "data": 1, 34 | "valid": false 35 | }, 36 | { 37 | "description": "other mismatch", 38 | "data": true, 39 | "valid": false 40 | } 41 | ] 42 | }, 43 | { 44 | "description": "not more complex schema", 45 | "schema": { 46 | "not": { 47 | "type": "object", 48 | "properties": { 49 | "foo": { 50 | "type": "string" 51 | } 52 | } 53 | } 54 | }, 55 | "tests": [ 56 | { 57 | "description": "match", 58 | "data": 1, 59 | "valid": true 60 | }, 61 | { 62 | "description": "other match", 63 | "data": {"foo": 1}, 64 | "valid": true 65 | }, 66 | { 67 | "description": "mismatch", 68 | "data": {"foo": "bar"}, 69 | "valid": false 70 | } 71 | ] 72 | }, 73 | { 74 | "description": "forbidden property", 75 | "schema": { 76 | "properties": { 77 | "foo": { 78 | "not": {} 79 | } 80 | } 81 | }, 82 | "tests": [ 83 | { 84 | "description": "property present", 85 | "data": {"foo": 1, "bar": 2}, 86 | "valid": false 87 | }, 88 | { 89 | "description": "property absent", 90 | "data": {"bar": 1, "baz": 2}, 91 | "valid": true 92 | } 93 | ] 94 | }, 95 | { 96 | "description": "not with boolean schema true", 97 | "schema": {"not": true}, 98 | "tests": [ 99 | { 100 | "description": "any value is invalid", 101 | "data": "foo", 102 | "valid": false 103 | } 104 | ] 105 | }, 106 | { 107 | "description": "not with boolean schema false", 108 | "schema": {"not": false}, 109 | "tests": [ 110 | { 111 | "description": "any value is valid", 112 | "data": "foo", 113 | "valid": true 114 | } 115 | ] 116 | } 117 | ] 118 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/propertyNames.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "propertyNames validation", 4 | "schema": { 5 | "propertyNames": {"maxLength": 3} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "all property names valid", 10 | "data": { 11 | "f": {}, 12 | "foo": {} 13 | }, 14 | "valid": true 15 | }, 16 | { 17 | "description": "some property names invalid", 18 | "data": { 19 | "foo": {}, 20 | "foobar": {} 21 | }, 22 | "valid": false 23 | }, 24 | { 25 | "description": "object without properties is valid", 26 | "data": {}, 27 | "valid": true 28 | }, 29 | { 30 | "description": "ignores arrays", 31 | "data": [1, 2, 3, 4], 32 | "valid": true 33 | }, 34 | { 35 | "description": "ignores strings", 36 | "data": "foobar", 37 | "valid": true 38 | }, 39 | { 40 | "description": "ignores other non-objects", 41 | "data": 12, 42 | "valid": true 43 | } 44 | ] 45 | }, 46 | { 47 | "description": "propertyNames validation with pattern", 48 | "schema": { 49 | "propertyNames": { "pattern": "^a+$" } 50 | }, 51 | "tests": [ 52 | { 53 | "description": "matching property names valid", 54 | "data": { 55 | "a": {}, 56 | "aa": {}, 57 | "aaa": {} 58 | }, 59 | "valid": true 60 | }, 61 | { 62 | "description": "non-matching property name is invalid", 63 | "data": { 64 | "aaA": {} 65 | }, 66 | "valid": false 67 | }, 68 | { 69 | "description": "object without properties is valid", 70 | "data": {}, 71 | "valid": true 72 | } 73 | ] 74 | }, 75 | { 76 | "description": "propertyNames with boolean schema true", 77 | "schema": {"propertyNames": true}, 78 | "tests": [ 79 | { 80 | "description": "object with any properties is valid", 81 | "data": {"foo": 1}, 82 | "valid": true 83 | }, 84 | { 85 | "description": "empty object is valid", 86 | "data": {}, 87 | "valid": true 88 | } 89 | ] 90 | }, 91 | { 92 | "description": "propertyNames with boolean schema false", 93 | "schema": {"propertyNames": false}, 94 | "tests": [ 95 | { 96 | "description": "object with any properties is invalid", 97 | "data": {"foo": 1}, 98 | "valid": false 99 | }, 100 | { 101 | "description": "empty object is valid", 102 | "data": {}, 103 | "valid": true 104 | } 105 | ] 106 | } 107 | ] 108 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | run-name: Tests 3 | 4 | on: 5 | push: 6 | branches: [ main ] 7 | pull_request: 8 | branches: [ main ] 9 | # Make it able to be used in other workflows 10 | workflow_call: 11 | 12 | defaults: 13 | run: 14 | shell: bash 15 | 16 | jobs: 17 | pre-commit: 18 | name: Check pre-commit 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - uses: actions/setup-python@v4 23 | - uses: pre-commit/action@v3.0.0 24 | 25 | test: 26 | name: Run ctests 27 | needs: [ pre-commit ] 28 | continue-on-error: ${{ matrix.experimental }} 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | toolchain: [ gcc, llvm, intel ] 33 | json_version: [ develop, v3.12.0, v3.8.0 ] 34 | experimental: [ false ] 35 | include: 36 | - toolchain: llvm 37 | compiler_version: 15 38 | - toolchain: gcc 39 | compiler_version: latest 40 | env: 41 | NLOHMANN_JSON_VERSION: ${{ matrix.json_version }} 42 | runs-on: ubuntu-latest 43 | container: ghcr.io/nlohmann/json-ci:v2.4.0 44 | steps: 45 | - name: Activate Intel compilers 46 | # Not elegant, it will propagate all environment variable. 47 | # Intel does not provide a way to output the environment variables to a file 48 | # Note: PATH needs to be exported to GITHUB_PATH otherwise it can be overwritten 49 | run: | 50 | source /opt/intel/oneapi/setvars.sh 51 | printenv >> $GITHUB_ENV 52 | echo $PATH >> $GITHUB_PATH 53 | if: matrix.toolchain == 'intel' 54 | - name: Setup gcc toolchain 55 | run: | 56 | update-alternatives --install /usr/bin/g++ g++ $(which g++-${{ matrix.compiler_version }}) 999 57 | if: matrix.compiler_version && matrix.toolchain == 'gcc' 58 | - name: Setup llvm toolchain 59 | run: | 60 | update-alternatives --install /usr/bin/clang++ clang++ $(which clang++-${{ matrix.compiler_version }}) 999 61 | if: matrix.compiler_version && matrix.toolchain == 'llvm' 62 | - uses: actions/checkout@v3 63 | # container version is < 3.25 which does not have workflows 64 | - name: Get a working cmake version 65 | uses: lukka/get-cmake@v3.25.2 66 | - name: Run CMake ${{ matrix.toolchain }}-ci workflow with nlohmann/json version ${{ matrix.json_version }} 67 | uses: lukka/run-cmake@v10.5 68 | with: 69 | workflowPreset: "${{ matrix.toolchain }}-ci" 70 | coverage: 71 | name: Run coverage tests 72 | needs: [ test ] 73 | runs-on: ubuntu-latest 74 | container: ghcr.io/nlohmann/json-ci:v2.4.0 75 | if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} 76 | steps: 77 | - uses: actions/checkout@v3 78 | - name: Get latest cmake version 79 | uses: lukka/get-cmake@latest 80 | - name: Get test coverage 81 | uses: lukka/run-cmake@v10.5 82 | with: 83 | workflowPreset: ci-coverage 84 | - name: Get lcov data 85 | uses: danielealbano/lcov-action@v3 86 | with: 87 | # Note lcov-action prepends and appends wild-cards *. Account for those 88 | # https://github.com/danielealbano/lcov-action/issues/11 89 | remove_patterns: /test/,/cmake-build*/ 90 | - name: Upload coverage to Codecov 91 | uses: codecov/codecov-action@v3 92 | with: 93 | files: coverage.info 94 | verbose: true 95 | -------------------------------------------------------------------------------- /src/json-patch.cpp: -------------------------------------------------------------------------------- 1 | #include "json-patch.hpp" 2 | 3 | #include 4 | 5 | namespace 6 | { 7 | 8 | // originally from http://jsonpatch.com/, http://json.schemastore.org/json-patch 9 | // with fixes 10 | const nlohmann::json patch_schema = R"patch({ 11 | "title": "JSON schema for JSONPatch files", 12 | "$schema": "http://json-schema.org/draft-04/schema#", 13 | "type": "array", 14 | 15 | "items": { 16 | "oneOf": [ 17 | { 18 | "additionalProperties": false, 19 | "required": [ "value", "op", "path"], 20 | "properties": { 21 | "path" : { "$ref": "#/definitions/path" }, 22 | "op": { 23 | "description": "The operation to perform.", 24 | "type": "string", 25 | "enum": [ "add", "replace", "test" ] 26 | }, 27 | "value": { 28 | "description": "The value to add, replace or test." 29 | } 30 | } 31 | }, 32 | { 33 | "additionalProperties": false, 34 | "required": [ "op", "path"], 35 | "properties": { 36 | "path" : { "$ref": "#/definitions/path" }, 37 | "op": { 38 | "description": "The operation to perform.", 39 | "type": "string", 40 | "enum": [ "remove" ] 41 | } 42 | } 43 | }, 44 | { 45 | "additionalProperties": false, 46 | "required": [ "from", "op", "path" ], 47 | "properties": { 48 | "path" : { "$ref": "#/definitions/path" }, 49 | "op": { 50 | "description": "The operation to perform.", 51 | "type": "string", 52 | "enum": [ "move", "copy" ] 53 | }, 54 | "from": { 55 | "$ref": "#/definitions/path", 56 | "description": "A JSON Pointer path pointing to the location to move/copy from." 57 | } 58 | } 59 | } 60 | ] 61 | }, 62 | "definitions": { 63 | "path": { 64 | "description": "A JSON Pointer path.", 65 | "type": "string" 66 | } 67 | } 68 | })patch"_json; 69 | } // namespace 70 | 71 | namespace nlohmann 72 | { 73 | 74 | json_patch::json_patch(json &&patch) 75 | : j_(std::move(patch)) 76 | { 77 | validateJsonPatch(j_); 78 | } 79 | 80 | json_patch::json_patch(const json &patch) 81 | : j_(std::move(patch)) 82 | { 83 | validateJsonPatch(j_); 84 | } 85 | 86 | json_patch &json_patch::add(const json::json_pointer &ptr, json value) 87 | { 88 | j_.push_back(json{{"op", "add"}, {"path", ptr.to_string()}, {"value", std::move(value)}}); 89 | return *this; 90 | } 91 | 92 | json_patch &json_patch::replace(const json::json_pointer &ptr, json value) 93 | { 94 | j_.push_back(json{{"op", "replace"}, {"path", ptr.to_string()}, {"value", std::move(value)}}); 95 | return *this; 96 | } 97 | 98 | json_patch &json_patch::remove(const json::json_pointer &ptr) 99 | { 100 | j_.push_back(json{{"op", "remove"}, {"path", ptr.to_string()}}); 101 | return *this; 102 | } 103 | 104 | void json_patch::validateJsonPatch(json const &patch) 105 | { 106 | // static put here to have it created at the first usage of validateJsonPatch 107 | static nlohmann::json_schema::json_validator patch_validator(patch_schema); 108 | 109 | patch_validator.validate(patch); 110 | 111 | for (auto const &op : patch) 112 | json::json_pointer(op["path"].get()); 113 | } 114 | 115 | } // namespace nlohmann 116 | -------------------------------------------------------------------------------- /test/id-ref.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | using nlohmann::json; 8 | 9 | auto schema_draft = R"( 10 | { 11 | "$id": "http://example.com/root.json", 12 | "definitions": { 13 | "A": { "$id": "#foo" }, 14 | "B": { 15 | "$id": "other.json", 16 | "definitions": { 17 | "X": { "$id": "#bar" }, 18 | "Y": { "$id": "t/inner.json" } 19 | } 20 | }, 21 | "C": { 22 | 23 | "$id": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", 24 | "definitions": { 25 | "Z": { "$id": "#bar" }, 26 | "9": { "$id": "http://example.com/drole.json" } 27 | } 28 | } 29 | } 30 | } 31 | )"_json; 32 | 33 | /* # (document root) 34 | 35 | http://example.com/root.json 36 | http://example.com/root.json# 37 | 38 | #/definitions/A 39 | 40 | http://example.com/root.json#foo 41 | http://example.com/root.json#/definitions/A 42 | 43 | #/definitions/B 44 | 45 | http://example.com/other.json 46 | http://example.com/other.json# 47 | http://example.com/root.json#/definitions/B 48 | 49 | #/definitions/B/definitions/X 50 | 51 | http://example.com/other.json#bar 52 | http://example.com/other.json#/definitions/X 53 | http://example.com/root.json#/definitions/B/definitions/X 54 | 55 | #/definitions/B/definitions/Y 56 | 57 | http://example.com/t/inner.json 58 | http://example.com/t/inner.json# 59 | http://example.com/other.json#/definitions/Y 60 | http://example.com/root.json#/definitions/B/definitions/Y 61 | 62 | #/definitions/C 63 | 64 | urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f 65 | urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f# 66 | http://example.com/root.json#/definitions/C 67 | */ 68 | 69 | auto schema = R"( 70 | { 71 | "id": "http://localhost:1234/scope_change_defs2.json", 72 | "type" : "object", 73 | "properties": { 74 | "list": {"$ref": "#/definitions/baz/definitions/bar"} 75 | }, 76 | "definitions": { 77 | "baz": { 78 | "id": "folder/", 79 | "definitions": { 80 | "bar": { 81 | "type": "array", 82 | "items": {"$ref": "folderInteger.json"} 83 | } 84 | } 85 | } 86 | } 87 | })"_json; 88 | 89 | class json_schema_validator 90 | { 91 | public: 92 | std::vector schemas_; 93 | std::map schema_store_; 94 | 95 | public: 96 | void insert_schema(json &s, std::vector base_uris) 97 | { 98 | auto id = s.find("$id"); 99 | if (id != s.end()) 100 | base_uris.push_back(base_uris.back().derive(id.value())); 101 | 102 | for (auto &u : base_uris) 103 | schema_store_[u] = &s; 104 | 105 | for (auto i = s.begin(); 106 | i != s.end(); 107 | ++i) { 108 | 109 | switch (i.value().type()) { 110 | case json::value_t::object: { // child is object, thus a schema 111 | std::vector subschema_uri = base_uris; 112 | 113 | for (auto &ss : subschema_uri) 114 | ss = ss.append(nlohmann::json_uri::escape(i.key())); 115 | 116 | insert_schema(i.value(), subschema_uri); 117 | } break; 118 | 119 | case json::value_t::string: 120 | // this schema is a reference 121 | if (i.key() == "$ref") { 122 | auto id = base_uris.back().derive(i.value()); 123 | i.value() = id.to_string(); 124 | } 125 | 126 | break; 127 | 128 | default: 129 | break; 130 | } 131 | } 132 | } 133 | }; 134 | 135 | int main(void) 136 | { 137 | json_schema_validator store; 138 | 139 | store.insert_schema(schema_draft, {{"#"}}); 140 | 141 | for (auto &i : store.schema_store_) { 142 | std::cerr << i.first << " " << *i.second << "\n"; 143 | } 144 | 145 | return 0; 146 | } 147 | -------------------------------------------------------------------------------- /test/string-format-check-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | /** @return number of failed tests */ 6 | size_t 7 | testStringFormat(const std::string &format, 8 | const std::vector> &stringValidTests) 9 | { 10 | size_t numberOfErrors = 0; 11 | 12 | for (auto stringValid = stringValidTests.begin(); stringValid != stringValidTests.end(); ++stringValid) { 13 | std::cout << "[INFO] Testing " << format << ": " << stringValid->first << "\n"; 14 | 15 | try { 16 | nlohmann::json_schema::default_string_format_check(format, stringValid->first); 17 | 18 | if (!stringValid->second) { 19 | ++numberOfErrors; 20 | std::cerr << "[ERROR] String with " << format << " format '" << stringValid->first 21 | << "' validated even though it should NOT!\n"; 22 | } 23 | } catch (std::exception &exception) { 24 | std::cout << "[INFO] Validation failed with: " << exception.what() << "\n"; 25 | if (stringValid->second) { 26 | ++numberOfErrors; 27 | std::cerr << "[ERROR] String with " << format << " format '" << stringValid->first 28 | << "' did NOT validate even though it should!\n"; 29 | } 30 | } 31 | } 32 | 33 | return numberOfErrors; 34 | } 35 | 36 | int main() 37 | { 38 | size_t numberOfErrors = 0; 39 | 40 | const std::vector> dateTimeChecks{ 41 | {"1985-04-12T23:20:50.52Z", true}, 42 | {"1996-12-19T16:39:57-08:00", true}, 43 | {"1990-12-31T23:59:60Z", true}, 44 | {"1990-12-31T15:59:60-08:00", true}, 45 | {"1937-01-01T12:00:27.87+00:20", true}, 46 | {"1985-4-12T23:20:50.52Z", false}, 47 | {"1985-04-12T23:20:50.52", false}, 48 | {"1985-04-12T24:00:00", false}, 49 | {"", false}, 50 | {"2019-04-30T11:11:11+01:00", true}, 51 | {"2019-04-31T11:11:11+01:00", false}, 52 | {"2019-02-28T11:11:11+01:00", true}, 53 | {"2019-02-29T11:11:11+01:00", false}, 54 | {"2020-02-29T11:11:11+01:00", true}, 55 | {"2020-02-30T11:11:11+01:00", false}, 56 | {"2020-02-29T23:59:59+01:00", true}, 57 | {"2020-02-29T23:59:60+01:00", false}, 58 | {"2020-02-29T23:59:60+00:00", true}, 59 | {"2020-02-29T23:60:59+01:00", false}, 60 | {"2019-09-30T11:11:11+01:00", true}, 61 | {"2019-09-31T11:11:11+01:00", false}, 62 | {"2019-09-30T11:11:11+23:59", true}, 63 | {"2019-09-30T11:11:11+24:00", false}}; 64 | 65 | numberOfErrors += testStringFormat("date-time", dateTimeChecks); 66 | 67 | const std::vector> ipv4Checks{ 68 | {"", false}, 69 | {"x99.99.99.99", false}, 70 | {"99.99.99.99x", false}, 71 | {"192.168.0.1", true}, 72 | {"127.0.0", false}, 73 | {"127.0.0.1", true}, 74 | {"127.0.0.0.1", false}, 75 | {"255.255.255.255", true}, 76 | {"255.255.255.256", false}, 77 | {"255.255.256.255", false}, 78 | {"255.256.255.255", false}, 79 | {"256.255.255.255", false}, 80 | {"256.256.256.256", false}, 81 | {"0x7f000001", false}}; 82 | 83 | numberOfErrors += testStringFormat("ipv4", ipv4Checks); 84 | 85 | const std::vector> uriChecks{ 86 | {"http://www.google.com/search?q=regular%20expression", true}, 87 | {"http://www.google.com/", true}, 88 | {"http://www.google.com/search?q=regular%20expression", true}, 89 | {"www.google.com", false}, 90 | {"http://www.google.comj", true}, 91 | {"ldap://[2001:db8::7]/c=GB?objectClass?one", true}, 92 | {"mailto:John.Doe@example.com", true}, 93 | {"news:comp.infosystems.www.servers.unix", true}, 94 | {"https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top", true}, 95 | {"tel:+1-816-555-1212", true}, 96 | {"telnet://192.0.2.16:80/", true}, 97 | {"urn:oasis:names:specification:docbook:dtd:xml:4.1.2", true}}; 98 | 99 | numberOfErrors += testStringFormat("uri", uriChecks); 100 | 101 | return numberOfErrors; 102 | } 103 | -------------------------------------------------------------------------------- /test/issue-70-root-schema-constructor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static int error_count; 6 | 7 | #define EXPECT_EQ(a, b) \ 8 | do { \ 9 | if (a != b) { \ 10 | std::cerr << "Failed: '" << a << "' != '" << b << "'\n"; \ 11 | error_count++; \ 12 | } \ 13 | } while (0) 14 | 15 | using nlohmann::json; 16 | using nlohmann::json_schema::json_validator; 17 | 18 | namespace 19 | { 20 | 21 | // The schema is defined based upon a string literal 22 | static json person_schema = R"( 23 | { 24 | "$schema": "http://json-schema.org/draft-07/schema#", 25 | "title": "A person", 26 | "properties": { 27 | "name": { 28 | "description": "Name", 29 | "type": "string" 30 | }, 31 | "age": { 32 | "description": "Age of the person", 33 | "type": "number", 34 | "minimum": 2, 35 | "maximum": 200 36 | }, 37 | "phones": { 38 | "type": "array", 39 | "items": { 40 | "type": "number" 41 | } 42 | } 43 | }, 44 | "required": [ 45 | "name", 46 | "age" 47 | ], 48 | "additionalProperties": false, 49 | "type": "object" 50 | })"_json; 51 | 52 | class store_ptr_err_handler : public nlohmann::json_schema::basic_error_handler 53 | { 54 | void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override 55 | { 56 | nlohmann::json_schema::basic_error_handler::error(ptr, instance, message); 57 | std::cerr << "ERROR: '" << ptr << "' - '" << instance << "': " << message << "\n"; 58 | failed_pointers.push_back(ptr); 59 | } 60 | 61 | public: 62 | std::vector failed_pointers; 63 | 64 | void reset() override 65 | { 66 | nlohmann::json_schema::basic_error_handler::reset(); 67 | failed_pointers.clear(); 68 | } 69 | }; 70 | 71 | } // namespace 72 | 73 | static json_validator validator(person_schema); 74 | 75 | int main(void) 76 | { 77 | store_ptr_err_handler err; 78 | 79 | validator.validate({{"age", 42}, {"name", "John"}}, err); // OK 80 | EXPECT_EQ(err.failed_pointers.size(), 0); 81 | err.reset(); 82 | 83 | validator.validate({{"age", 42}}, err); // no name 84 | 85 | EXPECT_EQ(err.failed_pointers.size(), 1); 86 | EXPECT_EQ(err.failed_pointers[0].to_string(), ""); 87 | err.reset(); 88 | 89 | validator.validate({{"street", "Boulevard"}}, err); // no name and no age 90 | EXPECT_EQ(err.failed_pointers.size(), 3); 91 | EXPECT_EQ(err.failed_pointers[0].to_string(), ""); 92 | EXPECT_EQ(err.failed_pointers[1].to_string(), ""); 93 | EXPECT_EQ(err.failed_pointers[2].to_string(), ""); 94 | err.reset(); 95 | 96 | validator.validate({{"age", 42}, {"name", 12}}, err); // name must be a string 97 | EXPECT_EQ(err.failed_pointers.size(), 1); 98 | EXPECT_EQ(err.failed_pointers[0].to_string(), "/name"); 99 | err.reset(); 100 | 101 | validator.validate({ 102 | {"age", 42}, 103 | {"name", "John"}, 104 | {"phones", {1234, "223"}}, 105 | }, 106 | err); // name must be a string 107 | EXPECT_EQ(err.failed_pointers.size(), 1); 108 | EXPECT_EQ(err.failed_pointers[0].to_string(), "/phones/1"); 109 | err.reset(); 110 | 111 | validator.validate({ 112 | {"age", 42}, 113 | {"name", "John"}, 114 | {"phones", {0}}, 115 | {"post-code", 12345}, 116 | }, 117 | err); // name must be a string 118 | EXPECT_EQ(err.failed_pointers.size(), 1); 119 | EXPECT_EQ(err.failed_pointers[0].to_string(), ""); 120 | err.reset(); 121 | 122 | return error_count; 123 | } 124 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "validation of URIs", 4 | "schema": {"format": "uri"}, 5 | "tests": [ 6 | { 7 | "description": "a valid URL with anchor tag", 8 | "data": "http://foo.bar/?baz=qux#quux", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a valid URL with anchor tag and parentheses", 13 | "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", 14 | "valid": true 15 | }, 16 | { 17 | "description": "a valid URL with URL-encoded stuff", 18 | "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", 19 | "valid": true 20 | }, 21 | { 22 | "description": "a valid puny-coded URL ", 23 | "data": "http://xn--nw2a.xn--j6w193g/", 24 | "valid": true 25 | }, 26 | { 27 | "description": "a valid URL with many special characters", 28 | "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", 29 | "valid": true 30 | }, 31 | { 32 | "description": "a valid URL based on IPv4", 33 | "data": "http://223.255.255.254", 34 | "valid": true 35 | }, 36 | { 37 | "description": "a valid URL with ftp scheme", 38 | "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", 39 | "valid": true 40 | }, 41 | { 42 | "description": "a valid URL for a simple text file", 43 | "data": "http://www.ietf.org/rfc/rfc2396.txt", 44 | "valid": true 45 | }, 46 | { 47 | "description": "a valid URL ", 48 | "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", 49 | "valid": true 50 | }, 51 | { 52 | "description": "a valid mailto URI", 53 | "data": "mailto:John.Doe@example.com", 54 | "valid": true 55 | }, 56 | { 57 | "description": "a valid newsgroup URI", 58 | "data": "news:comp.infosystems.www.servers.unix", 59 | "valid": true 60 | }, 61 | { 62 | "description": "a valid tel URI", 63 | "data": "tel:+1-816-555-1212", 64 | "valid": true 65 | }, 66 | { 67 | "description": "a valid URN", 68 | "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", 69 | "valid": true 70 | }, 71 | { 72 | "description": "an invalid protocol-relative URI Reference", 73 | "data": "//foo.bar/?baz=qux#quux", 74 | "valid": false 75 | }, 76 | { 77 | "description": "an invalid relative URI Reference", 78 | "data": "/abc", 79 | "valid": false 80 | }, 81 | { 82 | "description": "an invalid URI", 83 | "data": "\\\\WINDOWS\\fileshare", 84 | "valid": false 85 | }, 86 | { 87 | "description": "an invalid URI though valid URI reference", 88 | "data": "abc", 89 | "valid": false 90 | }, 91 | { 92 | "description": "an invalid URI with spaces", 93 | "data": "http:// shouldfail.com", 94 | "valid": false 95 | }, 96 | { 97 | "description": "an invalid URI with spaces and missing scheme", 98 | "data": ":// should fail", 99 | "valid": false 100 | }, 101 | { 102 | "description": "an invalid URI with comma in scheme", 103 | "data": "bar,baz:foo", 104 | "valid": false 105 | } 106 | ] 107 | } 108 | ] 109 | -------------------------------------------------------------------------------- /test/errors.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static int error_count; 6 | 7 | #define EXPECT_EQ(a, b) \ 8 | do { \ 9 | if (a != b) { \ 10 | std::cerr << "Failed: '" << a << "' != '" << b << "'\n"; \ 11 | error_count++; \ 12 | } \ 13 | } while (0) 14 | 15 | using nlohmann::json; 16 | using nlohmann::json_schema::json_validator; 17 | 18 | namespace 19 | { 20 | 21 | // The schema is defined based upon a string literal 22 | static json person_schema = R"( 23 | { 24 | "$schema": "http://json-schema.org/draft-07/schema#", 25 | "title": "A person", 26 | "properties": { 27 | "name": { 28 | "description": "Name", 29 | "type": "string" 30 | }, 31 | "age": { 32 | "description": "Age of the person", 33 | "type": "number", 34 | "minimum": 2, 35 | "maximum": 200 36 | }, 37 | "phones": { 38 | "type": "array", 39 | "items": { 40 | "type": "number" 41 | } 42 | } 43 | }, 44 | "required": [ 45 | "name", 46 | "age" 47 | ], 48 | "additionalProperties": false, 49 | "type": "object" 50 | })"_json; 51 | 52 | class store_ptr_err_handler : public nlohmann::json_schema::basic_error_handler 53 | { 54 | void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override 55 | { 56 | nlohmann::json_schema::basic_error_handler::error(ptr, instance, message); 57 | std::cerr << "ERROR: '" << ptr << "' - '" << instance << "': " << message << "\n"; 58 | failed_pointers.push_back(ptr); 59 | } 60 | 61 | public: 62 | std::vector failed_pointers; 63 | 64 | void reset() override 65 | { 66 | nlohmann::json_schema::basic_error_handler::reset(); 67 | failed_pointers.clear(); 68 | } 69 | }; 70 | 71 | } // namespace 72 | 73 | int main(void) 74 | { 75 | json_validator validator; 76 | 77 | try { 78 | validator.set_root_schema(person_schema); // insert root-schema 79 | } catch (const std::exception &e) { 80 | std::cerr << "Validation of schema failed, here is why: " << e.what() << "\n"; 81 | return EXIT_FAILURE; 82 | } 83 | 84 | store_ptr_err_handler err; 85 | 86 | validator.validate({{"age", 42}, {"name", "John"}}, err); // OK 87 | EXPECT_EQ(err.failed_pointers.size(), 0); 88 | err.reset(); 89 | 90 | validator.validate({{"age", 42}}, err); // no name 91 | 92 | EXPECT_EQ(err.failed_pointers.size(), 1); 93 | EXPECT_EQ(err.failed_pointers[0].to_string(), ""); 94 | err.reset(); 95 | 96 | validator.validate({{"street", "Boulevard"}}, err); // no name and no age 97 | EXPECT_EQ(err.failed_pointers.size(), 3); 98 | EXPECT_EQ(err.failed_pointers[0].to_string(), ""); 99 | EXPECT_EQ(err.failed_pointers[1].to_string(), ""); 100 | EXPECT_EQ(err.failed_pointers[2].to_string(), ""); 101 | err.reset(); 102 | 103 | validator.validate({{"age", 42}, {"name", 12}}, err); // name must be a string 104 | EXPECT_EQ(err.failed_pointers.size(), 1); 105 | EXPECT_EQ(err.failed_pointers[0].to_string(), "/name"); 106 | err.reset(); 107 | 108 | validator.validate({ 109 | {"age", 42}, 110 | {"name", "John"}, 111 | {"phones", {1234, "223"}}, 112 | }, 113 | err); // name must be a string 114 | EXPECT_EQ(err.failed_pointers.size(), 1); 115 | EXPECT_EQ(err.failed_pointers[0].to_string(), "/phones/1"); 116 | err.reset(); 117 | 118 | validator.validate({ 119 | {"age", 42}, 120 | {"name", "John"}, 121 | {"phones", {0}}, 122 | {"post-code", 12345}, 123 | }, 124 | err); // name must be a string 125 | EXPECT_EQ(err.failed_pointers.size(), 1); 126 | EXPECT_EQ(err.failed_pointers[0].to_string(), ""); 127 | err.reset(); 128 | 129 | return error_count; 130 | } 131 | -------------------------------------------------------------------------------- /src/json-uri.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * JSON schema validator for JSON for modern C++ 3 | * 4 | * Copyright (c) 2016-2019 Patrick Boettcher . 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | */ 9 | #include 10 | 11 | #include 12 | 13 | namespace nlohmann 14 | { 15 | 16 | void json_uri::update(const std::string &uri) 17 | { 18 | std::string pointer = ""; // default pointer is document-root 19 | 20 | // first split the URI into location and pointer 21 | auto pointer_separator = uri.find('#'); 22 | if (pointer_separator != std::string::npos) { // and extract the pointer-string if found 23 | pointer = uri.substr(pointer_separator + 1); // remove # 24 | 25 | // unescape %-values IOW, decode JSON-URI-formatted JSON-pointer 26 | std::size_t pos = pointer.size() - 1; 27 | do { 28 | pos = pointer.rfind('%', pos); 29 | if (pos == std::string::npos) 30 | break; 31 | 32 | if (pos >= pointer.size() - 2) { 33 | pos--; 34 | continue; 35 | } 36 | 37 | std::string hex = pointer.substr(pos + 1, 2); 38 | char ascii = static_cast(std::strtoul(hex.c_str(), nullptr, 16)); 39 | pointer.replace(pos, 3, 1, ascii); 40 | 41 | pos--; 42 | } while (1); 43 | } 44 | 45 | auto location = uri.substr(0, pointer_separator); 46 | 47 | if (location.size()) { // a location part has been found 48 | 49 | // if it is an URN take it as it is 50 | if (location.find("urn:") == 0) { 51 | urn_ = location; 52 | 53 | // and clear URL members 54 | scheme_ = ""; 55 | authority_ = ""; 56 | path_ = ""; 57 | 58 | } else { // it is an URL 59 | 60 | // split URL in protocol, hostname and path 61 | std::size_t pos = 0; 62 | auto proto = location.find("://", pos); 63 | if (proto != std::string::npos) { // extract the protocol 64 | 65 | urn_ = ""; // clear URN-member if URL is parsed 66 | 67 | scheme_ = location.substr(pos, proto - pos); 68 | pos = 3 + proto; // 3 == "://" 69 | 70 | auto authority = location.find("/", pos); 71 | if (authority != std::string::npos) { // and the hostname (no proto without hostname) 72 | authority_ = location.substr(pos, authority - pos); 73 | pos = authority; 74 | } 75 | } 76 | 77 | auto path = location.substr(pos); 78 | 79 | // URNs cannot of have paths 80 | if (urn_.size() && path.size()) 81 | throw std::invalid_argument("Cannot add a path (" + path + ") to an URN URI (" + urn_ + ")"); 82 | 83 | if (path[0] == '/') // if it starts with a / it is root-path 84 | path_ = path; 85 | else if (pos == 0) { // the URL contained only a path and the current path has no / at the end, strip last element until / and append 86 | auto last_slash = path_.rfind('/'); 87 | path_ = path_.substr(0, last_slash) + '/' + path; 88 | } else // otherwise it is a subfolder 89 | path_.append(path); 90 | } 91 | } 92 | 93 | pointer_ = ""_json_pointer; 94 | identifier_ = ""; 95 | 96 | if (pointer[0] == '/') 97 | pointer_ = json::json_pointer(pointer); 98 | else 99 | identifier_ = pointer; 100 | } 101 | 102 | std::string json_uri::location() const 103 | { 104 | if (urn_.size()) 105 | return urn_; 106 | 107 | std::stringstream s; 108 | 109 | if (scheme_.size() > 0) 110 | s << scheme_ << "://"; 111 | 112 | s << authority_ 113 | << path_; 114 | 115 | return s.str(); 116 | } 117 | 118 | std::string json_uri::to_string() const 119 | { 120 | std::stringstream s; 121 | 122 | s << location() << " # "; 123 | 124 | if (identifier_ == "") 125 | s << pointer_.to_string(); 126 | else 127 | s << identifier_; 128 | 129 | return s.str(); 130 | } 131 | 132 | std::ostream &operator<<(std::ostream &os, const json_uri &u) 133 | { 134 | return os << u.to_string(); 135 | } 136 | 137 | std::string json_uri::escape(const std::string &src) 138 | { 139 | std::vector> chars = { 140 | {"~", "~0"}, 141 | {"/", "~1"}}; 142 | 143 | std::string l = src; 144 | 145 | for (const auto &c : chars) { 146 | std::size_t pos = 0; 147 | do { 148 | pos = l.find(c.first, pos); 149 | if (pos == std::string::npos) 150 | break; 151 | l.replace(pos, 1, c.second); 152 | pos += c.second.size(); 153 | } while (1); 154 | } 155 | 156 | return l; 157 | } 158 | 159 | } // namespace nlohmann 160 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/additionalProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": 4 | "additionalProperties being false does not allow other properties", 5 | "schema": { 6 | "properties": {"foo": {}, "bar": {}}, 7 | "patternProperties": { "^v": {} }, 8 | "additionalProperties": false 9 | }, 10 | "tests": [ 11 | { 12 | "description": "no additional properties is valid", 13 | "data": {"foo": 1}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "an additional property is invalid", 18 | "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores arrays", 23 | "data": [1, 2, 3], 24 | "valid": true 25 | }, 26 | { 27 | "description": "ignores strings", 28 | "data": "foobarbaz", 29 | "valid": true 30 | }, 31 | { 32 | "description": "ignores other non-objects", 33 | "data": 12, 34 | "valid": true 35 | }, 36 | { 37 | "description": "patternProperties are not additional properties", 38 | "data": {"foo":1, "vroom": 2}, 39 | "valid": true 40 | } 41 | ] 42 | }, 43 | { 44 | "description": "non-ASCII pattern with additionalProperties", 45 | "schema": { 46 | "patternProperties": {"^á": {}}, 47 | "additionalProperties": false 48 | }, 49 | "tests": [ 50 | { 51 | "description": "matching the pattern is valid", 52 | "data": {"ármányos": 2}, 53 | "valid": true 54 | }, 55 | { 56 | "description": "not matching the pattern is invalid", 57 | "data": {"élmény": 2}, 58 | "valid": false 59 | } 60 | ] 61 | }, 62 | { 63 | "description": 64 | "additionalProperties allows a schema which should validate", 65 | "schema": { 66 | "properties": {"foo": {}, "bar": {}}, 67 | "additionalProperties": {"type": "boolean"} 68 | }, 69 | "tests": [ 70 | { 71 | "description": "no additional properties is valid", 72 | "data": {"foo": 1}, 73 | "valid": true 74 | }, 75 | { 76 | "description": "an additional valid property is valid", 77 | "data": {"foo" : 1, "bar" : 2, "quux" : true}, 78 | "valid": true 79 | }, 80 | { 81 | "description": "an additional invalid property is invalid", 82 | "data": {"foo" : 1, "bar" : 2, "quux" : 12}, 83 | "valid": false 84 | } 85 | ] 86 | }, 87 | { 88 | "description": 89 | "additionalProperties can exist by itself", 90 | "schema": { 91 | "additionalProperties": {"type": "boolean"} 92 | }, 93 | "tests": [ 94 | { 95 | "description": "an additional valid property is valid", 96 | "data": {"foo" : true}, 97 | "valid": true 98 | }, 99 | { 100 | "description": "an additional invalid property is invalid", 101 | "data": {"foo" : 1}, 102 | "valid": false 103 | } 104 | ] 105 | }, 106 | { 107 | "description": "additionalProperties are allowed by default", 108 | "schema": {"properties": {"foo": {}, "bar": {}}}, 109 | "tests": [ 110 | { 111 | "description": "additional properties are allowed", 112 | "data": {"foo": 1, "bar": 2, "quux": true}, 113 | "valid": true 114 | } 115 | ] 116 | }, 117 | { 118 | "description": "additionalProperties should not look in applicators", 119 | "schema": { 120 | "allOf": [ 121 | {"properties": {"foo": {}}} 122 | ], 123 | "additionalProperties": {"type": "boolean"} 124 | }, 125 | "tests": [ 126 | { 127 | "description": "properties defined in allOf are not examined", 128 | "data": {"foo": 1, "bar": true}, 129 | "valid": false 130 | } 131 | ] 132 | } 133 | ] 134 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(PIPE_IN_TEST_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/test-pipe-in.sh) 2 | 3 | # simple nlohmann_json_schema_validator-executable 4 | add_executable(json-schema-validate json-schema-validate.cpp) 5 | target_link_libraries(json-schema-validate nlohmann_json_schema_validator) 6 | 7 | function(add_test_simple_schema name schema instance) 8 | add_test( 9 | NAME ${name} 10 | COMMAND ${PIPE_IN_TEST_SCRIPT} 11 | $ 12 | ${schema} 13 | ${instance} 14 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 15 | endfunction() 16 | 17 | file(GLOB TEST_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/*) 18 | 19 | foreach(DIR ${TEST_DIRS}) 20 | if(IS_DIRECTORY ${DIR}) 21 | add_subdirectory(${DIR}) 22 | endif() 23 | endforeach() 24 | 25 | add_executable(uri uri.cpp) 26 | target_link_libraries(uri nlohmann_json_schema_validator) 27 | add_test(NAME uri COMMAND uri) 28 | 29 | add_executable(errors errors.cpp) 30 | target_link_libraries(errors nlohmann_json_schema_validator) 31 | add_test(NAME errors COMMAND errors) 32 | 33 | add_executable(issue-70 issue-70.cpp) 34 | target_link_libraries(issue-70 nlohmann_json_schema_validator) 35 | add_test(NAME issue-70 COMMAND issue-70) 36 | 37 | add_executable(issue-70-root-schema-constructor issue-70-root-schema-constructor.cpp) 38 | target_link_libraries(issue-70-root-schema-constructor nlohmann_json_schema_validator) 39 | add_test(NAME issue-70-root-schema-constructor COMMAND issue-70-root-schema-constructor) 40 | 41 | add_executable(issue-25-default-values issue-25-default-values.cpp) 42 | target_link_libraries(issue-25-default-values nlohmann_json_schema_validator) 43 | add_test(NAME issue-25-default-values COMMAND issue-25-default-values) 44 | 45 | add_executable(issue-98 issue-98.cpp) 46 | target_link_libraries(issue-98 nlohmann_json_schema_validator) 47 | add_test(NAME issue-98-erase-exception-unknown-keywords COMMAND issue-98) 48 | 49 | add_executable(issue-293 issue-293.cpp) 50 | target_link_libraries(issue-293 nlohmann_json_schema_validator) 51 | add_test(NAME issue-293-float-point-error COMMAND issue-293) 52 | 53 | # Unit test for string format checks 54 | add_executable(string-format-check-test string-format-check-test.cpp) 55 | target_include_directories(string-format-check-test PRIVATE ${PROJECT_SOURCE_DIR}/src/) 56 | target_link_libraries(string-format-check-test nlohmann_json_schema_validator) 57 | 58 | add_test(NAME string-format-check-test COMMAND string-format-check-test) 59 | 60 | # Unit test for json-patch 61 | add_executable(json-patch json-patch.cpp) 62 | target_include_directories(json-patch PRIVATE ${PROJECT_SOURCE_DIR}/src) 63 | target_link_libraries(json-patch nlohmann_json_schema_validator) 64 | add_test(NAME json-patch COMMAND json-patch) 65 | 66 | # Unit test for format checker fail at schema parsing time 67 | add_executable(issue-117-format-error issue-117-format-error.cpp) 68 | target_link_libraries(issue-117-format-error nlohmann_json_schema_validator) 69 | add_test(NAME issue-117-format-error COMMAND issue-117-format-error) 70 | 71 | add_executable(binary-validation binary-validation.cpp) 72 | target_include_directories(binary-validation PRIVATE ${PROJECT_SOURCE_DIR}/src) 73 | target_link_libraries(binary-validation PRIVATE nlohmann_json_schema_validator) 74 | add_test(NAME binary-validation COMMAND binary-validation) 75 | 76 | add_executable(issue-149-entry-selection issue-149-entry-selection.cpp) 77 | target_link_libraries(issue-149-entry-selection PRIVATE nlohmann_json_schema_validator) 78 | add_test(NAME issue-149-entry-selection COMMAND issue-149-entry-selection) 79 | 80 | add_executable(issue-189-default-values issue-189-default-values.cpp) 81 | target_link_libraries(issue-189-default-values nlohmann_json_schema_validator) 82 | add_test(NAME issue-189-default-values COMMAND issue-189-default-values) 83 | 84 | add_executable(issue-229-oneof-default-values issue-229-oneof-default-values.cpp) 85 | target_link_libraries(issue-229-oneof-default-values nlohmann_json_schema_validator) 86 | add_test(NAME issue-229-oneof-default-values COMMAND issue-229-oneof-default-values) 87 | 88 | add_executable(issue-243-root-default-values issue-243-root-default-values.cpp) 89 | target_link_libraries(issue-243-root-default-values nlohmann_json_schema_validator) 90 | add_test(NAME issue-243-root-default-values COMMAND issue-243-root-default-values) 91 | 92 | add_executable(issue-255-error-message-limit-precision issue-255-error-message-limit-precision.cpp) 93 | target_link_libraries(issue-255-error-message-limit-precision nlohmann_json_schema_validator) 94 | add_test(NAME issue-255-error-message-limit-precision COMMAND issue-255-error-message-limit-precision) 95 | 96 | add_executable(issue-105-verbose-combination-errors issue-105-verbose-combination-errors.cpp) 97 | target_link_libraries(issue-105-verbose-combination-errors nlohmann_json_schema_validator) 98 | add_test(NAME issue-105-verbose-combination-errors COMMAND issue-105-verbose-combination-errors) 99 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/json-schema-test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * JSON schema validator for JSON for modern C++ 3 | * 4 | * Copyright (c) 2016-2019 Patrick Boettcher . 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | */ 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | using nlohmann::json; 16 | using nlohmann::json_uri; 17 | using nlohmann::json_schema::json_validator; 18 | 19 | static void loader(const json_uri &uri, json &schema) 20 | { 21 | if (uri.location() == "http://json-schema.org/draft-07/schema") { 22 | schema = nlohmann::json_schema::draft7_schema_builtin; 23 | return; 24 | } 25 | 26 | std::string fn = JSON_SCHEMA_TEST_SUITE_PATH; 27 | fn += "/remotes"; 28 | fn += uri.path(); 29 | std::cerr << fn << "\n"; 30 | 31 | std::fstream s(fn.c_str()); 32 | if (!s.good()) 33 | throw std::invalid_argument("could not open " + uri.url() + " for schema loading\n"); 34 | 35 | try { 36 | s >> schema; 37 | } catch (std::exception &e) { 38 | throw e; 39 | } 40 | } 41 | 42 | // from here 43 | // https://stackoverflow.com/a/34571089/880584 44 | static std::string base64_decode(const std::string &in) 45 | { 46 | std::string out; 47 | 48 | std::vector T(256, -1); 49 | for (int i = 0; i < 64; i++) 50 | T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; 51 | 52 | unsigned val = 0; 53 | int valb = -8; 54 | for (uint8_t c : in) { 55 | if (c == '=') 56 | break; 57 | 58 | if (T[c] == -1) { 59 | throw std::invalid_argument("base64-decode: unexpected character in encode string: '" + std::string(1, c) + "'"); 60 | } 61 | val = (val << 6) + T[c]; 62 | valb += 6; 63 | if (valb >= 0) { 64 | out.push_back(char((val >> valb) & 0xFF)); 65 | valb -= 8; 66 | } 67 | } 68 | return out; 69 | } 70 | 71 | static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance) 72 | { 73 | std::string content = instance; 74 | 75 | if (contentEncoding == "base64") 76 | content = base64_decode(instance); 77 | else if (contentEncoding != "") 78 | throw std::invalid_argument("unable to check for contentEncoding '" + contentEncoding + "'"); 79 | 80 | if (contentMediaType == "application/json") 81 | auto dummy = json::parse(content); // throws if conversion fails 82 | else if (contentMediaType != "") 83 | throw std::invalid_argument("unable to check for contentMediaType '" + contentMediaType + "'"); 84 | } 85 | 86 | int main(void) 87 | { 88 | json validation; // a validation case following the JSON-test-suite-schema 89 | 90 | try { 91 | std::cin >> validation; 92 | } catch (std::exception &e) { 93 | std::cout << e.what() << "\n"; 94 | return EXIT_FAILURE; 95 | } 96 | 97 | size_t total_failed = 0, 98 | total = 0; 99 | 100 | for (auto &test_group : validation) { 101 | size_t group_failed = 0, 102 | group_total = 0; 103 | 104 | std::cout << "Testing Group " << test_group["description"] << "\n"; 105 | 106 | const auto &schema = test_group["schema"]; 107 | 108 | json_validator validator(loader, 109 | nlohmann::json_schema::default_string_format_check, 110 | content); 111 | 112 | validator.set_root_schema(schema); 113 | 114 | for (auto &test_case : test_group["tests"]) { 115 | std::cout << " Testing Case " << test_case["description"] << "\n"; 116 | 117 | bool valid = true; 118 | 119 | try { 120 | validator.validate(test_case["data"]); 121 | } catch (const std::out_of_range &e) { 122 | valid = false; 123 | std::cout << " Test Case Exception (out of range): " << e.what() << "\n"; 124 | 125 | } catch (const std::invalid_argument &e) { 126 | valid = false; 127 | std::cout << " Test Case Exception (invalid argument): " << e.what() << "\n"; 128 | 129 | } catch (const std::logic_error &e) { 130 | valid = !test_case["valid"]; /* force test-case failure */ 131 | std::cout << " Not yet implemented: " << e.what() << "\n"; 132 | } 133 | 134 | if (valid == test_case["valid"]) 135 | std::cout << " --> Test Case exited with " << valid << " as expected.\n"; 136 | else { 137 | group_failed++; 138 | std::cout << " --> Test Case exited with " << valid << " NOT expected.\n"; 139 | } 140 | group_total++; 141 | std::cout << "\n"; 142 | } 143 | total_failed += group_failed; 144 | total += group_total; 145 | std::cout << "Group RESULT: " << test_group["description"] << " " 146 | << (group_total - group_failed) << " of " << group_total 147 | << " have succeeded - " << group_failed << " failed\n"; 148 | std::cout << "-------------\n"; 149 | } 150 | 151 | std::cout << "Total RESULT: " << (total - total_failed) << " of " << total << " have succeeded - " << total_failed << " failed\n"; 152 | 153 | return total_failed; 154 | } 155 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/contains.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "contains keyword validation", 4 | "schema": { 5 | "contains": {"minimum": 5} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "array with item matching schema (5) is valid", 10 | "data": [3, 4, 5], 11 | "valid": true 12 | }, 13 | { 14 | "description": "array with item matching schema (6) is valid", 15 | "data": [3, 4, 6], 16 | "valid": true 17 | }, 18 | { 19 | "description": "array with two items matching schema (5, 6) is valid", 20 | "data": [3, 4, 5, 6], 21 | "valid": true 22 | }, 23 | { 24 | "description": "array without items matching schema is invalid", 25 | "data": [2, 3, 4], 26 | "valid": false 27 | }, 28 | { 29 | "description": "empty array is invalid", 30 | "data": [], 31 | "valid": false 32 | }, 33 | { 34 | "description": "not array is valid", 35 | "data": {}, 36 | "valid": true 37 | } 38 | ] 39 | }, 40 | { 41 | "description": "contains keyword with const keyword", 42 | "schema": { 43 | "contains": { "const": 5 } 44 | }, 45 | "tests": [ 46 | { 47 | "description": "array with item 5 is valid", 48 | "data": [3, 4, 5], 49 | "valid": true 50 | }, 51 | { 52 | "description": "array with two items 5 is valid", 53 | "data": [3, 4, 5, 5], 54 | "valid": true 55 | }, 56 | { 57 | "description": "array without item 5 is invalid", 58 | "data": [1, 2, 3, 4], 59 | "valid": false 60 | } 61 | ] 62 | }, 63 | { 64 | "description": "contains keyword with boolean schema true", 65 | "schema": {"contains": true}, 66 | "tests": [ 67 | { 68 | "description": "any non-empty array is valid", 69 | "data": ["foo"], 70 | "valid": true 71 | }, 72 | { 73 | "description": "empty array is invalid", 74 | "data": [], 75 | "valid": false 76 | } 77 | ] 78 | }, 79 | { 80 | "description": "contains keyword with boolean schema false", 81 | "schema": {"contains": false}, 82 | "tests": [ 83 | { 84 | "description": "any non-empty array is invalid", 85 | "data": ["foo"], 86 | "valid": false 87 | }, 88 | { 89 | "description": "empty array is invalid", 90 | "data": [], 91 | "valid": false 92 | }, 93 | { 94 | "description": "non-arrays are valid", 95 | "data": "contains does not apply to strings", 96 | "valid": true 97 | } 98 | ] 99 | }, 100 | { 101 | "description": "items + contains", 102 | "schema": { 103 | "items": { "multipleOf": 2 }, 104 | "contains": { "multipleOf": 3 } 105 | }, 106 | "tests": [ 107 | { 108 | "description": "matches items, does not match contains", 109 | "data": [ 2, 4, 8 ], 110 | "valid": false 111 | }, 112 | { 113 | "description": "does not match items, matches contains", 114 | "data": [ 3, 6, 9 ], 115 | "valid": false 116 | }, 117 | { 118 | "description": "matches both items and contains", 119 | "data": [ 6, 12 ], 120 | "valid": true 121 | }, 122 | { 123 | "description": "matches neither items nor contains", 124 | "data": [ 1, 5 ], 125 | "valid": false 126 | } 127 | ] 128 | }, 129 | { 130 | "description": "contains with false if subschema", 131 | "schema": { 132 | "contains": { 133 | "if": false, 134 | "else": true 135 | } 136 | }, 137 | "tests": [ 138 | { 139 | "description": "any non-empty array is valid", 140 | "data": ["foo"], 141 | "valid": true 142 | }, 143 | { 144 | "description": "empty array is invalid", 145 | "data": [], 146 | "valid": false 147 | } 148 | ] 149 | } 150 | ] 151 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/additionalItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "additionalItems as schema", 4 | "schema": { 5 | "items": [{}], 6 | "additionalItems": {"type": "integer"} 7 | }, 8 | "tests": [ 9 | { 10 | "description": "additional items match schema", 11 | "data": [ null, 2, 3, 4 ], 12 | "valid": true 13 | }, 14 | { 15 | "description": "additional items do not match schema", 16 | "data": [ null, 2, 3, "foo" ], 17 | "valid": false 18 | } 19 | ] 20 | }, 21 | { 22 | "description": "when items is schema, additionalItems does nothing", 23 | "schema": { 24 | "items": {}, 25 | "additionalItems": false 26 | }, 27 | "tests": [ 28 | { 29 | "description": "all items match schema", 30 | "data": [ 1, 2, 3, 4, 5 ], 31 | "valid": true 32 | } 33 | ] 34 | }, 35 | { 36 | "description": "array of items with no additionalItems permitted", 37 | "schema": { 38 | "items": [{}, {}, {}], 39 | "additionalItems": false 40 | }, 41 | "tests": [ 42 | { 43 | "description": "empty array", 44 | "data": [ ], 45 | "valid": true 46 | }, 47 | { 48 | "description": "fewer number of items present (1)", 49 | "data": [ 1 ], 50 | "valid": true 51 | }, 52 | { 53 | "description": "fewer number of items present (2)", 54 | "data": [ 1, 2 ], 55 | "valid": true 56 | }, 57 | { 58 | "description": "equal number of items present", 59 | "data": [ 1, 2, 3 ], 60 | "valid": true 61 | }, 62 | { 63 | "description": "additional items are not permitted", 64 | "data": [ 1, 2, 3, 4 ], 65 | "valid": false 66 | } 67 | ] 68 | }, 69 | { 70 | "description": "additionalItems as false without items", 71 | "schema": {"additionalItems": false}, 72 | "tests": [ 73 | { 74 | "description": 75 | "items defaults to empty schema so everything is valid", 76 | "data": [ 1, 2, 3, 4, 5 ], 77 | "valid": true 78 | }, 79 | { 80 | "description": "ignores non-arrays", 81 | "data": {"foo" : "bar"}, 82 | "valid": true 83 | } 84 | ] 85 | }, 86 | { 87 | "description": "additionalItems are allowed by default", 88 | "schema": {"items": [{"type": "integer"}]}, 89 | "tests": [ 90 | { 91 | "description": "only the first item is validated", 92 | "data": [1, "foo", false], 93 | "valid": true 94 | } 95 | ] 96 | }, 97 | { 98 | "description": "additionalItems should not look in applicators, valid case", 99 | "schema": { 100 | "allOf": [ 101 | { "items": [ { "type": "integer" } ] } 102 | ], 103 | "additionalItems": { "type": "boolean" } 104 | }, 105 | "tests": [ 106 | { 107 | "description": "items defined in allOf are not examined", 108 | "data": [ 1, null ], 109 | "valid": true 110 | } 111 | ] 112 | }, 113 | { 114 | "description": "additionalItems should not look in applicators, invalid case", 115 | "schema": { 116 | "allOf": [ 117 | { "items": [ { "type": "integer" }, { "type": "string" } ] } 118 | ], 119 | "items": [ {"type": "integer" } ], 120 | "additionalItems": { "type": "boolean" } 121 | }, 122 | "tests": [ 123 | { 124 | "description": "items defined in allOf are not examined", 125 | "data": [ 1, "hello" ], 126 | "valid": false 127 | } 128 | ] 129 | }, 130 | { 131 | "description": "items validation adjusts the starting index for additionalItems", 132 | "schema": { 133 | "items": [ { "type": "string" } ], 134 | "additionalItems": { "type": "integer" } 135 | }, 136 | "tests": [ 137 | { 138 | "description": "valid items", 139 | "data": [ "x", 2, 3 ], 140 | "valid": true 141 | }, 142 | { 143 | "description": "wrong type of second item", 144 | "data": [ "x", "y" ], 145 | "valid": false 146 | } 147 | ] 148 | } 149 | ] 150 | -------------------------------------------------------------------------------- /test/JSON-Schema-Test-Suite/tests/draft7/patternProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": 4 | "patternProperties validates properties matching a regex", 5 | "schema": { 6 | "patternProperties": { 7 | "f.*o": {"type": "integer"} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "a single valid match is valid", 13 | "data": {"foo": 1}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "multiple valid matches is valid", 18 | "data": {"foo": 1, "foooooo" : 2}, 19 | "valid": true 20 | }, 21 | { 22 | "description": "a single invalid match is invalid", 23 | "data": {"foo": "bar", "fooooo": 2}, 24 | "valid": false 25 | }, 26 | { 27 | "description": "multiple invalid matches is invalid", 28 | "data": {"foo": "bar", "foooooo" : "baz"}, 29 | "valid": false 30 | }, 31 | { 32 | "description": "ignores arrays", 33 | "data": ["foo"], 34 | "valid": true 35 | }, 36 | { 37 | "description": "ignores strings", 38 | "data": "foo", 39 | "valid": true 40 | }, 41 | { 42 | "description": "ignores other non-objects", 43 | "data": 12, 44 | "valid": true 45 | } 46 | ] 47 | }, 48 | { 49 | "description": "multiple simultaneous patternProperties are validated", 50 | "schema": { 51 | "patternProperties": { 52 | "a*": {"type": "integer"}, 53 | "aaa*": {"maximum": 20} 54 | } 55 | }, 56 | "tests": [ 57 | { 58 | "description": "a single valid match is valid", 59 | "data": {"a": 21}, 60 | "valid": true 61 | }, 62 | { 63 | "description": "a simultaneous match is valid", 64 | "data": {"aaaa": 18}, 65 | "valid": true 66 | }, 67 | { 68 | "description": "multiple matches is valid", 69 | "data": {"a": 21, "aaaa": 18}, 70 | "valid": true 71 | }, 72 | { 73 | "description": "an invalid due to one is invalid", 74 | "data": {"a": "bar"}, 75 | "valid": false 76 | }, 77 | { 78 | "description": "an invalid due to the other is invalid", 79 | "data": {"aaaa": 31}, 80 | "valid": false 81 | }, 82 | { 83 | "description": "an invalid due to both is invalid", 84 | "data": {"aaa": "foo", "aaaa": 31}, 85 | "valid": false 86 | } 87 | ] 88 | }, 89 | { 90 | "description": "regexes are not anchored by default and are case sensitive", 91 | "schema": { 92 | "patternProperties": { 93 | "[0-9]{2,}": { "type": "boolean" }, 94 | "X_": { "type": "string" } 95 | } 96 | }, 97 | "tests": [ 98 | { 99 | "description": "non recognized members are ignored", 100 | "data": { "answer 1": "42" }, 101 | "valid": true 102 | }, 103 | { 104 | "description": "recognized members are accounted for", 105 | "data": { "a31b": null }, 106 | "valid": false 107 | }, 108 | { 109 | "description": "regexes are case sensitive", 110 | "data": { "a_x_3": 3 }, 111 | "valid": true 112 | }, 113 | { 114 | "description": "regexes are case sensitive, 2", 115 | "data": { "a_X_3": 3 }, 116 | "valid": false 117 | } 118 | ] 119 | }, 120 | { 121 | "description": "patternProperties with boolean schemas", 122 | "schema": { 123 | "patternProperties": { 124 | "f.*": true, 125 | "b.*": false 126 | } 127 | }, 128 | "tests": [ 129 | { 130 | "description": "object with property matching schema true is valid", 131 | "data": {"foo": 1}, 132 | "valid": true 133 | }, 134 | { 135 | "description": "object with property matching schema false is invalid", 136 | "data": {"bar": 2}, 137 | "valid": false 138 | }, 139 | { 140 | "description": "object with both properties is invalid", 141 | "data": {"foo": 1, "bar": 2}, 142 | "valid": false 143 | }, 144 | { 145 | "description": "object with a property matching both true and false is invalid", 146 | "data": {"foobar":1}, 147 | "valid": false 148 | }, 149 | { 150 | "description": "empty object is valid", 151 | "data": {}, 152 | "valid": true 153 | } 154 | ] 155 | } 156 | ] 157 | -------------------------------------------------------------------------------- /test/issue-25-default-values.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using nlohmann::json; 5 | using nlohmann::json_schema::json_validator; 6 | 7 | static const json person_schema = R"( 8 | { 9 | "$schema": "http://json-schema.org/draft-07/schema#", 10 | "title": "A person", 11 | "properties": { 12 | "name": { 13 | "description": "Name", 14 | "type": "string" 15 | }, 16 | "age": { 17 | "description": "Age of the person", 18 | "type": "number", 19 | "minimum": 2, 20 | "maximum": 200 21 | }, 22 | "address": { 23 | "type": "object", 24 | "default": {}, 25 | "properties": { 26 | "street": { 27 | "type": "string", 28 | "default": "Abbey Road" 29 | } 30 | } 31 | }, 32 | "work address": { 33 | "type": "object", 34 | "default": null, 35 | "properties": { 36 | "street": { 37 | "type": "string", 38 | "default": "Abbey Road" 39 | } 40 | } 41 | }, 42 | "other address": { 43 | "type": "object", 44 | "properties": { 45 | "street": { 46 | "type": "string", 47 | "default": "Abbey Road" 48 | } 49 | } 50 | } 51 | }, 52 | "required": [ 53 | "name", 54 | "age" 55 | ], 56 | "additionalProperties": false, 57 | "type": "object" 58 | })"_json; 59 | 60 | int main(void) 61 | { 62 | json_validator validator{}; 63 | validator.set_root_schema(person_schema); 64 | 65 | { 66 | // add address which is optional that should generate a diff containing a default street 67 | json person_emtpy_address = R"({ 68 | "name": "Hans", 69 | "age": 69, 70 | "address": {} 71 | })"_json; 72 | 73 | const auto default_patch = validator.validate(person_emtpy_address); 74 | 75 | if (!default_patch.is_array()) { 76 | std::cerr << "Patch with defaults is expected to be an array" << std::endl; 77 | return 1; 78 | } 79 | 80 | if (default_patch.size() != 1) { 81 | std::cerr << "Patch with defaults is expected to contain one opperation" << std::endl; 82 | return 1; 83 | } 84 | 85 | const auto &single_op = default_patch[0]; 86 | 87 | if (!single_op.contains("op")) { 88 | std::cerr << "Patch with defaults is expected to contain opperation entry" << std::endl; 89 | return 1; 90 | } 91 | 92 | if (single_op["op"].get() != "add") { 93 | std::cerr << "Patch with defaults is expected to contain add opperation" << std::endl; 94 | return 1; 95 | } 96 | 97 | if (!single_op.contains("path")) { 98 | std::cerr << "Patch with defaults is expected to contain a path" << std::endl; 99 | return 1; 100 | } 101 | 102 | const auto &readPath = single_op["path"].get(); 103 | if (readPath != "/address/street") { 104 | std::cerr << "Patch with defaults contains wrong path. It is " << readPath << " and should be " 105 | << "/address/street" << std::endl; 106 | return 1; 107 | } 108 | 109 | if (!single_op.contains("value")) { 110 | std::cerr << "Patch with defaults is expected to contain a value" << std::endl; 111 | return 1; 112 | } 113 | 114 | if (single_op["value"].get() != "Abbey Road") { 115 | std::cerr << "Patch with defaults contains wrong value" << std::endl; 116 | return 1; 117 | } 118 | } 119 | { 120 | // add address which is optional that should generate a diff containing a empty object 121 | // but not work address which is null or other address which has no default 122 | json person_missing_address = R"({ 123 | "name": "Hans", 124 | "age": 69 125 | })"_json; 126 | 127 | const auto default_patch = validator.validate(person_missing_address); 128 | 129 | if (!default_patch.is_array()) { 130 | std::cerr << "Patch with defaults is expected to be an array" << std::endl; 131 | return 1; 132 | } 133 | 134 | if (default_patch.size() != 1) { 135 | std::cerr << "Patch with defaults is expected to contain one opperation" << std::endl; 136 | return 1; 137 | } 138 | 139 | const auto &single_op = default_patch[0]; 140 | 141 | if (!single_op.contains("op")) { 142 | std::cerr << "Patch with defaults is expected to contain opperation entry" << std::endl; 143 | return 1; 144 | } 145 | 146 | if (single_op["op"].get() != "add") { 147 | std::cerr << "Patch with defaults is expected to contain add opperation" << std::endl; 148 | return 1; 149 | } 150 | 151 | if (!single_op.contains("path")) { 152 | std::cerr << "Patch with defaults is expected to contain a path" << std::endl; 153 | return 1; 154 | } 155 | 156 | const auto &readPath = single_op["path"].get(); 157 | if (readPath != "/address") { 158 | std::cerr << "Patch with defaults contains wrong path. It is " << readPath << " and should be " 159 | << "/address" << std::endl; 160 | return 1; 161 | } 162 | 163 | if (!single_op.contains("value")) { 164 | std::cerr << "Patch with defaults is expected to contain a value" << std::endl; 165 | return 1; 166 | } 167 | 168 | if (!single_op["value"].is_object() || !single_op["value"].empty()) { 169 | std::cerr << "Patch with defaults contains wrong value" << std::endl; 170 | return 1; 171 | } 172 | } 173 | return 0; 174 | } 175 | --------------------------------------------------------------------------------