mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-09-27 16:29:33 +02:00
Initial commit.
This commit contains the third major design of a C++ library for JSON Schema validation. It is definitely not what I would consider production-ready, but I do think that the overall design of the library is robust.
This commit is contained in:
commit
4c9864de73
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
bin
|
||||
xcode/valijson.xcodeproj/project.xcworkspace
|
||||
xcode/valijson.xcodeproj/project.xcworkspace/xcuserdata
|
||||
xcode/valijson.xcodeproj/xcuserdata
|
||||
doc/html
|
24
LICENSE
Normal file
24
LICENSE
Normal file
@ -0,0 +1,24 @@
|
||||
Copyright (c) 2013, Tristan Penman
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
57
Makefile
Normal file
57
Makefile
Normal file
@ -0,0 +1,57 @@
|
||||
|
||||
CPPFLAGS = \
|
||||
-ggdb \
|
||||
-O0 \
|
||||
-Iinclude \
|
||||
-Ithirdparty/gtest-1.6.0 \
|
||||
-Ithirdparty/gtest-1.6.0/src \
|
||||
-Ithirdparty/gtest-1.6.0/include \
|
||||
-Ithirdparty/jsoncpp-0.5.0/include \
|
||||
-Ithirdparty/rapidjson-0.1/include \
|
||||
-I/usr/local/include \
|
||||
-DBOOST_NO_CXX11_SMART_PTR \
|
||||
-DGTEST_USE_OWN_TR1_TUPLE \
|
||||
-L/usr/local/lib -lboost_regex-mt
|
||||
|
||||
TEST_SRCS = tests/test_adapter_comparison.cpp \
|
||||
tests/test_dereference_callback.cpp \
|
||||
tests/test_jsoncpp_adapter.cpp \
|
||||
tests/test_property_tree_adapter.cpp \
|
||||
tests/test_rapidjson_adapter.cpp \
|
||||
tests/test_uri_resolution.cpp \
|
||||
tests/test_validation_errors.cpp \
|
||||
tests/test_validator.cpp
|
||||
|
||||
JSONCPP_SRCS = \
|
||||
thirdparty/jsoncpp-0.5.0/src/lib_json/json_reader.cpp \
|
||||
thirdparty/jsoncpp-0.5.0/src/lib_json/json_value.cpp \
|
||||
thirdparty/jsoncpp-0.5.0/src/lib_json/json_writer.cpp
|
||||
|
||||
GTEST_SRCS = \
|
||||
thirdparty/gtest-1.6.0/src/gtest-death-test.cc \
|
||||
thirdparty/gtest-1.6.0/src/gtest-filepath.cc \
|
||||
thirdparty/gtest-1.6.0/src/gtest-port.cc \
|
||||
thirdparty/gtest-1.6.0/src/gtest-printers.cc \
|
||||
thirdparty/gtest-1.6.0/src/gtest-test-part.cc \
|
||||
thirdparty/gtest-1.6.0/src/gtest-typed-test.cc \
|
||||
thirdparty/gtest-1.6.0/src/gtest.cc \
|
||||
thirdparty/gtest-1.6.0/src/gtest_main.cc
|
||||
|
||||
.PHONY: check
|
||||
|
||||
check: bin bin/test_suite
|
||||
cd bin && ./test_suite
|
||||
|
||||
examples: bin bin/custom_schema bin/external_schema
|
||||
|
||||
bin:
|
||||
mkdir -p bin
|
||||
|
||||
bin/custom_schema:
|
||||
g++ $(CPPFLAGS) examples/custom_schema.cpp -o bin/custom_schema
|
||||
|
||||
bin/external_schema:
|
||||
g++ $(CPPFLAGS) examples/external_schema.cpp -o bin/external_schema
|
||||
|
||||
bin/test_suite:
|
||||
g++ $(CPPFLAGS) $(TEST_SRCS) $(JSONCPP_SRCS) $(GTEST_SRCS) -o bin/test_suite
|
80
README.md
Normal file
80
README.md
Normal file
@ -0,0 +1,80 @@
|
||||
Valijson
|
||||
========
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Valijson is a header-only JSON Schema Validation library for C++.
|
||||
|
||||
Valijson provides a simple validation API that allows you load schemas, and
|
||||
validate documents created by one of several supported parser libraries.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Valijson is licensed under the Simplified BSD License. See the LICENSE file
|
||||
for more information.
|
||||
|
||||
Supported parsers
|
||||
-----------------
|
||||
|
||||
Valijson supports JsonCpp, RapidJson and Boost Property Trees. It has been
|
||||
tested against the following versions of these libraries:
|
||||
- jsoncpp 0.5.0
|
||||
- rapidjson 0.1
|
||||
- boost::property_tree 1.54
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
Required:
|
||||
- boost 1.54 (earlier versions may work, but have not been tested)
|
||||
|
||||
Optional
|
||||
- gtest 1.6.0 (for building unit tests)
|
||||
- jsoncpp 0.5.0 (for use of the JsonCppAdapter class)
|
||||
- rapidjson 0.1 (for use of the RapidJsonAdapter class)
|
||||
|
||||
JSON Schema Support
|
||||
-------------------
|
||||
|
||||
Valijson supports most of the constraints defined in Drafts 3 and 4 of the JSON
|
||||
Schema specification.
|
||||
|
||||
The exceptions for Draft 3 are:
|
||||
- disallow
|
||||
- divisibleBy
|
||||
- extends
|
||||
- format (optional)
|
||||
- readonly
|
||||
- ref
|
||||
- refRemote
|
||||
|
||||
The exceptions for Draft 4 are:
|
||||
- definitions
|
||||
- format (optional)
|
||||
- multipleOf
|
||||
- ref
|
||||
- refRemote
|
||||
|
||||
Support for JSON References is in development.
|
||||
|
||||
Build instructions
|
||||
------------------
|
||||
|
||||
A rudimentary Makefile has been included. Running 'make examples' will build
|
||||
the example programs in the 'examples' directory. Running 'make check' will
|
||||
build and run the gtest-based unit tests. Executables will be placed in 'bin'.
|
||||
|
||||
An Xcode 5 project has also been provided, in the 'xcode' directory. Note that
|
||||
in order to run the test suite, you may need to configure the working directory
|
||||
for the 'test_suite' scheme.
|
||||
|
||||
The Xcode project has been configured so that /usr/local/include is in the
|
||||
include path, and /usr/local/lib is in the library path. These are the
|
||||
locations that homebrew installed Boost on my test system.
|
||||
|
||||
Doxygen documentation can be built by running 'doxygen' from the project root
|
||||
directory. Generated documentation will be placed in 'doc/html'. Other relevant
|
||||
documentation such as schemas and specifications have been included in the 'doc'
|
||||
directory.
|
174
doc/schema/draft-03.json
Normal file
174
doc/schema/draft-03.json
Normal file
@ -0,0 +1,174 @@
|
||||
{
|
||||
"$schema" : "http://json-schema.org/draft-03/schema#",
|
||||
"id" : "http://json-schema.org/draft-03/schema#",
|
||||
"type" : "object",
|
||||
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : ["string", "array"],
|
||||
"items" : {
|
||||
"type" : ["string", {"$ref" : "#"}]
|
||||
},
|
||||
"uniqueItems" : true,
|
||||
"default" : "any"
|
||||
},
|
||||
|
||||
"properties" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : {"$ref" : "#"},
|
||||
"default" : {}
|
||||
},
|
||||
|
||||
"patternProperties" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : {"$ref" : "#"},
|
||||
"default" : {}
|
||||
},
|
||||
|
||||
"additionalProperties" : {
|
||||
"type" : [{"$ref" : "#"}, "boolean"],
|
||||
"default" : {}
|
||||
},
|
||||
|
||||
"items" : {
|
||||
"type" : [{"$ref" : "#"}, "array"],
|
||||
"items" : {"$ref" : "#"},
|
||||
"default" : {}
|
||||
},
|
||||
|
||||
"additionalItems" : {
|
||||
"type" : [{"$ref" : "#"}, "boolean"],
|
||||
"default" : {}
|
||||
},
|
||||
|
||||
"required" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
|
||||
"dependencies" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : {
|
||||
"type" : ["string", "array", {"$ref" : "#"}],
|
||||
"items" : {
|
||||
"type" : "string"
|
||||
}
|
||||
},
|
||||
"default" : {}
|
||||
},
|
||||
|
||||
"minimum" : {
|
||||
"type" : "number"
|
||||
},
|
||||
|
||||
"maximum" : {
|
||||
"type" : "number"
|
||||
},
|
||||
|
||||
"exclusiveMinimum" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
|
||||
"exclusiveMaximum" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
|
||||
"minItems" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 0,
|
||||
"default" : 0
|
||||
},
|
||||
|
||||
"maxItems" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 0
|
||||
},
|
||||
|
||||
"uniqueItems" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
|
||||
"pattern" : {
|
||||
"type" : "string",
|
||||
"format" : "regex"
|
||||
},
|
||||
|
||||
"minLength" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 0,
|
||||
"default" : 0
|
||||
},
|
||||
|
||||
"maxLength" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
|
||||
"enum" : {
|
||||
"type" : "array",
|
||||
"minItems" : 1,
|
||||
"uniqueItems" : true
|
||||
},
|
||||
|
||||
"default" : {
|
||||
"type" : "any"
|
||||
},
|
||||
|
||||
"title" : {
|
||||
"type" : "string"
|
||||
},
|
||||
|
||||
"description" : {
|
||||
"type" : "string"
|
||||
},
|
||||
|
||||
"format" : {
|
||||
"type" : "string"
|
||||
},
|
||||
|
||||
"divisibleBy" : {
|
||||
"type" : "number",
|
||||
"minimum" : 0,
|
||||
"exclusiveMinimum" : true,
|
||||
"default" : 1
|
||||
},
|
||||
|
||||
"disallow" : {
|
||||
"type" : ["string", "array"],
|
||||
"items" : {
|
||||
"type" : ["string", {"$ref" : "#"}]
|
||||
},
|
||||
"uniqueItems" : true
|
||||
},
|
||||
|
||||
"extends" : {
|
||||
"type" : [{"$ref" : "#"}, "array"],
|
||||
"items" : {"$ref" : "#"},
|
||||
"default" : {}
|
||||
},
|
||||
|
||||
"id" : {
|
||||
"type" : "string",
|
||||
"format" : "uri"
|
||||
},
|
||||
|
||||
"$ref" : {
|
||||
"type" : "string",
|
||||
"format" : "uri"
|
||||
},
|
||||
|
||||
"$schema" : {
|
||||
"type" : "string",
|
||||
"format" : "uri"
|
||||
}
|
||||
},
|
||||
|
||||
"dependencies" : {
|
||||
"exclusiveMinimum" : "minimum",
|
||||
"exclusiveMaximum" : "maximum"
|
||||
},
|
||||
|
||||
"default" : {}
|
||||
}
|
150
doc/schema/draft-04.json
Normal file
150
doc/schema/draft-04.json
Normal file
@ -0,0 +1,150 @@
|
||||
{
|
||||
"id": "http://json-schema.org/draft-04/schema#",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"description": "Core schema meta-schema",
|
||||
"definitions": {
|
||||
"schemaArray": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#" }
|
||||
},
|
||||
"positiveInteger": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"positiveIntegerDefault0": {
|
||||
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
|
||||
},
|
||||
"simpleTypes": {
|
||||
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
|
||||
},
|
||||
"stringArray": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {},
|
||||
"multipleOf": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"exclusiveMinimum": true
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number"
|
||||
},
|
||||
"exclusiveMaximum": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"minimum": {
|
||||
"type": "number"
|
||||
},
|
||||
"exclusiveMinimum": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"maxLength": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"pattern": {
|
||||
"type": "string",
|
||||
"format": "regex"
|
||||
},
|
||||
"additionalItems": {
|
||||
"anyOf": [
|
||||
{ "type": "boolean" },
|
||||
{ "$ref": "#" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#" },
|
||||
{ "$ref": "#/definitions/schemaArray" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"maxItems": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"uniqueItems": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"maxProperties": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"required": { "$ref": "#/definitions/stringArray" },
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{ "type": "boolean" },
|
||||
{ "$ref": "#" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"definitions": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"patternProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#" },
|
||||
{ "$ref": "#/definitions/stringArray" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"enum": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"type": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#/definitions/simpleTypes" },
|
||||
{
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/simpleTypes" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"allOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"anyOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"oneOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"not": { "$ref": "#" }
|
||||
},
|
||||
"dependencies": {
|
||||
"exclusiveMaximum": [ "maximum" ],
|
||||
"exclusiveMinimum": [ "minimum" ]
|
||||
},
|
||||
"default": {}
|
||||
}
|
1400
doc/specifications/draft-fge-json-schema-validation-00.txt
Normal file
1400
doc/specifications/draft-fge-json-schema-validation-00.txt
Normal file
File diff suppressed because it is too large
Load Diff
1512
doc/specifications/draft-luff-json-hyper-schema-00.txt
Normal file
1512
doc/specifications/draft-luff-json-hyper-schema-00.txt
Normal file
File diff suppressed because it is too large
Load Diff
280
doc/specifications/draft-pbryan-zyp-json-ref-03.txt
Normal file
280
doc/specifications/draft-pbryan-zyp-json-ref-03.txt
Normal file
@ -0,0 +1,280 @@
|
||||
|
||||
|
||||
|
||||
Internet Engineering Task Force P. Bryan, Ed.
|
||||
Internet-Draft Salesforce.com
|
||||
Intended status: Informational K. Zyp
|
||||
Expires: March 20, 2013 SitePen (USA)
|
||||
September 16, 2012
|
||||
|
||||
|
||||
JSON Reference
|
||||
draft-pbryan-zyp-json-ref-03
|
||||
|
||||
Abstract
|
||||
|
||||
JSON Reference allows a JSON value to reference another value in a
|
||||
JSON document.
|
||||
|
||||
Status of this Memo
|
||||
|
||||
This Internet-Draft is submitted in full conformance with the
|
||||
provisions of BCP 78 and BCP 79.
|
||||
|
||||
Internet-Drafts are working documents of the Internet Engineering
|
||||
Task Force (IETF). Note that other groups may also distribute
|
||||
working documents as Internet-Drafts. The list of current Internet-
|
||||
Drafts is at http://datatracker.ietf.org/drafts/current/.
|
||||
|
||||
Internet-Drafts are draft documents valid for a maximum of six months
|
||||
and may be updated, replaced, or obsoleted by other documents at any
|
||||
time. It is inappropriate to use Internet-Drafts as reference
|
||||
material or to cite them other than as "work in progress."
|
||||
|
||||
This Internet-Draft will expire on March 20, 2013.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2012 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan & Zyp Expires March 20, 2013 [Page 1]
|
||||
|
||||
Internet-Draft JSON Reference September 2012
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3. Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
4. Resolution . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
5. Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 4
|
||||
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 4
|
||||
7. Security Considerations . . . . . . . . . . . . . . . . . . . . 4
|
||||
8. Normative References . . . . . . . . . . . . . . . . . . . . . 4
|
||||
Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . . 4
|
||||
Appendix B. Examples . . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan & Zyp Expires March 20, 2013 [Page 2]
|
||||
|
||||
Internet-Draft JSON Reference September 2012
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
This specification defines a JSON [RFC4627] structure which allows a
|
||||
JSON value to reference another value in a JSON document. This
|
||||
provides the basis for transclusion in JSON: the use of a target
|
||||
resource as an effective substitute for the reference.
|
||||
|
||||
|
||||
2. Conventions
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
|
||||
3. Syntax
|
||||
|
||||
A JSON Reference is a JSON object, which contains a member named
|
||||
"$ref", which has a JSON string value. Example:
|
||||
|
||||
{ "$ref": "http://example.com/example.json#/foo/bar" }
|
||||
|
||||
If a JSON value does not have these characteristics, then it SHOULD
|
||||
NOT be interpreted as a JSON Reference.
|
||||
|
||||
The "$ref" string value contains a URI [RFC3986], which identifies
|
||||
the location of the JSON value being referenced. It is an error
|
||||
condition if the string value does not conform to URI syntax rules.
|
||||
Any members other than "$ref" in a JSON Reference object SHALL be
|
||||
ignored.
|
||||
|
||||
|
||||
4. Resolution
|
||||
|
||||
Resolution of a JSON Reference object SHOULD yield the referenced
|
||||
JSON value. Implementations MAY choose to replace the reference with
|
||||
the referenced value.
|
||||
|
||||
If the URI contained in the JSON Reference value is a relative URI,
|
||||
then the base URI resolution MUST be calculated according to
|
||||
[RFC3986], section 5.2. Resolution is performed relative to the
|
||||
referring document.
|
||||
|
||||
If a URI contains a fragment identifier, then the fragment should be
|
||||
resolved per the fragment resolution mechansim of the referrant
|
||||
document. If the representation of the referrant document is JSON,
|
||||
then the fragment identifier SHOULD be interpreted as a
|
||||
[JSON-Pointer].
|
||||
|
||||
|
||||
|
||||
Bryan & Zyp Expires March 20, 2013 [Page 3]
|
||||
|
||||
Internet-Draft JSON Reference September 2012
|
||||
|
||||
|
||||
5. Error Handling
|
||||
|
||||
In the event of an error condition, evaluation of the JSON Reference
|
||||
SHOULD fail to complete.
|
||||
|
||||
|
||||
6. IANA Considerations
|
||||
|
||||
This draft includes no request to IANA.
|
||||
|
||||
|
||||
7. Security Considerations
|
||||
|
||||
A JSON Reference is not guaranteed to resolve to a JSON value.
|
||||
Implementations of this specification SHOULD take appropriate
|
||||
precautions.
|
||||
|
||||
Documents containing JSON References can be structured to resolve
|
||||
cyclically. Implementations SHOULD include appropriate checks to
|
||||
prevent such structures from resulting in infinite recursion or
|
||||
iteration.
|
||||
|
||||
|
||||
8. Normative References
|
||||
|
||||
[JSON-Pointer]
|
||||
Bryan, P., Zyp, K., and M. Nottingham, "JSON Pointer",
|
||||
draft-ietf-appsawg-json-pointer-04 (work in progress),
|
||||
September 2012.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
|
||||
Resource Identifier (URI): Generic Syntax", STD 66,
|
||||
RFC 3986, January 2005.
|
||||
|
||||
[RFC4627] Crockford, D., "The application/json Media Type for
|
||||
JavaScript Object Notation (JSON)", RFC 4627, July 2006.
|
||||
|
||||
|
||||
Appendix A. Acknowledgements
|
||||
|
||||
The following individuals contributed ideas, feedback and wording to
|
||||
this specification:
|
||||
|
||||
Bob Aman, Francis Galiegue.
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan & Zyp Expires March 20, 2013 [Page 4]
|
||||
|
||||
Internet-Draft JSON Reference September 2012
|
||||
|
||||
|
||||
Appendix B. Examples
|
||||
|
||||
TBD.
|
||||
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Paul C. Bryan (editor)
|
||||
Salesforce.com
|
||||
|
||||
Phone: +1 604 783 1481
|
||||
Email: pbryan@anode.ca
|
||||
|
||||
|
||||
Kris Zyp
|
||||
SitePen (USA)
|
||||
|
||||
Phone: +1 650 968 8787
|
||||
Email: kris@sitepen.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan & Zyp Expires March 20, 2013 [Page 5]
|
||||
|
1569
doc/specifications/draft-zyp-json-schema-03.txt
Normal file
1569
doc/specifications/draft-zyp-json-schema-03.txt
Normal file
File diff suppressed because it is too large
Load Diff
784
doc/specifications/draft-zyp-json-schema-04.txt
Normal file
784
doc/specifications/draft-zyp-json-schema-04.txt
Normal file
@ -0,0 +1,784 @@
|
||||
|
||||
|
||||
|
||||
Internet Engineering Task Force F. Galiegue, Ed.
|
||||
Internet-Draft
|
||||
Intended status: Informational K. Zyp, Ed.
|
||||
Expires: August 4, 2013 SitePen (USA)
|
||||
G. Court
|
||||
January 31, 2013
|
||||
|
||||
|
||||
JSON Schema: core definitions and terminology
|
||||
draft-zyp-json-schema-04
|
||||
|
||||
Abstract
|
||||
|
||||
JSON Schema defines the media type "application/schema+json", a JSON
|
||||
based format for defining the structure of JSON data. JSON Schema
|
||||
provides a contract for what JSON data is required for a given
|
||||
application and how to interact with it. JSON Schema is intended to
|
||||
define validation, documentation, hyperlink navigation, and
|
||||
interaction control of JSON data.
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This Internet-Draft is submitted in full conformance with the
|
||||
provisions of BCP 78 and BCP 79.
|
||||
|
||||
Internet-Drafts are working documents of the Internet Engineering
|
||||
Task Force (IETF). Note that other groups may also distribute
|
||||
working documents as Internet-Drafts. The list of current Internet-
|
||||
Drafts is at http://datatracker.ietf.org/drafts/current/.
|
||||
|
||||
Internet-Drafts are draft documents valid for a maximum of six months
|
||||
and may be updated, replaced, or obsoleted by other documents at any
|
||||
time. It is inappropriate to use Internet-Drafts as reference
|
||||
material or to cite them other than as "work in progress."
|
||||
|
||||
This Internet-Draft will expire on August 4, 2013.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2013 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 1]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
2. Conventions and Terminology . . . . . . . . . . . . . . . . . 3
|
||||
3. Core terminology . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3.1. Property, item . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3.2. JSON Schema, keywords . . . . . . . . . . . . . . . . . . 3
|
||||
3.3. Empty schema . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3.4. Root schema, subschema . . . . . . . . . . . . . . . . . . 4
|
||||
3.5. JSON Schema primitive types . . . . . . . . . . . . . . . 4
|
||||
3.6. JSON value equality . . . . . . . . . . . . . . . . . . . 5
|
||||
3.7. Instance . . . . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
4. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
4.1. Validation . . . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
4.2. Hypermedia and linking . . . . . . . . . . . . . . . . . . 6
|
||||
5. General considerations . . . . . . . . . . . . . . . . . . . . 6
|
||||
5.1. Applicability to all JSON values . . . . . . . . . . . . . 6
|
||||
5.2. Programming language independence . . . . . . . . . . . . 6
|
||||
5.3. JSON Schema and HTTP . . . . . . . . . . . . . . . . . . . 6
|
||||
5.4. JSON Schema and other protocols . . . . . . . . . . . . . 6
|
||||
5.5. Mathematical integers . . . . . . . . . . . . . . . . . . 7
|
||||
5.6. Extending JSON Schema . . . . . . . . . . . . . . . . . . 7
|
||||
5.7. Security considerations . . . . . . . . . . . . . . . . . 7
|
||||
6. The "$schema" keyword . . . . . . . . . . . . . . . . . . . . 7
|
||||
6.1. Purpose . . . . . . . . . . . . . . . . . . . . . . . . . 7
|
||||
6.2. Customization . . . . . . . . . . . . . . . . . . . . . . 8
|
||||
7. URI resolution scopes and dereferencing . . . . . . . . . . . 8
|
||||
7.1. Definition . . . . . . . . . . . . . . . . . . . . . . . . 8
|
||||
7.2. URI resolution scope alteration with the "id" keyword . . 8
|
||||
7.2.1. Valid values . . . . . . . . . . . . . . . . . . . . . 8
|
||||
7.2.2. Usage . . . . . . . . . . . . . . . . . . . . . . . . 9
|
||||
7.2.3. Canonical dereferencing and inline dereferencing . . . 10
|
||||
7.2.4. Inline dereferencing and fragments . . . . . . . . . . 11
|
||||
7.3. Interoperability considerations . . . . . . . . . . . . . 11
|
||||
8. Recommended correlation mechanisms for use with the HTTP
|
||||
protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
|
||||
8.1. Correlation by means of the "Content-Type" header . . . . 11
|
||||
8.2. Correlation by means of the "Link" header . . . . . . . . 12
|
||||
9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 12
|
||||
10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 12
|
||||
10.1. Normative References . . . . . . . . . . . . . . . . . . . 12
|
||||
10.2. Informative References . . . . . . . . . . . . . . . . . . 12
|
||||
Appendix A. ChangeLog . . . . . . . . . . . . . . . . . . . . . . 13
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 2]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
JSON Schema is a JSON media type for defining the structure of JSON
|
||||
data. JSON Schema provides a contract for what JSON data is required
|
||||
for a given application and how to interact with it. JSON Schema is
|
||||
intended to define validation, documentation, hyperlink navigation,
|
||||
and interaction control of JSON data.
|
||||
|
||||
This specification defines JSON Schema core terminology and
|
||||
mechanisms; related specifications build upon this specification and
|
||||
define different applications of JSON Schema.
|
||||
|
||||
2. Conventions and Terminology
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in RFC 2119 [RFC2119].
|
||||
|
||||
The terms "JSON", "JSON text", "JSON value", "member", "element",
|
||||
"object", "array", "number", "string", "boolean", "true", "false",
|
||||
and "null" in this document are to be interpreted as defined in RFC
|
||||
4627 [RFC4627].
|
||||
|
||||
3. Core terminology
|
||||
|
||||
3.1. Property, item
|
||||
|
||||
When refering to a JSON Object, as defined by [RFC4627], the terms
|
||||
"member" and "property" may be used interchangeably.
|
||||
|
||||
When refering to a JSON Array, as defined by [RFC4627], the terms
|
||||
"element" and "item" may be used interchangeably.
|
||||
|
||||
3.2. JSON Schema, keywords
|
||||
|
||||
A JSON Schema is a JSON document, and that document MUST be an
|
||||
object. Object members (or properties) defined by JSON Schema (this
|
||||
specification, or related specifications) are called keywords, or
|
||||
schema keywords.
|
||||
|
||||
A JSON Schema MAY contain properties which are not schema keywords.
|
||||
|
||||
3.3. Empty schema
|
||||
|
||||
An empty schema is a JSON Schema with no properties, or with
|
||||
properties which are not schema keywords.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 3]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
3.4. Root schema, subschema
|
||||
|
||||
This example of a JSON Schema has no subschemas:
|
||||
|
||||
|
||||
{
|
||||
"title": "root"
|
||||
}
|
||||
|
||||
|
||||
JSON Schemas can also be nested, as in this example:
|
||||
|
||||
|
||||
{
|
||||
"title": "root",
|
||||
"otherSchema": {
|
||||
"title": "nested",
|
||||
"anotherSchema": {
|
||||
"title": "alsoNested"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
In this example, "nested" and "alsoNested" are subschemas, and "root"
|
||||
is a root schema.
|
||||
|
||||
3.5. JSON Schema primitive types
|
||||
|
||||
JSON Schema defines seven primitive types for JSON values:
|
||||
|
||||
array A JSON array.
|
||||
|
||||
boolean A JSON boolean.
|
||||
|
||||
integer A JSON number without a fraction or exponent part.
|
||||
|
||||
number Any JSON number. Number includes integer.
|
||||
|
||||
null The JSON null value.
|
||||
|
||||
object A JSON object.
|
||||
|
||||
string A JSON string.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 4]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
3.6. JSON value equality
|
||||
|
||||
Two JSON values are said to be equal if and only if:
|
||||
|
||||
both are nulls; or
|
||||
|
||||
both are booleans, and have the same value; or
|
||||
|
||||
both are strings, and have the same value; or
|
||||
|
||||
both are numbers, and have the same mathematical value; or
|
||||
|
||||
both are arrays, and:
|
||||
|
||||
have the same number of items; and
|
||||
|
||||
items at the same index are equal according to this definition;
|
||||
or
|
||||
|
||||
both are objects, and:
|
||||
|
||||
have the same set of property names; and
|
||||
|
||||
values for a same property name are equal according to this
|
||||
definition.
|
||||
|
||||
3.7. Instance
|
||||
|
||||
An instance is any JSON value. An instance may be described by one
|
||||
or more schemas.
|
||||
|
||||
An instance may also be referred to as "JSON instance", or "JSON
|
||||
data".
|
||||
|
||||
4. Overview
|
||||
|
||||
This document proposes a new media type "application/schema+json" to
|
||||
identify JSON Schema for describing JSON data. JSON Schemas are
|
||||
themselves written in JSON. This, and related specifications, define
|
||||
keywords allowing to describe this data in terms of allowable values,
|
||||
textual descriptions and interpreting relations with other resources.
|
||||
The following sections are a summary of features defined by related
|
||||
specifications.
|
||||
|
||||
4.1. Validation
|
||||
|
||||
JSON Schema allows applications to validate instances, either non
|
||||
interactively or interactively. For instance, an application may
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 5]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
collect JSON data and check that this data matches a given set of
|
||||
constraints; another application may use a JSON Schema to build an
|
||||
interactive interface in order to collect user input according to
|
||||
constraints described by JSON Schema.
|
||||
|
||||
4.2. Hypermedia and linking
|
||||
|
||||
JSON Schema provides a method for extracting link relations from
|
||||
instances to other resources, as well as describing interpretations
|
||||
of instances as multimedia data. This allows JSON data to be
|
||||
interpreted as rich hypermedia documents, placed in the context of a
|
||||
larger set of related resources.
|
||||
|
||||
5. General considerations
|
||||
|
||||
5.1. Applicability to all JSON values
|
||||
|
||||
It is acknowledged that an instance may be any valid JSON value as
|
||||
defined by [RFC4627]. As such, JSON Schema does not mandate that an
|
||||
instance be of a particular type: JSON Schema can describe any JSON
|
||||
value, including null.
|
||||
|
||||
5.2. Programming language independence
|
||||
|
||||
JSON Schema is programming language agnostic. The only limitations
|
||||
are the ones expressed by [RFC4627] and those of the host programming
|
||||
language.
|
||||
|
||||
5.3. JSON Schema and HTTP
|
||||
|
||||
This specification acknowledges the role of HTTP [RFC2616] as the
|
||||
dominant protocol in use on the Internet, and the wealth of official
|
||||
specifications related to it.
|
||||
|
||||
This specification uses a subset of these specifications to recommend
|
||||
a set of mechanisms, usable by this protocol, to associate JSON
|
||||
instances to one or more schemas.
|
||||
|
||||
5.4. JSON Schema and other protocols
|
||||
|
||||
JSON Schema does not define any semantics for the client-server
|
||||
interface for any other protocols than HTTP. These semantics are
|
||||
application dependent, or subject to agreement between the parties
|
||||
involved in the use of JSON Schema for their own needs.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 6]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
5.5. Mathematical integers
|
||||
|
||||
It is acknowledged by this specification that some programming
|
||||
languages, and their associated parsers, use different internal
|
||||
representations for floating point numbers and integers, while others
|
||||
do not.
|
||||
|
||||
As a consequence, for interoperability reasons, JSON values used in
|
||||
the context of JSON Schema, whether that JSON be a JSON Schema or an
|
||||
instance, SHOULD ensure that mathematical integers be represented as
|
||||
integers as defined by this specification.
|
||||
|
||||
5.6. Extending JSON Schema
|
||||
|
||||
Implementations MAY choose to define additional keywords to JSON
|
||||
Schema. Save for explicit agreement, schema authors SHALL NOT expect
|
||||
these additional keywords to be supported by peer implementations.
|
||||
Implementations SHOULD ignore keywords they do not support.
|
||||
|
||||
5.7. Security considerations
|
||||
|
||||
Both schemas and instances are JSON values. As such, all security
|
||||
considerations defined in RFC 4627 [RFC4627] apply.
|
||||
|
||||
6. The "$schema" keyword
|
||||
|
||||
6.1. Purpose
|
||||
|
||||
The "$schema" keyword is both used as a JSON Schema version
|
||||
identifier and the location of a resource which is itself a JSON
|
||||
Schema, which describes any schema written for this particular
|
||||
version.
|
||||
|
||||
This keyword MUST be located at the root of a JSON Schema. The value
|
||||
of this keyword MUST be a URI [RFC3986] and a valid JSON Reference
|
||||
[json-reference]; this URI MUST be both absolute and normalized. The
|
||||
resource located at this URI MUST successfully describe itself. It
|
||||
is RECOMMENDED that schema authors include this keyword in their
|
||||
schemas.
|
||||
|
||||
The following values are predefined:
|
||||
|
||||
http://json-schema.org/schema# JSON Schema written against the
|
||||
current version of the specification.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 7]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
http://json-schema.org/hyper-schema# JSON Schema written against the
|
||||
current version of the specification.
|
||||
|
||||
http://json-schema.org/draft-04/schema# JSON Schema written against
|
||||
this version.
|
||||
|
||||
http://json-schema.org/draft-04/hyper-schema# JSON Schema
|
||||
hyperschema written against this version.
|
||||
|
||||
http://json-schema.org/draft-03/schema# JSON Schema written against
|
||||
JSON Schema, draft v3 [json-schema-03].
|
||||
|
||||
http://json-schema.org/draft-03/hyper-schema# JSON Schema
|
||||
hyperschema written against JSON Schema, draft v3
|
||||
[json-schema-03].
|
||||
|
||||
6.2. Customization
|
||||
|
||||
When extending JSON Schema with custom keywords, schema authors
|
||||
SHOULD define a custom URI for "$schema". This custom URI MUST NOT
|
||||
be one of the predefined values.
|
||||
|
||||
7. URI resolution scopes and dereferencing
|
||||
|
||||
7.1. Definition
|
||||
|
||||
JSON Schema uses JSON Reference [json-reference] as a mechanism for
|
||||
schema addressing. It extends this specification in two ways:
|
||||
|
||||
JSON Schema offers facilities to alter the base URI against which
|
||||
a reference must resolve by the means of the "id" keyword;
|
||||
|
||||
it defines a specific dereferencing mechanism extending JSON
|
||||
Reference to accept arbitrary fragment parts.
|
||||
|
||||
Altering the URI within a schema is called defining a new resolution
|
||||
scope. The initial resolution scope of a schema is the URI of the
|
||||
schema itself, if any, or the empty URI if the schema was not loaded
|
||||
from a URI.
|
||||
|
||||
7.2. URI resolution scope alteration with the "id" keyword
|
||||
|
||||
7.2.1. Valid values
|
||||
|
||||
The value for this keyword MUST be a string, and MUST be a valid URI.
|
||||
This URI MUST be normalized, and SHOULD NOT be an empty fragment (#)
|
||||
or the empty URI.
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 8]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
7.2.2. Usage
|
||||
|
||||
The "id" keyword (or "id", for short) is used to alter the resolution
|
||||
scope. When an id is encountered, an implementation MUST resolve
|
||||
this id against the most immediate parent scope. The resolved URI
|
||||
will be the new resolution scope for this subschema and all its
|
||||
children, until another id is encountered.
|
||||
|
||||
When using "id" to alter resolution scopes, schema authors SHOULD
|
||||
ensure that resolution scopes are unique within the schema.
|
||||
|
||||
This schema will be taken as an example:
|
||||
|
||||
|
||||
{
|
||||
"id": "http://x.y.z/rootschema.json#",
|
||||
"schema1": {
|
||||
"id": "#foo"
|
||||
},
|
||||
"schema2": {
|
||||
"id": "otherschema.json",
|
||||
"nested": {
|
||||
"id": "#bar"
|
||||
},
|
||||
"alsonested": {
|
||||
"id": "t/inner.json#a"
|
||||
}
|
||||
},
|
||||
"schema3": {
|
||||
"id": "some://where.else/completely#"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Subschemas at the following URI-encoded JSON Pointer [json-pointer]s
|
||||
(starting from the root schema) define the following resolution
|
||||
scopes:
|
||||
|
||||
# (document root) http://x.y.z/rootschema.json#
|
||||
|
||||
#/schema1 http://x.y.z/rootschema.json#foo
|
||||
|
||||
#/schema2 http://x.y.z/otherschema.json#
|
||||
|
||||
#/schema2/nested http://x.y.z/otherschema.json#bar
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 9]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
#/schema2/alsonested http://x.y.z/t/inner.json#a
|
||||
|
||||
#/schema3 some://where.else/completely#
|
||||
|
||||
7.2.3. Canonical dereferencing and inline dereferencing
|
||||
|
||||
When resolving a URI against a resolution scope, an implementation
|
||||
may choose two modes of operation:
|
||||
|
||||
canonical dereferencing The implementation dereferences all resolved
|
||||
URIs.
|
||||
|
||||
inline dereferencing The implementation chooses to dereference URIs
|
||||
within the schema.
|
||||
|
||||
Implementations MUST support canonical dereferencing, and MAY support
|
||||
inline dereferencing.
|
||||
|
||||
For example, consider this schema:
|
||||
|
||||
|
||||
{
|
||||
"id": "http://my.site/myschema#",
|
||||
"definitions": {
|
||||
"schema1": {
|
||||
"id": "schema1",
|
||||
"type": "integer"
|
||||
},
|
||||
"schema2", {
|
||||
"type": "array",
|
||||
"items": { "$ref": "schema1" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
When an implementation encounters the "schema1" reference, it
|
||||
resolves it against the most immediate parent scope, leading to URI
|
||||
"http://my.site/schema1#". The way to process this URI will differ
|
||||
according to the chosen dereferencing mode:
|
||||
|
||||
if canonical dereferencing is used, the implementation will
|
||||
dereference this URI, and fetch the content at this URI;
|
||||
|
||||
if inline dereferencing is used, the implementation will notice
|
||||
that URI scope "http://my.site/schema1#" is already defined within
|
||||
the schema, and choose to use the appropriate subschema.
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 10]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
7.2.4. Inline dereferencing and fragments
|
||||
|
||||
When using inline dereferencing, a resolution scope may lead to a URI
|
||||
which has a non empty fragment part which is not a JSON Pointer, as
|
||||
in this example:
|
||||
|
||||
|
||||
{
|
||||
"id": "http://some.site/schema#",
|
||||
"not": { "$ref": "#inner" },
|
||||
"definitions": {
|
||||
"schema1": {
|
||||
"id": "#inner",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
An implementation choosing to support inline dereferencing SHOULD be
|
||||
able to use this kind of reference. Implementations choosing to use
|
||||
canonical dereferencing, however, are not required to support it.
|
||||
|
||||
7.3. Interoperability considerations
|
||||
|
||||
Inline dereferencing can produce canonical URIs which differ from the
|
||||
canonical URI of the root schema. Schema authors SHOULD ensure that
|
||||
implementations using canonical dereferencing obtain the same content
|
||||
as implementations using inline dereferencing.
|
||||
|
||||
Extended JSON References using fragments which are not JSON Pointers
|
||||
are not dereferenceable by implementations choosing not to support
|
||||
inline dereferencing. This kind of reference is defined for
|
||||
backwards compatibility, and SHOULD NOT be used in new schemas.
|
||||
|
||||
8. Recommended correlation mechanisms for use with the HTTP protocol
|
||||
|
||||
It is acknowledged by this specification that the majority of
|
||||
interactive JSON Schema processing will be over HTTP. This section
|
||||
therefore gives recommendations for materializing an instance/schema
|
||||
correlation using mechanisms currently available for this protocol.
|
||||
An instance is said to be described by one (or more) schema(s).
|
||||
|
||||
8.1. Correlation by means of the "Content-Type" header
|
||||
|
||||
It is RECOMMENDED that a MIME type parameter by the name of "profile"
|
||||
be appended to the "Content-Type" header of the instance being
|
||||
processed. If present, the value of this parameter MUST be a valid
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 11]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
URI, and this URI SHOULD resolve to a valid JSON Schema. The MIME
|
||||
type MUST be "application/json", or any other subtype.
|
||||
|
||||
An example of such a header would be:
|
||||
|
||||
|
||||
Content-Type: application/my-media-type+json;
|
||||
profile=http://example.com/my-hyper-schema#
|
||||
|
||||
|
||||
8.2. Correlation by means of the "Link" header
|
||||
|
||||
When using the "Link" header, the relation type used MUST be
|
||||
"describedBy", as defined by RFC 5988, section 5.3 [RFC5988]. The
|
||||
target URI of the "Link" header MUST be a valid JSON Schema.
|
||||
|
||||
An example of such a header would be:
|
||||
|
||||
|
||||
Link: <http://example.com/my-hyper-schema#>; rel="describedBy"
|
||||
|
||||
|
||||
9. IANA Considerations
|
||||
|
||||
The proposed MIME media type for JSON Schema is defined as follows:
|
||||
|
||||
type name: application;
|
||||
|
||||
subtype name: schema+json.
|
||||
|
||||
10. References
|
||||
|
||||
10.1. Normative References
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
10.2. Informative References
|
||||
|
||||
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
|
||||
Masinter, L., Leach, P., and T. Berners-Lee,
|
||||
"Hypertext Transfer Protocol -- HTTP/1.1",
|
||||
RFC 2616, June 1999.
|
||||
|
||||
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter,
|
||||
"Uniform Resource Identifier (URI): Generic
|
||||
Syntax", STD 66, RFC 3986, January 2005.
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 12]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
[RFC4627] Crockford, D., "The application/json Media Type for
|
||||
JavaScript Object Notation (JSON)", RFC 4627,
|
||||
July 2006.
|
||||
|
||||
[RFC5988] Nottingham, M., "Web Linking", RFC 5988,
|
||||
October 2010.
|
||||
|
||||
[json-reference] Bryan, P. and K. Zyp, "JSON Reference (work in
|
||||
progress)", September 2012, <http://tools.ietf.org/
|
||||
html/draft-pbryan-zyp-json-ref-03>.
|
||||
|
||||
[json-pointer] Bryan, P. and K. Zyp, "JSON Pointer (work in
|
||||
progress)", September 2012, <http://tools.ietf.org/
|
||||
html/draft-ietf-appsawg-json-pointer-07>.
|
||||
|
||||
[json-schema-03] Court, G. and K. Zyp, "JSON Schema, draft 3",
|
||||
September 2012, <http://tools.ietf.org/html/
|
||||
draft-zyp-json-schema-03>.
|
||||
|
||||
Appendix A. ChangeLog
|
||||
|
||||
draft-00
|
||||
|
||||
* Initial draft.
|
||||
|
||||
* Salvaged from draft v3.
|
||||
|
||||
* Mandate the use of JSON Reference, JSON Pointer.
|
||||
|
||||
* Define the role of "id". Define URI resolution scope.
|
||||
|
||||
* Add interoperability considerations.
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Francis Galiegue (editor)
|
||||
|
||||
EMail: fgaliegue@gmail.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 13]
|
||||
|
||||
Internet-Draft JSON Schema January 2013
|
||||
|
||||
|
||||
Kris Zyp (editor)
|
||||
SitePen (USA)
|
||||
530 Lytton Avenue
|
||||
Palo Alto, CA 94301
|
||||
USA
|
||||
|
||||
Phone: +1 650 968 8787
|
||||
EMail: kris@sitepen.com
|
||||
|
||||
|
||||
Gary Court
|
||||
Calgary, AB
|
||||
Canada
|
||||
|
||||
EMail: gary.court@gmail.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Galiegue, et al. Expires August 4, 2013 [Page 14]
|
||||
|
3419
doc/specifications/rfc3986-uri.txt
Normal file
3419
doc/specifications/rfc3986-uri.txt
Normal file
File diff suppressed because it is too large
Load Diff
563
doc/specifications/rfc4627-json.txt
Normal file
563
doc/specifications/rfc4627-json.txt
Normal file
@ -0,0 +1,563 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group D. Crockford
|
||||
Request for Comments: 4627 JSON.org
|
||||
Category: Informational July 2006
|
||||
|
||||
|
||||
The application/json Media Type for JavaScript Object Notation (JSON)
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This memo provides information for the Internet community. It does
|
||||
not specify an Internet standard of any kind. Distribution of this
|
||||
memo is unlimited.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (C) The Internet Society (2006).
|
||||
|
||||
Abstract
|
||||
|
||||
JavaScript Object Notation (JSON) is a lightweight, text-based,
|
||||
language-independent data interchange format. It was derived from
|
||||
the ECMAScript Programming Language Standard. JSON defines a small
|
||||
set of formatting rules for the portable representation of structured
|
||||
data.
|
||||
|
||||
1. Introduction
|
||||
|
||||
JavaScript Object Notation (JSON) is a text format for the
|
||||
serialization of structured data. It is derived from the object
|
||||
literals of JavaScript, as defined in the ECMAScript Programming
|
||||
Language Standard, Third Edition [ECMA].
|
||||
|
||||
JSON can represent four primitive types (strings, numbers, booleans,
|
||||
and null) and two structured types (objects and arrays).
|
||||
|
||||
A string is a sequence of zero or more Unicode characters [UNICODE].
|
||||
|
||||
An object is an unordered collection of zero or more name/value
|
||||
pairs, where a name is a string and a value is a string, number,
|
||||
boolean, null, object, or array.
|
||||
|
||||
An array is an ordered sequence of zero or more values.
|
||||
|
||||
The terms "object" and "array" come from the conventions of
|
||||
JavaScript.
|
||||
|
||||
JSON's design goals were for it to be minimal, portable, textual, and
|
||||
a subset of JavaScript.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 1]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
1.1. Conventions Used in This Document
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
The grammatical rules in this document are to be interpreted as
|
||||
described in [RFC4234].
|
||||
|
||||
2. JSON Grammar
|
||||
|
||||
A JSON text is a sequence of tokens. The set of tokens includes six
|
||||
structural characters, strings, numbers, and three literal names.
|
||||
|
||||
A JSON text is a serialized object or array.
|
||||
|
||||
JSON-text = object / array
|
||||
|
||||
These are the six structural characters:
|
||||
|
||||
begin-array = ws %x5B ws ; [ left square bracket
|
||||
|
||||
begin-object = ws %x7B ws ; { left curly bracket
|
||||
|
||||
end-array = ws %x5D ws ; ] right square bracket
|
||||
|
||||
end-object = ws %x7D ws ; } right curly bracket
|
||||
|
||||
name-separator = ws %x3A ws ; : colon
|
||||
|
||||
value-separator = ws %x2C ws ; , comma
|
||||
|
||||
Insignificant whitespace is allowed before or after any of the six
|
||||
structural characters.
|
||||
|
||||
ws = *(
|
||||
%x20 / ; Space
|
||||
%x09 / ; Horizontal tab
|
||||
%x0A / ; Line feed or New line
|
||||
%x0D ; Carriage return
|
||||
)
|
||||
|
||||
2.1. Values
|
||||
|
||||
A JSON value MUST be an object, array, number, or string, or one of
|
||||
the following three literal names:
|
||||
|
||||
false null true
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 2]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
The literal names MUST be lowercase. No other literal names are
|
||||
allowed.
|
||||
|
||||
value = false / null / true / object / array / number / string
|
||||
|
||||
false = %x66.61.6c.73.65 ; false
|
||||
|
||||
null = %x6e.75.6c.6c ; null
|
||||
|
||||
true = %x74.72.75.65 ; true
|
||||
|
||||
2.2. Objects
|
||||
|
||||
An object structure is represented as a pair of curly brackets
|
||||
surrounding zero or more name/value pairs (or members). A name is a
|
||||
string. A single colon comes after each name, separating the name
|
||||
from the value. A single comma separates a value from a following
|
||||
name. The names within an object SHOULD be unique.
|
||||
|
||||
object = begin-object [ member *( value-separator member ) ]
|
||||
end-object
|
||||
|
||||
member = string name-separator value
|
||||
|
||||
2.3. Arrays
|
||||
|
||||
An array structure is represented as square brackets surrounding zero
|
||||
or more values (or elements). Elements are separated by commas.
|
||||
|
||||
array = begin-array [ value *( value-separator value ) ] end-array
|
||||
|
||||
2.4. Numbers
|
||||
|
||||
The representation of numbers is similar to that used in most
|
||||
programming languages. A number contains an integer component that
|
||||
may be prefixed with an optional minus sign, which may be followed by
|
||||
a fraction part and/or an exponent part.
|
||||
|
||||
Octal and hex forms are not allowed. Leading zeros are not allowed.
|
||||
|
||||
A fraction part is a decimal point followed by one or more digits.
|
||||
|
||||
An exponent part begins with the letter E in upper or lowercase,
|
||||
which may be followed by a plus or minus sign. The E and optional
|
||||
sign are followed by one or more digits.
|
||||
|
||||
Numeric values that cannot be represented as sequences of digits
|
||||
(such as Infinity and NaN) are not permitted.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 3]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
number = [ minus ] int [ frac ] [ exp ]
|
||||
|
||||
decimal-point = %x2E ; .
|
||||
|
||||
digit1-9 = %x31-39 ; 1-9
|
||||
|
||||
e = %x65 / %x45 ; e E
|
||||
|
||||
exp = e [ minus / plus ] 1*DIGIT
|
||||
|
||||
frac = decimal-point 1*DIGIT
|
||||
|
||||
int = zero / ( digit1-9 *DIGIT )
|
||||
|
||||
minus = %x2D ; -
|
||||
|
||||
plus = %x2B ; +
|
||||
|
||||
zero = %x30 ; 0
|
||||
|
||||
2.5. Strings
|
||||
|
||||
The representation of strings is similar to conventions used in the C
|
||||
family of programming languages. A string begins and ends with
|
||||
quotation marks. All Unicode characters may be placed within the
|
||||
quotation marks except for the characters that must be escaped:
|
||||
quotation mark, reverse solidus, and the control characters (U+0000
|
||||
through U+001F).
|
||||
|
||||
Any character may be escaped. If the character is in the Basic
|
||||
Multilingual Plane (U+0000 through U+FFFF), then it may be
|
||||
represented as a six-character sequence: a reverse solidus, followed
|
||||
by the lowercase letter u, followed by four hexadecimal digits that
|
||||
encode the character's code point. The hexadecimal letters A though
|
||||
F can be upper or lowercase. So, for example, a string containing
|
||||
only a single reverse solidus character may be represented as
|
||||
"\u005C".
|
||||
|
||||
Alternatively, there are two-character sequence escape
|
||||
representations of some popular characters. So, for example, a
|
||||
string containing only a single reverse solidus character may be
|
||||
represented more compactly as "\\".
|
||||
|
||||
To escape an extended character that is not in the Basic Multilingual
|
||||
Plane, the character is represented as a twelve-character sequence,
|
||||
encoding the UTF-16 surrogate pair. So, for example, a string
|
||||
containing only the G clef character (U+1D11E) may be represented as
|
||||
"\uD834\uDD1E".
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 4]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
string = quotation-mark *char quotation-mark
|
||||
|
||||
char = unescaped /
|
||||
escape (
|
||||
%x22 / ; " quotation mark U+0022
|
||||
%x5C / ; \ reverse solidus U+005C
|
||||
%x2F / ; / solidus U+002F
|
||||
%x62 / ; b backspace U+0008
|
||||
%x66 / ; f form feed U+000C
|
||||
%x6E / ; n line feed U+000A
|
||||
%x72 / ; r carriage return U+000D
|
||||
%x74 / ; t tab U+0009
|
||||
%x75 4HEXDIG ) ; uXXXX U+XXXX
|
||||
|
||||
escape = %x5C ; \
|
||||
|
||||
quotation-mark = %x22 ; "
|
||||
|
||||
unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
|
||||
|
||||
3. Encoding
|
||||
|
||||
JSON text SHALL be encoded in Unicode. The default encoding is
|
||||
UTF-8.
|
||||
|
||||
Since the first two characters of a JSON text will always be ASCII
|
||||
characters [RFC0020], it is possible to determine whether an octet
|
||||
stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||
at the pattern of nulls in the first four octets.
|
||||
|
||||
00 00 00 xx UTF-32BE
|
||||
00 xx 00 xx UTF-16BE
|
||||
xx 00 00 00 UTF-32LE
|
||||
xx 00 xx 00 UTF-16LE
|
||||
xx xx xx xx UTF-8
|
||||
|
||||
4. Parsers
|
||||
|
||||
A JSON parser transforms a JSON text into another representation. A
|
||||
JSON parser MUST accept all texts that conform to the JSON grammar.
|
||||
A JSON parser MAY accept non-JSON forms or extensions.
|
||||
|
||||
An implementation may set limits on the size of texts that it
|
||||
accepts. An implementation may set limits on the maximum depth of
|
||||
nesting. An implementation may set limits on the range of numbers.
|
||||
An implementation may set limits on the length and character contents
|
||||
of strings.
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 5]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
5. Generators
|
||||
|
||||
A JSON generator produces JSON text. The resulting text MUST
|
||||
strictly conform to the JSON grammar.
|
||||
|
||||
6. IANA Considerations
|
||||
|
||||
The MIME media type for JSON text is application/json.
|
||||
|
||||
Type name: application
|
||||
|
||||
Subtype name: json
|
||||
|
||||
Required parameters: n/a
|
||||
|
||||
Optional parameters: n/a
|
||||
|
||||
Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32
|
||||
|
||||
JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON
|
||||
is written in UTF-8, JSON is 8bit compatible. When JSON is
|
||||
written in UTF-16 or UTF-32, the binary content-transfer-encoding
|
||||
must be used.
|
||||
|
||||
Security considerations:
|
||||
|
||||
Generally there are security issues with scripting languages. JSON
|
||||
is a subset of JavaScript, but it is a safe subset that excludes
|
||||
assignment and invocation.
|
||||
|
||||
A JSON text can be safely passed into JavaScript's eval() function
|
||||
(which compiles and executes a string) if all the characters not
|
||||
enclosed in strings are in the set of characters that form JSON
|
||||
tokens. This can be quickly determined in JavaScript with two
|
||||
regular expressions and calls to the test and replace methods.
|
||||
|
||||
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
|
||||
text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
|
||||
eval('(' + text + ')');
|
||||
|
||||
Interoperability considerations: n/a
|
||||
|
||||
Published specification: RFC 4627
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 6]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
Applications that use this media type:
|
||||
|
||||
JSON has been used to exchange data between applications written
|
||||
in all of these programming languages: ActionScript, C, C#,
|
||||
ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua,
|
||||
Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme.
|
||||
|
||||
Additional information:
|
||||
|
||||
Magic number(s): n/a
|
||||
File extension(s): .json
|
||||
Macintosh file type code(s): TEXT
|
||||
|
||||
Person & email address to contact for further information:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
Intended usage: COMMON
|
||||
|
||||
Restrictions on usage: none
|
||||
|
||||
Author:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
Change controller:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
7. Security Considerations
|
||||
|
||||
See Security Considerations in Section 6.
|
||||
|
||||
8. Examples
|
||||
|
||||
This is a JSON object:
|
||||
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http://www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 7]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Its Image member is an object whose Thumbnail member is an object
|
||||
and whose IDs member is an array of numbers.
|
||||
|
||||
This is a JSON array containing two objects:
|
||||
|
||||
[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
||||
|
||||
9. References
|
||||
|
||||
9.1. Normative References
|
||||
|
||||
[ECMA] European Computer Manufacturers Association, "ECMAScript
|
||||
Language Specification 3rd Edition", December 1999,
|
||||
<http://www.ecma-international.org/publications/files/
|
||||
ecma-st/ECMA-262.pdf>.
|
||||
|
||||
[RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20,
|
||||
October 1969.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
|
||||
Specifications: ABNF", RFC 4234, October 2005.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 8]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
[UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0",
|
||||
2003, <http://www.unicode.org/versions/Unicode4.1.0/>.
|
||||
|
||||
Author's Address
|
||||
|
||||
Douglas Crockford
|
||||
JSON.org
|
||||
EMail: douglas@crockford.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 9]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
Full Copyright Statement
|
||||
|
||||
Copyright (C) The Internet Society (2006).
|
||||
|
||||
This document is subject to the rights, licenses and restrictions
|
||||
contained in BCP 78, and except as set forth therein, the authors
|
||||
retain all their rights.
|
||||
|
||||
This document and the information contained herein are provided on an
|
||||
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
|
||||
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
|
||||
ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
|
||||
INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
Intellectual Property
|
||||
|
||||
The IETF takes no position regarding the validity or scope of any
|
||||
Intellectual Property Rights or other rights that might be claimed to
|
||||
pertain to the implementation or use of the technology described in
|
||||
this document or the extent to which any license under such rights
|
||||
might or might not be available; nor does it represent that it has
|
||||
made any independent effort to identify any such rights. Information
|
||||
on the procedures with respect to rights in RFC documents can be
|
||||
found in BCP 78 and BCP 79.
|
||||
|
||||
Copies of IPR disclosures made to the IETF Secretariat and any
|
||||
assurances of licenses to be made available, or the result of an
|
||||
attempt made to obtain a general license or permission for the use of
|
||||
such proprietary rights by implementers or users of this
|
||||
specification can be obtained from the IETF on-line IPR repository at
|
||||
http://www.ietf.org/ipr.
|
||||
|
||||
The IETF invites any interested party to bring to its attention any
|
||||
copyrights, patents or patent applications, or other proprietary
|
||||
rights that may cover technology that may be required to implement
|
||||
this standard. Please address the information to the IETF at
|
||||
ietf-ipr@ietf.org.
|
||||
|
||||
Acknowledgement
|
||||
|
||||
Funding for the RFC Editor function is provided by the IETF
|
||||
Administrative Support Activity (IASA).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 10]
|
||||
|
451
doc/specifications/rfc6901-json-pointer.txt
Normal file
451
doc/specifications/rfc6901-json-pointer.txt
Normal file
@ -0,0 +1,451 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Internet Engineering Task Force (IETF) P. Bryan, Ed.
|
||||
Request for Comments: 6901 Salesforce.com
|
||||
Category: Standards Track K. Zyp
|
||||
ISSN: 2070-1721 SitePen (USA)
|
||||
M. Nottingham, Ed.
|
||||
Akamai
|
||||
April 2013
|
||||
|
||||
|
||||
JavaScript Object Notation (JSON) Pointer
|
||||
|
||||
Abstract
|
||||
|
||||
JSON Pointer defines a string syntax for identifying a specific value
|
||||
within a JavaScript Object Notation (JSON) document.
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This is an Internet Standards Track document.
|
||||
|
||||
This document is a product of the Internet Engineering Task Force
|
||||
(IETF). It represents the consensus of the IETF community. It has
|
||||
received public review and has been approved for publication by the
|
||||
Internet Engineering Steering Group (IESG). Further information on
|
||||
Internet Standards is available in Section 2 of RFC 5741.
|
||||
|
||||
Information about the current status of this document, any errata,
|
||||
and how to provide feedback on it may be obtained at
|
||||
http://www.rfc-editor.org/info/rfc6901.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2013 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan, et al. Standards Track [Page 1]
|
||||
|
||||
RFC 6901 JSON Pointer April 2013
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
|
||||
2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . 2
|
||||
3. Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
|
||||
4. Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
5. JSON String Representation . . . . . . . . . . . . . . . . . . 4
|
||||
6. URI Fragment Identifier Representation . . . . . . . . . . . . 5
|
||||
7. Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 6
|
||||
8. Security Considerations . . . . . . . . . . . . . . . . . . . . 6
|
||||
9. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 7
|
||||
10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 7
|
||||
10.1. Normative References . . . . . . . . . . . . . . . . . . . 7
|
||||
10.2. Informative References . . . . . . . . . . . . . . . . . . 7
|
||||
|
||||
1. Introduction
|
||||
|
||||
This specification defines JSON Pointer, a string syntax for
|
||||
identifying a specific value within a JavaScript Object Notation
|
||||
(JSON) document [RFC4627]. JSON Pointer is intended to be easily
|
||||
expressed in JSON string values as well as Uniform Resource
|
||||
Identifier (URI) [RFC3986] fragment identifiers.
|
||||
|
||||
2. Conventions
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
This specification expresses normative syntax rules using Augmented
|
||||
Backus-Naur Form (ABNF) [RFC5234] notation.
|
||||
|
||||
3. Syntax
|
||||
|
||||
A JSON Pointer is a Unicode string (see [RFC4627], Section 3)
|
||||
containing a sequence of zero or more reference tokens, each prefixed
|
||||
by a '/' (%x2F) character.
|
||||
|
||||
Because the characters '~' (%x7E) and '/' (%x2F) have special
|
||||
meanings in JSON Pointer, '~' needs to be encoded as '~0' and '/'
|
||||
needs to be encoded as '~1' when these characters appear in a
|
||||
reference token.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan, et al. Standards Track [Page 2]
|
||||
|
||||
RFC 6901 JSON Pointer April 2013
|
||||
|
||||
|
||||
The ABNF syntax of a JSON Pointer is:
|
||||
|
||||
json-pointer = *( "/" reference-token )
|
||||
reference-token = *( unescaped / escaped )
|
||||
unescaped = %x00-2E / %x30-7D / %x7F-10FFFF
|
||||
; %x2F ('/') and %x7E ('~') are excluded from 'unescaped'
|
||||
escaped = "~" ( "0" / "1" )
|
||||
; representing '~' and '/', respectively
|
||||
|
||||
It is an error condition if a JSON Pointer value does not conform to
|
||||
this syntax (see Section 7).
|
||||
|
||||
Note that JSON Pointers are specified in characters, not as bytes.
|
||||
|
||||
4. Evaluation
|
||||
|
||||
Evaluation of a JSON Pointer begins with a reference to the root
|
||||
value of a JSON document and completes with a reference to some value
|
||||
within the document. Each reference token in the JSON Pointer is
|
||||
evaluated sequentially.
|
||||
|
||||
Evaluation of each reference token begins by decoding any escaped
|
||||
character sequence. This is performed by first transforming any
|
||||
occurrence of the sequence '~1' to '/', and then transforming any
|
||||
occurrence of the sequence '~0' to '~'. By performing the
|
||||
substitutions in this order, an implementation avoids the error of
|
||||
turning '~01' first into '~1' and then into '/', which would be
|
||||
incorrect (the string '~01' correctly becomes '~1' after
|
||||
transformation).
|
||||
|
||||
The reference token then modifies which value is referenced according
|
||||
to the following scheme:
|
||||
|
||||
o If the currently referenced value is a JSON object, the new
|
||||
referenced value is the object member with the name identified by
|
||||
the reference token. The member name is equal to the token if it
|
||||
has the same number of Unicode characters as the token and their
|
||||
code points are byte-by-byte equal. No Unicode character
|
||||
normalization is performed. If a referenced member name is not
|
||||
unique in an object, the member that is referenced is undefined,
|
||||
and evaluation fails (see below).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan, et al. Standards Track [Page 3]
|
||||
|
||||
RFC 6901 JSON Pointer April 2013
|
||||
|
||||
|
||||
o If the currently referenced value is a JSON array, the reference
|
||||
token MUST contain either:
|
||||
|
||||
* characters comprised of digits (see ABNF below; note that
|
||||
leading zeros are not allowed) that represent an unsigned
|
||||
base-10 integer value, making the new referenced value the
|
||||
array element with the zero-based index identified by the
|
||||
token, or
|
||||
|
||||
* exactly the single character "-", making the new referenced
|
||||
value the (nonexistent) member after the last array element.
|
||||
|
||||
The ABNF syntax for array indices is:
|
||||
|
||||
array-index = %x30 / ( %x31-39 *(%x30-39) )
|
||||
; "0", or digits without a leading "0"
|
||||
|
||||
Implementations will evaluate each reference token against the
|
||||
document's contents and will raise an error condition if it fails to
|
||||
resolve a concrete value for any of the JSON pointer's reference
|
||||
tokens. For example, if an array is referenced with a non-numeric
|
||||
token, an error condition will be raised. See Section 7 for details.
|
||||
|
||||
Note that the use of the "-" character to index an array will always
|
||||
result in such an error condition because by definition it refers to
|
||||
a nonexistent array element. Thus, applications of JSON Pointer need
|
||||
to specify how that character is to be handled, if it is to be
|
||||
useful.
|
||||
|
||||
Any error condition for which a specific action is not defined by the
|
||||
JSON Pointer application results in termination of evaluation.
|
||||
|
||||
5. JSON String Representation
|
||||
|
||||
A JSON Pointer can be represented in a JSON string value. Per
|
||||
[RFC4627], Section 2.5, all instances of quotation mark '"' (%x22),
|
||||
reverse solidus '\' (%x5C), and control (%x00-1F) characters MUST be
|
||||
escaped.
|
||||
|
||||
Note that before processing a JSON string as a JSON Pointer,
|
||||
backslash escape sequences must be unescaped.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan, et al. Standards Track [Page 4]
|
||||
|
||||
RFC 6901 JSON Pointer April 2013
|
||||
|
||||
|
||||
For example, given the JSON document
|
||||
|
||||
{
|
||||
"foo": ["bar", "baz"],
|
||||
"": 0,
|
||||
"a/b": 1,
|
||||
"c%d": 2,
|
||||
"e^f": 3,
|
||||
"g|h": 4,
|
||||
"i\\j": 5,
|
||||
"k\"l": 6,
|
||||
" ": 7,
|
||||
"m~n": 8
|
||||
}
|
||||
|
||||
The following JSON strings evaluate to the accompanying values:
|
||||
|
||||
"" // the whole document
|
||||
"/foo" ["bar", "baz"]
|
||||
"/foo/0" "bar"
|
||||
"/" 0
|
||||
"/a~1b" 1
|
||||
"/c%d" 2
|
||||
"/e^f" 3
|
||||
"/g|h" 4
|
||||
"/i\\j" 5
|
||||
"/k\"l" 6
|
||||
"/ " 7
|
||||
"/m~0n" 8
|
||||
|
||||
6. URI Fragment Identifier Representation
|
||||
|
||||
A JSON Pointer can be represented in a URI fragment identifier by
|
||||
encoding it into octets using UTF-8 [RFC3629], while percent-encoding
|
||||
those characters not allowed by the fragment rule in [RFC3986].
|
||||
|
||||
Note that a given media type needs to specify JSON Pointer as its
|
||||
fragment identifier syntax explicitly (usually, in its registration
|
||||
[RFC6838]). That is, just because a document is JSON does not imply
|
||||
that JSON Pointer can be used as its fragment identifier syntax. In
|
||||
particular, the fragment identifier syntax for application/json is
|
||||
not JSON Pointer.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan, et al. Standards Track [Page 5]
|
||||
|
||||
RFC 6901 JSON Pointer April 2013
|
||||
|
||||
|
||||
Given the same example document as above, the following URI fragment
|
||||
identifiers evaluate to the accompanying values:
|
||||
|
||||
# // the whole document
|
||||
#/foo ["bar", "baz"]
|
||||
#/foo/0 "bar"
|
||||
#/ 0
|
||||
#/a~1b 1
|
||||
#/c%25d 2
|
||||
#/e%5Ef 3
|
||||
#/g%7Ch 4
|
||||
#/i%5Cj 5
|
||||
#/k%22l 6
|
||||
#/%20 7
|
||||
#/m~0n 8
|
||||
|
||||
7. Error Handling
|
||||
|
||||
In the event of an error condition, evaluation of the JSON Pointer
|
||||
fails to complete.
|
||||
|
||||
Error conditions include, but are not limited to:
|
||||
|
||||
o Invalid pointer syntax
|
||||
|
||||
o A pointer that references a nonexistent value
|
||||
|
||||
This specification does not define how errors are handled. An
|
||||
application of JSON Pointer SHOULD specify the impact and handling of
|
||||
each type of error.
|
||||
|
||||
For example, some applications might stop pointer processing upon an
|
||||
error, while others may attempt to recover from missing values by
|
||||
inserting default ones.
|
||||
|
||||
8. Security Considerations
|
||||
|
||||
A given JSON Pointer is not guaranteed to reference an actual JSON
|
||||
value. Therefore, applications using JSON Pointer should anticipate
|
||||
this situation by defining how a pointer that does not resolve ought
|
||||
to be handled.
|
||||
|
||||
Note that JSON pointers can contain the NUL (Unicode U+0000)
|
||||
character. Care is needed not to misinterpret this character in
|
||||
programming languages that use NUL to mark the end of a string.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan, et al. Standards Track [Page 6]
|
||||
|
||||
RFC 6901 JSON Pointer April 2013
|
||||
|
||||
|
||||
9. Acknowledgements
|
||||
|
||||
The following individuals contributed ideas, feedback, and wording to
|
||||
this specification:
|
||||
|
||||
Mike Acar, Carsten Bormann, Tim Bray, Jacob Davies, Martin J.
|
||||
Duerst, Bjoern Hoehrmann, James H. Manger, Drew Perttula, and
|
||||
Julian Reschke.
|
||||
|
||||
10. References
|
||||
|
||||
10.1. Normative References
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
|
||||
10646", STD 63, RFC 3629, November 2003.
|
||||
|
||||
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
|
||||
Resource Identifier (URI): Generic Syntax", STD 66,
|
||||
RFC 3986, January 2005.
|
||||
|
||||
[RFC4627] Crockford, D., "The application/json Media Type for
|
||||
JavaScript Object Notation (JSON)", RFC 4627, July 2006.
|
||||
|
||||
[RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
|
||||
Specifications: ABNF", STD 68, RFC 5234, January 2008.
|
||||
|
||||
10.2. Informative References
|
||||
|
||||
[RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type
|
||||
Specifications and Registration Procedures", BCP 13,
|
||||
RFC 6838, January 2013.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan, et al. Standards Track [Page 7]
|
||||
|
||||
RFC 6901 JSON Pointer April 2013
|
||||
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Paul C. Bryan (editor)
|
||||
Salesforce.com
|
||||
|
||||
Phone: +1 604 783 1481
|
||||
EMail: pbryan@anode.ca
|
||||
|
||||
|
||||
Kris Zyp
|
||||
SitePen (USA)
|
||||
|
||||
Phone: +1 650 968 8787
|
||||
EMail: kris@sitepen.com
|
||||
|
||||
|
||||
Mark Nottingham (editor)
|
||||
Akamai
|
||||
|
||||
EMail: mnot@mnot.net
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bryan, et al. Standards Track [Page 8]
|
||||
|
189
examples/custom_schema.cpp
Normal file
189
examples/custom_schema.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Demonstrates validation against a manually constructed schema.
|
||||
*
|
||||
* This example demonstrates the construction and composition of a Schema object
|
||||
* using manually created Constraint objects. The following Constraint classes
|
||||
* are used:
|
||||
* - EnumConstraint
|
||||
* - MaxLengthConstraint
|
||||
* - MinimumConstraint
|
||||
* - MinLengthConstraint
|
||||
* - PropertiesConstraint
|
||||
* - RequiredConstraint
|
||||
* - TypeConstraint
|
||||
*
|
||||
* The MinimumConstraint class provides support for the exclusiveMinimum and
|
||||
* minimum keywords in JSON Schema. And the PropertiesConstraint class provides
|
||||
* support for the properties, patternProperties, and additionalProperties
|
||||
* keywords.
|
||||
*
|
||||
* This is the JSON Schema representation of the Schema that will be created:
|
||||
*
|
||||
* {
|
||||
* "properties": {
|
||||
* "category": {
|
||||
* "enum": [
|
||||
* "album",
|
||||
* "book",
|
||||
* "other",
|
||||
* "video"
|
||||
* ]
|
||||
* },
|
||||
* "description": {
|
||||
* "type": "string"
|
||||
* },
|
||||
* "price": {
|
||||
* "exclusiveMinimum": true,
|
||||
* "minimum": 0.0,
|
||||
* "type": "number"
|
||||
* },
|
||||
* "title": {
|
||||
* "maxLength": 200,
|
||||
* "minLength": 1,
|
||||
* "type": "string"
|
||||
* }
|
||||
* },
|
||||
* "required": [
|
||||
* "category",
|
||||
* "price",
|
||||
* "title"
|
||||
* ],
|
||||
* "type": "object"
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <valijson/adapters/rapidjson_adapter.hpp>
|
||||
#include <valijson/constraints/concrete_constraints.hpp>
|
||||
#include <valijson/utils/rapidjson_utils.hpp>
|
||||
#include <valijson/schema.hpp>
|
||||
#include <valijson/schema_parser.hpp>
|
||||
#include <valijson/validation_results.hpp>
|
||||
#include <valijson/validator.hpp>
|
||||
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
using valijson::Schema;
|
||||
using valijson::SchemaParser;
|
||||
using valijson::Validator;
|
||||
using valijson::ValidationResults;
|
||||
using valijson::adapters::RapidJsonAdapter;
|
||||
using valijson::adapters::RapidJsonFrozenValue;
|
||||
using valijson::constraints::EnumConstraint;
|
||||
using valijson::constraints::MaxLengthConstraint;
|
||||
using valijson::constraints::MinimumConstraint;
|
||||
using valijson::constraints::MinLengthConstraint;
|
||||
using valijson::constraints::PropertiesConstraint;
|
||||
using valijson::constraints::RequiredConstraint;
|
||||
using valijson::constraints::TypeConstraint;
|
||||
|
||||
void addPropertiesConstraint(Schema &schema)
|
||||
{
|
||||
|
||||
PropertiesConstraint::PropertySchemaMap propertySchemaMap;
|
||||
PropertiesConstraint::PropertySchemaMap patternPropertiesSchemaMap;
|
||||
|
||||
{
|
||||
// Create a child schema for the 'category' property that requires one
|
||||
// of several possible values.
|
||||
Schema &propertySchema = propertySchemaMap["category"];
|
||||
EnumConstraint::Values enumConstraintValues;
|
||||
enumConstraintValues.push_back(new RapidJsonFrozenValue("album"));
|
||||
enumConstraintValues.push_back(new RapidJsonFrozenValue("book"));
|
||||
enumConstraintValues.push_back(new RapidJsonFrozenValue("other"));
|
||||
enumConstraintValues.push_back(new RapidJsonFrozenValue("video"));
|
||||
propertySchema.addConstraint(new EnumConstraint(enumConstraintValues));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a child schema for the 'description' property that requires
|
||||
// a string, but does not enforce any length constraints.
|
||||
Schema &propertySchema = propertySchemaMap["description"];
|
||||
propertySchema.addConstraint(new TypeConstraint(TypeConstraint::kString));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a child schema for the 'price' property, that requires a
|
||||
// number with a value greater than zero.
|
||||
Schema &propertySchema = propertySchemaMap["price"];
|
||||
propertySchema.addConstraint(new MinimumConstraint(0.0, true));
|
||||
propertySchema.addConstraint(new TypeConstraint(TypeConstraint::kNumber));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a child schema for the 'title' property that requires a string
|
||||
// that is between 1 and 200 characters in length.
|
||||
Schema &propertySchema = propertySchemaMap["title"];
|
||||
propertySchema.addConstraint(new MaxLengthConstraint(200));
|
||||
propertySchema.addConstraint(new MinLengthConstraint(1));
|
||||
propertySchema.addConstraint(new TypeConstraint(TypeConstraint::kString));
|
||||
}
|
||||
|
||||
// Add a PropertiesConstraint to the schema, with the properties defined
|
||||
// above, no pattern properties, and with additional property schemas
|
||||
// prohibited.
|
||||
schema.addConstraint(new PropertiesConstraint(
|
||||
propertySchemaMap, patternPropertiesSchemaMap));
|
||||
}
|
||||
|
||||
void addRequiredConstraint(Schema &schema)
|
||||
{
|
||||
// Add a RequiredConstraint to the schema, specifying that the category,
|
||||
// price, and title properties must be present.
|
||||
RequiredConstraint::RequiredProperties requiredProperties;
|
||||
requiredProperties.insert("category");
|
||||
requiredProperties.insert("price");
|
||||
requiredProperties.insert("title");
|
||||
schema.addConstraint(new RequiredConstraint(requiredProperties));
|
||||
}
|
||||
|
||||
void addTypeConstraint(Schema &schema)
|
||||
{
|
||||
// Add a TypeConstraint to the schema, specifying that the root of the
|
||||
// document must be an object.
|
||||
schema.addConstraint(new TypeConstraint(TypeConstraint::kObject));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Load the document that is to be validated
|
||||
rapidjson::Document targetDocument;
|
||||
if (!valijson::utils::loadDocument(argv[1], targetDocument)) {
|
||||
cerr << "Failed to load target document." << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Populate a schema
|
||||
Schema schema;
|
||||
addPropertiesConstraint(schema);
|
||||
addRequiredConstraint(schema);
|
||||
addTypeConstraint(schema);
|
||||
|
||||
// Perform validation
|
||||
Validator validator(schema);
|
||||
ValidationResults results;
|
||||
RapidJsonAdapter targetDocumentAdapter(targetDocument);
|
||||
if (!validator.validate(targetDocumentAdapter, &results)) {
|
||||
std::cerr << "Validation failed." << endl;
|
||||
ValidationResults::Error error;
|
||||
unsigned int errorNum = 1;
|
||||
while (results.popError(error)) {
|
||||
cerr << "Error #" << errorNum << std::endl
|
||||
<< " context: " << error.context << endl
|
||||
<< " desc: " << error.description << endl;
|
||||
++errorNum;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
76
examples/external_schema.cpp
Normal file
76
examples/external_schema.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Demonstrates validation against a schema loaded from a file.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <valijson/adapters/rapidjson_adapter.hpp>
|
||||
#include <valijson/utils/rapidjson_utils.hpp>
|
||||
#include <valijson/schema.hpp>
|
||||
#include <valijson/schema_parser.hpp>
|
||||
#include <valijson/validation_results.hpp>
|
||||
#include <valijson/validator.hpp>
|
||||
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
using valijson::Schema;
|
||||
using valijson::SchemaParser;
|
||||
using valijson::Validator;
|
||||
using valijson::ValidationResults;
|
||||
using valijson::adapters::RapidJsonAdapter;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Load the document containing the schema
|
||||
rapidjson::Document schemaDocument;
|
||||
if (!valijson::utils::loadDocument(argv[1], schemaDocument)) {
|
||||
cerr << "Failed to load schema document." << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load the document that is to be validated
|
||||
rapidjson::Document targetDocument;
|
||||
if (!valijson::utils::loadDocument(argv[2], targetDocument)) {
|
||||
cerr << "Failed to load target document." << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse the json schema into an internal schema format
|
||||
Schema schema;
|
||||
SchemaParser parser;
|
||||
RapidJsonAdapter schemaDocumentAdapter(schemaDocument);
|
||||
try {
|
||||
parser.populateSchema(schemaDocumentAdapter, schema);
|
||||
} catch (...) {
|
||||
cerr << "Failed to parse schema." << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Perform validation
|
||||
Validator validator(schema);
|
||||
validator.setStrict(false);
|
||||
ValidationResults results;
|
||||
RapidJsonAdapter targetDocumentAdapter(targetDocument);
|
||||
if (!validator.validate(targetDocumentAdapter, &results)) {
|
||||
std::cerr << "Validation failed." << endl;
|
||||
ValidationResults::Error error;
|
||||
unsigned int errorNum = 1;
|
||||
while (results.popError(error)) {
|
||||
cerr << "Error #" << errorNum << std::endl
|
||||
<< " context: " << error.context << endl
|
||||
<< " desc: " << error.description << endl;
|
||||
++errorNum;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
475
include/valijson/adapters/adapter.hpp
Normal file
475
include/valijson/adapters/adapter.hpp
Normal file
@ -0,0 +1,475 @@
|
||||
#ifndef __VALIJSON_ADAPTERS_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_ADAPTER_HPP
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class FrozenValue;
|
||||
|
||||
/**
|
||||
* @brief An interface that encapsulates access to the JSON values provided
|
||||
* by a JSON parser implementation.
|
||||
*
|
||||
* This interface allows JSON processing code to be parser-agnostic. It provides
|
||||
* functions to access the plain old datatypes (PODs) that are described in the
|
||||
* JSON specification, and callback-based access to the contents of arrays and
|
||||
* objects.
|
||||
*
|
||||
* The interface also defines a set of functions that allow for type-casting and
|
||||
* type-comparison based on value rather than on type.
|
||||
*/
|
||||
class Adapter
|
||||
{
|
||||
public:
|
||||
|
||||
/// Typedef for callback function supplied to applyToArray.
|
||||
typedef boost::function<bool (const Adapter &)>
|
||||
ArrayValueCallback;
|
||||
|
||||
/// Typedef for callback function supplied to applyToObject.
|
||||
typedef boost::function<bool (const std::string &, const Adapter &)>
|
||||
ObjectMemberCallback;
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor defined to ensure deletion via base-class
|
||||
* pointers is safe.
|
||||
*/
|
||||
virtual ~Adapter() { };
|
||||
|
||||
/**
|
||||
* @brief Apply a callback function to each value in an array.
|
||||
*
|
||||
* The callback function is invoked for each element in the array, until
|
||||
* it has been applied to all values, or it returns false.
|
||||
*
|
||||
* @param fn Callback function to invoke
|
||||
*
|
||||
* @returns true if Adapter contains an array and all values are equal,
|
||||
* false otherwise.
|
||||
*/
|
||||
virtual bool applyToArray(ArrayValueCallback fn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Apply a callback function to each member in an object.
|
||||
*
|
||||
* The callback function shall be invoked for each member in the object,
|
||||
* until it has been applied to all values, or it returns false.
|
||||
*
|
||||
* @param fn Callback function to invoke
|
||||
*
|
||||
* @returns true if Adapter contains an object, and callback function
|
||||
* returns true for each member in the object, false otherwise.
|
||||
*/
|
||||
virtual bool applyToObject(ObjectMemberCallback fn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the boolean representation of the contained value.
|
||||
*
|
||||
* This function shall return a boolean value if the Adapter contains either
|
||||
* an actual boolean value, or one of the strings 'true' or 'false'.
|
||||
* The string comparison is case sensitive.
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a boolean.
|
||||
*
|
||||
* @returns Boolean representation of contained value.
|
||||
*/
|
||||
virtual bool asBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the boolean representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a boolean value if the Adapter contains
|
||||
* either an actual boolean value, or one of the strings 'true' or 'false'.
|
||||
* The string comparison is case sensitive.
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a bool to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asBool(bool &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the double representation of the contained value.
|
||||
*
|
||||
* This function shall return a double value if the Adapter contains either
|
||||
* an actual double, an integer, or a string that contains a valid
|
||||
* representation of a numeric value (according to the C++ Std Library).
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a double.
|
||||
*
|
||||
* @returns Double representation of contained value.
|
||||
*/
|
||||
virtual double asDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the double representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a double value if the Adapter contains either
|
||||
* an actual double, an integer, or a string that contains a valid
|
||||
* representation of a numeric value (according to the C++ Std Library).
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a double to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asDouble(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the int64_t representation of the contained value.
|
||||
*
|
||||
* This function shall return an int64_t value if the Adapter contains either
|
||||
* an actual integer, or a string that contains a valid representation of an
|
||||
* integer value (according to the C++ Std Library).
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to an int64_t.
|
||||
*
|
||||
* @returns int64_t representation of contained value.
|
||||
*/
|
||||
virtual int64_t asInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the int64_t representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve an int64_t value if the Adapter contains
|
||||
* either an actual integer, or a string that contains a valid
|
||||
* representation of an integer value (according to the C++ Std Library).
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a int64_t to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asInteger(int64_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the string representation of the contained value.
|
||||
*
|
||||
* This function shall return a string value if the Adapter contains either
|
||||
* an actual string, a literal value of another POD type, an empty array,
|
||||
* an empty object, or null.
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a string.
|
||||
*
|
||||
* @returns string representation of contained value.
|
||||
*/
|
||||
virtual std::string asString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the string representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a string value if the Adapter contains either
|
||||
* an actual string, a literal value of another POD type, an empty array,
|
||||
* an empty object, or null.
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a string to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asString(std::string &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Compare the value held by this Adapter instance with the value
|
||||
* held by another Adapter instance.
|
||||
*
|
||||
* @param other the other adapter instance
|
||||
* @param strict flag to use strict type comparison
|
||||
*
|
||||
* @returns true if values are equal, false otherwise
|
||||
*/
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Create a new FrozenValue instance that is equivalent to the
|
||||
* value contained by the Adapter.
|
||||
*
|
||||
* @returns pointer to a new FrozenValue instance, belonging to the caller.
|
||||
*/
|
||||
virtual FrozenValue* freeze() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the number of elements in the array.
|
||||
*
|
||||
* Throws an exception if the value is not an array.
|
||||
*
|
||||
* @return number of elements if value is an array
|
||||
*/
|
||||
virtual size_t getArraySize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array.
|
||||
*
|
||||
* This function shall return true or false to indicate whether or not the
|
||||
* result value was set. If the contained value is not an array, the
|
||||
* result value shall not be set. This applies even if the value could be
|
||||
* cast to an empty array. The calling code is expected to handles those
|
||||
* cases manually.
|
||||
*
|
||||
* @param result reference to size_t variable to set with result.
|
||||
*
|
||||
* @return true if value retrieved successfully, false otherwise.
|
||||
*/
|
||||
virtual bool getArraySize(size_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained boolean value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* boolean.
|
||||
*
|
||||
* @returns contained boolean value.
|
||||
*/
|
||||
virtual bool getBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained boolean value.
|
||||
*
|
||||
* This function shall retrieve the boolean value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to boolean variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getBool(bool &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained double value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* double.
|
||||
*
|
||||
* @returns contained double value.
|
||||
*/
|
||||
virtual double getDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained double value.
|
||||
*
|
||||
* This function shall retrieve the double value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to double variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getDouble(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained integer value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* integer.
|
||||
*
|
||||
* @returns contained integer value.
|
||||
*/
|
||||
virtual int64_t getInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained integer value.
|
||||
*
|
||||
* This function shall retrieve the integer value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to integer variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getInteger(int64_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained numeric value as a double.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* integer or a double.
|
||||
*
|
||||
* @returns contained double or integral value.
|
||||
*/
|
||||
virtual double getNumber() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained numeric value as a double.
|
||||
*
|
||||
* This function shall retrieve the double or integral value contained by
|
||||
* this Adapter, and store it in the result variable that was passed by
|
||||
* reference.
|
||||
*
|
||||
* @param result reference to double variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getNumber(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the number of members in the object.
|
||||
*
|
||||
* Throws an exception if the value is not an object.
|
||||
*
|
||||
* @return number of members if value is an object
|
||||
*/
|
||||
virtual size_t getObjectSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object.
|
||||
*
|
||||
* This function shall return true or false to indicate whether or not the
|
||||
* result value was set. If the contained value is not an object, the
|
||||
* result value shall not be set. This applies even if the value could be
|
||||
* cast to an empty object. The calling code is expected to handles those
|
||||
* cases manually.
|
||||
*
|
||||
* @param result reference to size_t variable to set with result.
|
||||
*
|
||||
* @return true if value retrieved successfully, false otherwise.
|
||||
*/
|
||||
virtual bool getObjectSize(size_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained string value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* string - even if the value could be cast to a string. The asString()
|
||||
* function should be used when casting is allowed.
|
||||
*
|
||||
* @returns string contained by this Adapter
|
||||
*/
|
||||
virtual std::string getString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained string value.
|
||||
*
|
||||
* This function shall retrieve the string value contained by this Adapter,
|
||||
* and store it in result variable that is passed by reference.
|
||||
*
|
||||
* @param result reference to string to set with result
|
||||
*
|
||||
* @returns true if string was retrieved, false otherwise
|
||||
*/
|
||||
virtual bool getString(std::string &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not this Adapter supports strict types.
|
||||
*
|
||||
* This function shall return true if the Adapter implementation supports
|
||||
* strict types, or false if the Adapter fails to store any part of the
|
||||
* type information supported by the Adapter interface.
|
||||
*
|
||||
* For example, the PropertyTreeAdapter implementation stores POD values as
|
||||
* strings, effectively discarding any other type information. If you were
|
||||
* to call isDouble() on a double stored by this Adapter, the result would
|
||||
* be false. The maybeDouble(), asDouble() and various related functions
|
||||
* are provided to perform type checking based on value rather than on type.
|
||||
*
|
||||
* The BasicAdapter template class provides implementations for the type-
|
||||
* casting functions so that Adapter implementations are semantically
|
||||
* equivalent in their type-casting behaviour.
|
||||
*
|
||||
* @returns true if Adapter supports strict types, false otherwise
|
||||
*/
|
||||
virtual bool hasStrictTypes() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an array.
|
||||
virtual bool isArray() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a boolean.
|
||||
virtual bool isBool() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a double.
|
||||
virtual bool isDouble() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an integer.
|
||||
virtual bool isInteger() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a null.
|
||||
virtual bool isNull() const = 0;
|
||||
|
||||
/// Returns true if the contained value is either a double or an integer.
|
||||
virtual bool isNumber() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an object.
|
||||
virtual bool isObject() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a string.
|
||||
virtual bool isString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an array.
|
||||
*
|
||||
* @returns true if the contained value is an array, an empty string, or an
|
||||
* empty object.
|
||||
*/
|
||||
virtual bool maybeArray() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a boolean.
|
||||
*
|
||||
* @returns true if the contained value is a boolean, or one of the strings
|
||||
* 'true' or 'false'. Note that numeric values are not to be cast
|
||||
* to boolean values.
|
||||
*/
|
||||
virtual bool maybeBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a double.
|
||||
*
|
||||
* @returns true if the contained value is a double, an integer, or a string
|
||||
* containing a double or integral value.
|
||||
*/
|
||||
virtual bool maybeDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an integer.
|
||||
*
|
||||
* @returns true if the contained value is an integer, or a string
|
||||
* containing an integral value.
|
||||
*/
|
||||
virtual bool maybeInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a null.
|
||||
*
|
||||
* @returns true if the contained value is null or an empty string.
|
||||
*/
|
||||
virtual bool maybeNull() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an object.
|
||||
*
|
||||
* @returns true if the contained value is an object, an empty array or
|
||||
* an empty string.
|
||||
*/
|
||||
virtual bool maybeObject() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a string.
|
||||
*
|
||||
* @returns true if the contained value is a non-null POD type, an empty
|
||||
* array, or an empty object.
|
||||
*/
|
||||
virtual bool maybeString() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Template struct that should be specialised for each concrete Adapter
|
||||
* class.
|
||||
*
|
||||
* @deprecated This is a bit of a hack, and I'd like to remove it.
|
||||
*/
|
||||
template<typename T>
|
||||
struct AdapterTraits
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
848
include/valijson/adapters/basic_adapter.hpp
Normal file
848
include/valijson/adapters/basic_adapter.hpp
Normal file
@ -0,0 +1,848 @@
|
||||
#ifndef __VALIJSON_ADAPTERS_BASIC_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_BASIC_ADAPTER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
/**
|
||||
* @brief Template class that implements the expected semantics of an Adapter.
|
||||
*
|
||||
* Implementing all of the type-casting functionality for each Adapter is error
|
||||
* prone and tedious, so this template class aims to minimise the duplication
|
||||
* of code between various Adapter implementations. This template doesn't quite
|
||||
* succeed in removing all duplication, but it has greatly simplified the
|
||||
* implementation of a new Adapter by encapsulating the type-casting semantics
|
||||
* and a lot of the trivial functionality associated with the Adapter interface.
|
||||
*
|
||||
* By inheriting from this template class, Adapter implementations will inherit
|
||||
* the exception throwing behaviour that is expected by other parts of the
|
||||
* Valijson library.
|
||||
*
|
||||
* @tparam AdapterType Self-referential name of the Adapter being
|
||||
* specialised.
|
||||
* @tparam ArrayType Name of the type that will be returned by the
|
||||
* getArray() function. Instances of this type should
|
||||
* provide begin(), end() and size() functions so
|
||||
* that it is possible to iterate over the values in
|
||||
* the array.
|
||||
* @tparam ObjectMemberType Name of the type exposed when iterating over the
|
||||
* contents of an object returned by getObject().
|
||||
* @tparam ObjectType Name of the type that will be returned by the
|
||||
* getObject() function. Instances of this type
|
||||
* should provide begin(), end(), find() and size()
|
||||
* functions so that it is possible to iterate over
|
||||
* the members of the object.
|
||||
* @tparam ValueType Name of the type that provides a consistent
|
||||
* interface to a JSON value for a parser. For
|
||||
* example, this type should provide the getDouble()
|
||||
* and isDouble() functions. But it does not need to
|
||||
* know how to cast values from one type to another -
|
||||
* that functionality is provided by this template
|
||||
* class.
|
||||
*/
|
||||
template<
|
||||
typename AdapterType,
|
||||
typename ArrayType,
|
||||
typename ObjectMemberType,
|
||||
typename ObjectType,
|
||||
typename ValueType>
|
||||
class BasicAdapter: public Adapter
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief Functor for comparing two arrays.
|
||||
*
|
||||
* This functor is used to compare the elements in an array of the type
|
||||
* ArrayType with individual values provided as generic Adapter objects.
|
||||
* Comparison is performed by the () operator.
|
||||
*
|
||||
* The functor works by maintaining an iterator for the current position
|
||||
* in an array. Each time the () operator is called, the value at this
|
||||
* position is compared with the value passed as an argument to ().
|
||||
* Immediately after the comparison, the iterator will be incremented.
|
||||
*
|
||||
* This functor is designed to be passed to the applyToArray() function
|
||||
* of an Adapter object.
|
||||
*/
|
||||
class ArrayComparisonFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an ArrayComparisonFunctor for an array.
|
||||
*
|
||||
* @param array Array to compare values against
|
||||
* @param strict Flag to use strict type comparison
|
||||
*/
|
||||
ArrayComparisonFunctor(const ArrayType &array, bool strict)
|
||||
: itr(array.begin()),
|
||||
end(array.end()),
|
||||
strict(strict) { }
|
||||
|
||||
/**
|
||||
* @brief Compare a value against the current element in the array.
|
||||
*
|
||||
* @param adapter Value to be compared with current element
|
||||
*
|
||||
* @returns true if values are equal, false otherwise.
|
||||
*/
|
||||
bool operator()(const Adapter &adapter)
|
||||
{
|
||||
if (itr == end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return AdapterType(*itr++).equalTo(adapter, strict);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iterator for current element in the array
|
||||
typename ArrayType::const_iterator itr;
|
||||
|
||||
/// Iterator for one-past the last element of the array
|
||||
typename ArrayType::const_iterator end;
|
||||
|
||||
/// Flag to use strict type comparison
|
||||
const bool strict;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Functor for comparing two objects
|
||||
*
|
||||
* This functor is used to compare the members of an object of the type
|
||||
* ObjectType with key-value pairs belonging to another object.
|
||||
*
|
||||
* The functor works by maintaining a reference to an object provided via
|
||||
* the constructor. When time the () operator is called with a key-value
|
||||
* pair as arguments, the function will attempt to find the key in the
|
||||
* base object. If found, the associated value will be compared with the
|
||||
* value provided to the () operator.
|
||||
*
|
||||
* This functor is designed to be passed to the applyToObject() function
|
||||
* of an Adapter object.
|
||||
*/
|
||||
class ObjectComparisonFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new ObjectComparisonFunctor for an object.
|
||||
*
|
||||
* @param object object to use as comparison baseline
|
||||
* @param strict flag to use strict type-checking
|
||||
*/
|
||||
ObjectComparisonFunctor(
|
||||
const ObjectType &object, bool strict)
|
||||
: object(object),
|
||||
strict(strict) { }
|
||||
|
||||
/**
|
||||
* @brief Find a key in the object and compare its value.
|
||||
*
|
||||
* @param key Key to find
|
||||
* @param value Value to be compared against
|
||||
*
|
||||
* @returns true if key is found and values are equal, false otherwise.
|
||||
*/
|
||||
bool operator()(const std::string &key, const Adapter &value)
|
||||
{
|
||||
const typename ObjectType::const_iterator itr = object.find(key);
|
||||
if (itr == object.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (*itr).second.equalTo(value, strict);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Object to be used as a comparison baseline
|
||||
const ObjectType &object;
|
||||
|
||||
/// Flag to use strict type-checking
|
||||
bool strict;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// Alias for ArrayType template parameter
|
||||
typedef ArrayType Array;
|
||||
|
||||
/// Alias for ObjectMemberType template parameter
|
||||
typedef ObjectMemberType ObjectMember;
|
||||
|
||||
/// Alias for ObjectType template parameter
|
||||
typedef ObjectType Object;
|
||||
|
||||
/**
|
||||
* @brief Construct an Adapter using the default value.
|
||||
*
|
||||
* This constructor relies on the default constructor of the ValueType
|
||||
* class provided as a template argument.
|
||||
*/
|
||||
BasicAdapter() { }
|
||||
|
||||
/**
|
||||
* @brief Construct an Adapter using a specified ValueType object.
|
||||
*
|
||||
* This constructor relies on the copy constructor of the ValueType
|
||||
* class provided as template argument.
|
||||
*/
|
||||
BasicAdapter(const ValueType &value)
|
||||
: value(value) { }
|
||||
|
||||
virtual bool applyToArray(ArrayValueCallback fn) const
|
||||
{
|
||||
if (!maybeArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Due to the fact that the only way a value can be 'maybe an array' is
|
||||
// if it is an empty string or empty object, we only need to go to
|
||||
// effort of constructing an ArrayType instance if the value is
|
||||
// definitely an array.
|
||||
if (value.isArray()) {
|
||||
const boost::optional<Array> array = value.getArrayOptional();
|
||||
BOOST_FOREACH( const AdapterType element, *array ) {
|
||||
if (!fn(element)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool applyToObject(ObjectMemberCallback fn) const
|
||||
{
|
||||
if (!maybeObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.isObject()) {
|
||||
const boost::optional<Object> object = value.getObjectOptional();
|
||||
BOOST_FOREACH( const ObjectMemberType member, *object ) {
|
||||
if (!fn(member.first, AdapterType(member.second))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ArrayType instance containing an array representation
|
||||
* of the value held by this Adapter.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the elements in an array. The ArrayType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained value is either an empty object, or an empty string,
|
||||
* then this function will cast the value to an empty array.
|
||||
*
|
||||
* @returns ArrayType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ArrayType asArray() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return *value.getArrayOptional();
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return ArrayType();
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue) && stringValue.empty()) {
|
||||
return ArrayType();
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to an array.");
|
||||
}
|
||||
|
||||
virtual bool asBool() const
|
||||
{
|
||||
bool result;
|
||||
if (asBool(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a boolean.");
|
||||
}
|
||||
|
||||
virtual bool asBool(bool &result) const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
return value.getBool(result);
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
if (s.compare("true") == 0) {
|
||||
result = true;
|
||||
return true;
|
||||
} else if (s.compare("false") == 0) {
|
||||
result = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual double asDouble() const
|
||||
{
|
||||
double result;
|
||||
if (asDouble(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a double.");
|
||||
}
|
||||
|
||||
virtual bool asDouble(double &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
return value.getDouble(result);
|
||||
} else if (value.isInteger()) {
|
||||
int64_t i;
|
||||
if (value.getInteger(i)) {
|
||||
result = double(i);
|
||||
return true;
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
std::istringstream i(s);
|
||||
double x;
|
||||
char c;
|
||||
if (!(!(i >> x) || i.get(c))) {
|
||||
result = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual int64_t asInteger() const
|
||||
{
|
||||
int64_t result;
|
||||
if (asInteger(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast as an integer.");
|
||||
}
|
||||
|
||||
virtual bool asInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isInteger()) {
|
||||
return value.getInteger(result);
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
std::istringstream i(s);
|
||||
int64_t x;
|
||||
char c;
|
||||
if (!(!(i >> x) || i.get(c))) {
|
||||
result = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ObjectType instance containing an array representation
|
||||
* of the value held by this Adapter.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the members of the object. The ObjectType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* @returns ObjectType instance containing an object representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ObjectType asObject() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return *value.getObjectOptional();
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return ObjectType();
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue) && stringValue.empty()) {
|
||||
return ObjectType();
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to an object.");
|
||||
}
|
||||
|
||||
virtual std::string asString() const
|
||||
{
|
||||
std::string result;
|
||||
if (asString(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a string.");
|
||||
}
|
||||
|
||||
virtual bool asString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
return value.getString(result);
|
||||
} else if (value.isNull()) {
|
||||
result.clear();
|
||||
return true;
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
result.clear();
|
||||
return true;
|
||||
}
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
result.clear();
|
||||
return true;
|
||||
}
|
||||
} else if (value.isBool()) {
|
||||
bool boolValue;
|
||||
if (value.getBool(boolValue)) {
|
||||
result = boolValue ? "true" : "false";
|
||||
return true;
|
||||
}
|
||||
} else if (value.isInteger()) {
|
||||
int64_t integerValue;
|
||||
if (value.getInteger(integerValue)) {
|
||||
try {
|
||||
result = boost::lexical_cast<std::string>(integerValue);
|
||||
return true;
|
||||
} catch (boost::bad_lexical_cast &) { }
|
||||
}
|
||||
} else if (value.isDouble()) {
|
||||
double doubleValue;
|
||||
if (value.getDouble(doubleValue)) {
|
||||
try {
|
||||
result = boost::lexical_cast<std::string>(doubleValue);
|
||||
return true;
|
||||
} catch (boost::bad_lexical_cast &) { }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
if (isNull() || (!strict && maybeNull())) {
|
||||
return other.isNull() || (!strict && other.maybeNull());
|
||||
} else if (isBool() || (!strict && maybeBool())) {
|
||||
return (other.isBool() || (!strict && other.maybeBool())) &&
|
||||
other.asBool() == asBool();
|
||||
} else if (isNumber() && strict) {
|
||||
return other.isNumber() && other.getNumber() == getNumber();
|
||||
} else if (!strict && maybeDouble()) {
|
||||
return (other.maybeDouble() &&
|
||||
other.asDouble() == asDouble());
|
||||
} else if (!strict && maybeInteger()) {
|
||||
return (other.maybeInteger() &&
|
||||
other.asInteger() == asInteger());
|
||||
} else if (isString() || (!strict && maybeString())) {
|
||||
return (other.isString() || (!strict && other.maybeString())) &&
|
||||
other.asString() == asString();
|
||||
} else if (isArray()) {
|
||||
if (other.isArray() && getArraySize() == other.getArraySize()) {
|
||||
const boost::optional<ArrayType> array = value.getArrayOptional();
|
||||
if (array) {
|
||||
ArrayComparisonFunctor fn(*array, strict);
|
||||
return other.applyToArray(fn);
|
||||
}
|
||||
} else if (!strict && other.maybeArray() && getArraySize() == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (isObject()) {
|
||||
if (other.isObject() && other.getObjectSize() == getObjectSize()) {
|
||||
const boost::optional<ObjectType> object = value.getObjectOptional();
|
||||
if (object) {
|
||||
ObjectComparisonFunctor fn(*object, strict);
|
||||
return other.applyToObject(fn);
|
||||
}
|
||||
} else if (!strict && other.maybeObject() && getObjectSize() == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ArrayType instance representing the array contained
|
||||
* by this Adapter instance.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the elements in an array. The ArrayType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained is not an array, this function will throw an exception.
|
||||
*
|
||||
* @returns ArrayType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ArrayType getArray() const
|
||||
{
|
||||
boost::optional<ArrayType> arrayValue = value.getArrayOptional();
|
||||
if (arrayValue) {
|
||||
return *arrayValue;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an array.");
|
||||
}
|
||||
|
||||
virtual size_t getArraySize() const
|
||||
{
|
||||
size_t result;
|
||||
if (value.getArraySize(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an array.");
|
||||
}
|
||||
|
||||
virtual bool getArraySize(size_t &result) const
|
||||
{
|
||||
return value.getArraySize(result);
|
||||
}
|
||||
|
||||
virtual bool getBool() const
|
||||
{
|
||||
bool result;
|
||||
if (getBool(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a boolean.");
|
||||
}
|
||||
|
||||
virtual bool getBool(bool &result) const
|
||||
{
|
||||
return value.getBool(result);
|
||||
}
|
||||
|
||||
virtual double getDouble() const
|
||||
{
|
||||
double result;
|
||||
if (getDouble(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a double.");
|
||||
}
|
||||
|
||||
virtual bool getDouble(double &result) const
|
||||
{
|
||||
return value.getDouble(result);
|
||||
}
|
||||
|
||||
virtual int64_t getInteger() const
|
||||
{
|
||||
int64_t result;
|
||||
if (getInteger(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an integer.");
|
||||
}
|
||||
|
||||
virtual bool getInteger(int64_t &result) const
|
||||
{
|
||||
return value.getInteger(result);
|
||||
}
|
||||
|
||||
virtual double getNumber() const
|
||||
{
|
||||
double result;
|
||||
if (getNumber(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a number.");
|
||||
}
|
||||
|
||||
virtual bool getNumber(double &result) const
|
||||
{
|
||||
if (isDouble()) {
|
||||
return getDouble(result);
|
||||
} else if (isInteger()) {
|
||||
int64_t integerResult;
|
||||
if (getInteger(integerResult)) {
|
||||
result = static_cast<double>(integerResult);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ObjectType instance representing the object contained
|
||||
* by this Adapter instance.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the members of an object. The ObjectType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained is not an object, this function will throw an exception.
|
||||
*
|
||||
* @returns ObjectType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ObjectType getObject() const
|
||||
{
|
||||
boost::optional<ObjectType> objectValue = value.getObjectOptional();
|
||||
if (objectValue) {
|
||||
return *objectValue;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an object.");
|
||||
}
|
||||
|
||||
virtual size_t getObjectSize() const
|
||||
{
|
||||
size_t result;
|
||||
if (getObjectSize(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an object.");
|
||||
}
|
||||
|
||||
virtual bool getObjectSize(size_t &result) const
|
||||
{
|
||||
return value.getObjectSize(result);
|
||||
}
|
||||
|
||||
virtual std::string getString() const
|
||||
{
|
||||
std::string result;
|
||||
if (getString(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a string.");
|
||||
}
|
||||
|
||||
virtual bool getString(std::string &result) const
|
||||
{
|
||||
return value.getString(result);
|
||||
}
|
||||
|
||||
virtual FrozenValue * freeze() const
|
||||
{
|
||||
return value.freeze();
|
||||
}
|
||||
|
||||
virtual bool hasStrictTypes() const
|
||||
{
|
||||
return ValueType::hasStrictTypes();
|
||||
}
|
||||
|
||||
virtual bool isArray() const
|
||||
{
|
||||
return value.isArray();
|
||||
}
|
||||
|
||||
virtual bool isBool() const
|
||||
{
|
||||
return value.isBool();
|
||||
}
|
||||
|
||||
virtual bool isDouble() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
virtual bool isInteger() const
|
||||
{
|
||||
return value.isInteger();
|
||||
}
|
||||
|
||||
virtual bool isNull() const
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
|
||||
virtual bool isNumber() const
|
||||
{
|
||||
return value.isInteger() || value.isDouble();
|
||||
}
|
||||
|
||||
virtual bool isObject() const
|
||||
{
|
||||
return value.isObject();
|
||||
}
|
||||
|
||||
virtual bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
virtual bool maybeArray() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return true;
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeBool() const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue)) {
|
||||
if (stringValue.compare("true") == 0 || stringValue.compare("false") == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeDouble() const
|
||||
{
|
||||
if (value.isNumber()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
std::istringstream i(s);
|
||||
double x;
|
||||
char c;
|
||||
if (!(i >> x) || i.get(c)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeInteger() const
|
||||
{
|
||||
if (value.isInteger()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
std::istringstream i(s);
|
||||
int64_t x;
|
||||
char c;
|
||||
if (!(i >> x) || i.get(c)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeNull() const
|
||||
{
|
||||
if (value.isNull()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue)) {
|
||||
if (stringValue.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeObject() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return true;
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool maybeString() const
|
||||
{
|
||||
if (value.isString() || value.isBool() || value.isInteger() ||
|
||||
value.isDouble()) {
|
||||
return true;
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const ValueType value;
|
||||
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
68
include/valijson/adapters/frozen_value.hpp
Normal file
68
include/valijson/adapters/frozen_value.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef __VALIJSON_ADAPTERS_FROZEN_VALUE_HPP
|
||||
#define __VALIJSON_ADAPTERS_FROZEN_VALUE_HPP
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
/**
|
||||
* @brief An interface that provides minimal access to a stored JSON value.
|
||||
*
|
||||
* The main reason that this interface exists is to support the 'enum'
|
||||
* constraint. Each Adapter type is expected to provide an implementation of
|
||||
* this interface. That class should be able to maintain its own copy of a
|
||||
* JSON value, independent of the original document.
|
||||
*
|
||||
* This interface currently provides just the clone and equalTo functions, but
|
||||
* could be expanded to include other functions declared in the Adapter
|
||||
* interface.
|
||||
*
|
||||
* @todo it would be nice to better integrate this with the Adapter interface
|
||||
*/
|
||||
class FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor defined to ensure deletion via base-class
|
||||
* pointers is safe.
|
||||
*/
|
||||
virtual ~FrozenValue() { }
|
||||
|
||||
/**
|
||||
* @brief Clone the stored value and return a pointer to a new FrozenValue
|
||||
* object containing the value.
|
||||
*/
|
||||
virtual FrozenValue *clone() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return true if the stored value is equal to the value contained
|
||||
* by an Adapter instance.
|
||||
*
|
||||
* @param adapter Adapter to compare value against
|
||||
* @param strict Flag to use strict type comparison
|
||||
*
|
||||
* @returns true if values are equal, false otherwise
|
||||
*/
|
||||
virtual bool equalTo(const Adapter &adapter, bool strict) const = 0;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implementation of new_clone for the FrozenValue interface, as
|
||||
* required for the boost pointer containers.
|
||||
*
|
||||
* @param frozenValue reference to FrozenValue to clone
|
||||
*
|
||||
* @returns a pointer to a new FrozenValue, belonging to the caller
|
||||
*/
|
||||
inline FrozenValue * new_clone(const FrozenValue &frozenValue)
|
||||
{
|
||||
return frozenValue.clone();
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
685
include/valijson/adapters/jsoncpp_adapter.hpp
Normal file
685
include/valijson/adapters/jsoncpp_adapter.hpp
Normal file
@ -0,0 +1,685 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the JsonCpp parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for JsonCpp.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - JsonCppAdapter
|
||||
* - JsonCppArray
|
||||
* - JsonCppArrayValueIterator
|
||||
* - JsonCppFrozenValue
|
||||
* - JsonCppObject
|
||||
* - JsonCppObjectMember
|
||||
* - JsonCppObjectMemberIterator
|
||||
* - JsonCppValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is JsonCppAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#ifndef __VALIJSON_ADAPTERS_JSONCPP_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_JSONCPP_ADAPTER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class JsonCppAdapter;
|
||||
class JsonCppArrayValueIterator;
|
||||
class JsonCppObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, JsonCppAdapter> JsonCppObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp array value.
|
||||
*
|
||||
* This class is light weight wrapper for a JsonCpp array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* JsonCpp value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class JsonCppArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef JsonCppArrayValueIterator const_iterator;
|
||||
typedef JsonCppArrayValueIterator iterator;
|
||||
|
||||
/// Construct a JsonCppArray referencing an empty array.
|
||||
JsonCppArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a JsonCppArray referencing a specific JsonCpp value.
|
||||
*
|
||||
* @param value reference to a JsonCpp value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
JsonCppArray(const Json::Value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.isArray()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array.
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a JsonCpp value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const Json::Value & emptyArray()
|
||||
{
|
||||
static const Json::Value array(Json::arrayValue);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained array
|
||||
const Json::Value &value;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp object.
|
||||
*
|
||||
* This class is light weight wrapper for a JsonCpp object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* JsonCpp object, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class JsonCppObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef JsonCppObjectMemberIterator const_iterator;
|
||||
typedef JsonCppObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a JsonCppObject referencing an empty object singleton.
|
||||
JsonCppObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a JsonCppObject referencing a specific JsonCpp value.
|
||||
*
|
||||
* @param value reference to a JsonCpp value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
JsonCppObject(const Json::Value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.isObject()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for a member/property with the given name
|
||||
*
|
||||
* @param propertyName Property name
|
||||
*
|
||||
* @returns a valid iterator if found, or an invalid iterator if not found
|
||||
*/
|
||||
JsonCppObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Return the number of members in the object
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty JsonCpp object
|
||||
static const Json::Value & emptyObject()
|
||||
{
|
||||
static const Json::Value object(Json::objectValue);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const Json::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a JsonCpp value.
|
||||
*
|
||||
* This class allows a JsonCpp value to be stored independent of its original
|
||||
* document. JsonCpp makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class JsonCppFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a JsonCpp value
|
||||
*
|
||||
* @param source the JsonCpp value to be copied
|
||||
*/
|
||||
JsonCppFrozenValue(const Json::Value &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new JsonCppFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored JsonCpp value
|
||||
Json::Value value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a JsonCpp value. This class is responsible
|
||||
* for the mechanics of actually reading a JsonCpp value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class JsonCppValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
JsonCppValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific JsonCpp value
|
||||
JsonCppValue(const Json::Value &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new JsonCppFrozenValue instance that contains the
|
||||
* value referenced by this JsonCppValue instance.
|
||||
*
|
||||
* @returns pointer to a new JsonCppFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new JsonCppFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a JsonCppArray instance.
|
||||
*
|
||||
* If the referenced JsonCpp value is an array, this function will return a
|
||||
* boost::optional containing a JsonCppArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return boost::none.
|
||||
*/
|
||||
boost::optional<JsonCppArray> getArrayOptional() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return boost::make_optional(JsonCppArray(value));
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced JsonCpp value is an array, this function will retrieve
|
||||
* the number of elements in the array and store it in the output variable
|
||||
* provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
result = value.asBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
result = value.asDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isIntegral()) {
|
||||
result = static_cast<int64_t>(value.asInt());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a JsonCppObject instance.
|
||||
*
|
||||
* If the referenced JsonCpp value is an object, this function will return a
|
||||
* boost::optional containing a JsonCppObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return boost::none.
|
||||
*/
|
||||
boost::optional<JsonCppObject> getObjectOptional() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return boost::make_optional(JsonCppObject(value));
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced JsonCpp value is an object, this function will retrieve
|
||||
* the number of members in the object and store it in the output variable
|
||||
* provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
result = value.asString();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.isArray() && !value.isNull();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.isBool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.isIntegral() && !value.isBool();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.isNumeric() && !value.isBool();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.isObject() && !value.isNull();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton.
|
||||
static const Json::Value &emptyObject()
|
||||
{
|
||||
static Json::Value object(Json::objectValue);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained JsonCpp value
|
||||
const Json::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting JsonCpp.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class JsonCppAdapter:
|
||||
public BasicAdapter<JsonCppAdapter,
|
||||
JsonCppArray,
|
||||
JsonCppObjectMember,
|
||||
JsonCppObject,
|
||||
JsonCppValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a JsonCppAdapter that contains an empty object
|
||||
JsonCppAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a JsonCppAdapter containing a specific JsonCpp value
|
||||
JsonCppAdapter(const Json::Value &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* JsonCppAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see JsonCppArray
|
||||
*/
|
||||
class JsonCppArrayValueIterator:
|
||||
public boost::iterator_facade<
|
||||
JsonCppArrayValueIterator, // name of derived type
|
||||
JsonCppAdapter, // value type
|
||||
boost::bidirectional_traversal_tag, // bi-directional iterator
|
||||
JsonCppAdapter> // type returned when dereferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new JsonCppArrayValueIterator using an existing
|
||||
* JsonCpp iterator.
|
||||
*
|
||||
* @param itr JsonCpp iterator to store
|
||||
*/
|
||||
JsonCppArrayValueIterator(const Json::Value::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a JsonCppAdapter that contains the value of the current element.
|
||||
JsonCppAdapter dereference() const
|
||||
{
|
||||
return JsonCppAdapter(*itr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool equal(const JsonCppArrayValueIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
itr--;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
if (n > 0) {
|
||||
while (n-- > 0) {
|
||||
itr++;
|
||||
}
|
||||
} else {
|
||||
while (n++ < 0) {
|
||||
itr--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Json::Value::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of JsonCppObjectMember representing one of the members of the object. It has
|
||||
* been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see JsonCppObject
|
||||
* @see JsonCppObjectMember
|
||||
*/
|
||||
class JsonCppObjectMemberIterator:
|
||||
public boost::iterator_facade<
|
||||
JsonCppObjectMemberIterator, // name of derived type
|
||||
JsonCppObjectMember, // value type
|
||||
boost::bidirectional_traversal_tag, // bi-directional iterator
|
||||
JsonCppObjectMember> // type returned when dereferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a JsonCpp iterator.
|
||||
*
|
||||
* @param itr JsonCpp iterator to store
|
||||
*/
|
||||
JsonCppObjectMemberIterator(const Json::ValueConstIterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a JsonCppObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
JsonCppObjectMember dereference() const
|
||||
{
|
||||
return JsonCppObjectMember(itr.key().asString(), *itr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool equal(const JsonCppObjectMemberIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
itr--;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original JsonCpp iterator
|
||||
Json::ValueConstIterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for JsonCppAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::JsonCppAdapter>
|
||||
{
|
||||
typedef Json::Value DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "JsonCppAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool JsonCppFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return JsonCppAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline JsonCppArrayValueIterator JsonCppArray::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline JsonCppArrayValueIterator JsonCppArray::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
if (value.isMember(propertyName)) {
|
||||
Json::ValueConstIterator itr;
|
||||
for ( itr = value.begin(); itr != value.end(); ++itr) {
|
||||
if (itr.key() == propertyName) {
|
||||
return itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value.end();
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
718
include/valijson/adapters/property_tree_adapter.hpp
Normal file
718
include/valijson/adapters/property_tree_adapter.hpp
Normal file
@ -0,0 +1,718 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the Boost property tree library.
|
||||
*
|
||||
* Include this file in your program to enable support for boost property trees.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - PropertyTreeAdapter
|
||||
* - PropertyTreeArray
|
||||
* - PropertyTreeArrayValueIterator
|
||||
* - PropertyTreeFrozenValue
|
||||
* - PropertyTreeObject
|
||||
* - PropertyTreeObjectMember
|
||||
* - PropertyTreeObjectMemberIterator
|
||||
* - PropertyTreeValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is PropertyTreeAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#ifndef __VALIJSON_ADAPTERS_PROPERTY_TREE_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_PROPERTY_TREE_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class PropertyTreeAdapter;
|
||||
class PropertyTreeArrayValueIterator;
|
||||
class PropertyTreeObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, PropertyTreeAdapter> PropertyTreeObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree that contains
|
||||
* array-like data.
|
||||
*
|
||||
* This class is light weight wrapper for a Boost property tree. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to a Boost property
|
||||
* tree that is assumed to contain unnamed key-value pairs. There is very little
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PropertyTreeArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PropertyTreeArrayValueIterator const_iterator;
|
||||
typedef PropertyTreeArrayValueIterator iterator;
|
||||
|
||||
/// Construct a PropertyTreeArra7 referencing an empty property tree
|
||||
/// singleton.
|
||||
PropertyTreeArray()
|
||||
: array(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct PropertyTreeArray referencing a specific Boost
|
||||
* property tree.
|
||||
*
|
||||
* @param array reference to a property tree containing an array
|
||||
*
|
||||
* It is assumed that this value contains array-like data, but this is not
|
||||
* checked due to runtime cost.
|
||||
*/
|
||||
PropertyTreeArray(const boost::property_tree::ptree &array)
|
||||
: array(array) { }
|
||||
|
||||
/// Return an iterator for the first element in the array.
|
||||
PropertyTreeArrayValueIterator begin() const;
|
||||
|
||||
/// Return an iterator for one-past the last element of the array.
|
||||
PropertyTreeArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return array.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a property tree that looks like an
|
||||
* empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const boost::property_tree::ptree &array;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree that contains
|
||||
* object-like data.
|
||||
*
|
||||
* This class is light weight wrapper for a Boost property tree. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* property tree value, assumed to be object-like, so there is very little
|
||||
* overhead associated with copy construction and passing by value.
|
||||
*/
|
||||
class PropertyTreeObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PropertyTreeObjectMemberIterator const_iterator;
|
||||
typedef PropertyTreeObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a PropertyTreeObject referencing an empty property tree.
|
||||
PropertyTreeObject()
|
||||
: object(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PropertyTreeObject referencing a specific property
|
||||
* tree.
|
||||
*
|
||||
* @param object reference to a property tree containing an object
|
||||
*
|
||||
* Note that the value of the property tree is not checked, due to the
|
||||
* runtime cost of doing so.
|
||||
*/
|
||||
PropertyTreeObject(const boost::property_tree::ptree &object)
|
||||
: object(object) { }
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying property tree
|
||||
* implementation.
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the pointer value returned by the underlying property tree
|
||||
* implementation.
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param property property name to search for
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator find(const std::string &property) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return object.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to an empty property tree.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const boost::property_tree::ptree &object;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a Boost property tree.
|
||||
*
|
||||
* This class allows a property tree value to be stored independent of its
|
||||
* original 'document'. Boost property trees make this easy to do, as they do
|
||||
* not perform any custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class PropertyTreeFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a Boost property tree POD value
|
||||
*
|
||||
* @param source string containing the POD vlaue
|
||||
*/
|
||||
PropertyTreeFrozenValue(const boost::property_tree::ptree::data_type &source)
|
||||
: value(source) { }
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a Boost property tree object or array value
|
||||
*
|
||||
* @param source the property tree to be copied
|
||||
*/
|
||||
PropertyTreeFrozenValue(const boost::property_tree::ptree &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new PropertyTreeFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored value
|
||||
boost::property_tree::ptree value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a Boost property tree value. This class
|
||||
* is responsible for the mechanics of actually reading a property tree, whereas
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PropertyTreeValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for an empty property tree
|
||||
PropertyTreeValue()
|
||||
: object(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PropertyTreeValue from a tree object
|
||||
*
|
||||
* This function will determine whether the tree object represents an array
|
||||
* or an object by scanning the key names for any non-empty strings. In the
|
||||
* case of an empty tree object, it is not possible to determine whether it
|
||||
* is an array or an object, so it will be treated as an array by default.
|
||||
* Empty arrays are considered equal to empty objects when compared using
|
||||
* non-strict type comparison. Empty strings will also be stored as empty
|
||||
* arrays.
|
||||
*
|
||||
* @param tree Tree object to be wrapped
|
||||
*/
|
||||
PropertyTreeValue(const boost::property_tree::ptree &tree)
|
||||
{
|
||||
if (tree.data().empty()) { // No string content
|
||||
if (tree.size() == 0) { // No children
|
||||
array = tree; // Treat as empty array
|
||||
} else {
|
||||
bool isArray = true;
|
||||
boost::property_tree::ptree::const_iterator itr;
|
||||
for (itr = tree.begin(); itr != tree.end(); itr++) {
|
||||
if (!itr->first.empty()) {
|
||||
isArray = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isArray) {
|
||||
array = tree;
|
||||
} else {
|
||||
object = tree;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = tree.data();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new PropertyTreeFrozenValue instance that contains the
|
||||
* value referenced by this PropertyTreeValue instance.
|
||||
*
|
||||
* @returns pointer to a new PropertyTreeFrozenValue instance, belonging to
|
||||
* the caller.
|
||||
*/
|
||||
FrozenValue* freeze() const
|
||||
{
|
||||
if (array) {
|
||||
return new PropertyTreeFrozenValue(*array);
|
||||
} else if (object) {
|
||||
return new PropertyTreeFrozenValue(*object);
|
||||
} else {
|
||||
return new PropertyTreeFrozenValue(*value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an instance of PropertyTreeArrayAdapter.
|
||||
*
|
||||
* If the referenced property tree value is an array, this function will
|
||||
* return a boost::optional containing a PropertyTreeArray instance
|
||||
* referencing the array.
|
||||
*
|
||||
* Otherwise it will return boost::none.
|
||||
*/
|
||||
boost::optional<PropertyTreeArray> getArrayOptional() const
|
||||
{
|
||||
if (array) {
|
||||
return boost::make_optional(PropertyTreeArray(*array));
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced property tree value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (array) {
|
||||
result = array->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PropertyTreeObject instance.
|
||||
*
|
||||
* If the referenced property tree is an object, this function will return a
|
||||
* boost::optional containing a PropertyTreeObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return boost::none.
|
||||
*/
|
||||
boost::optional<PropertyTreeObject> getObjectOptional() const
|
||||
{
|
||||
if (object) {
|
||||
return boost::make_optional(PropertyTreeObject(*object));
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced property tree value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (object) {
|
||||
result = object->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value) {
|
||||
result = *value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return array != boost::none;
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return object != boost::none;
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value != boost::none;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference used if the value is known to be an array
|
||||
boost::optional<const boost::property_tree::ptree &> array;
|
||||
|
||||
/// Reference used if the value is known to be an object
|
||||
boost::optional<const boost::property_tree::ptree &> object;
|
||||
|
||||
/// Reference used if the value is known to be a POD type
|
||||
boost::optional<std::string> value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting the Boost
|
||||
* property tree library.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PropertyTreeAdapter:
|
||||
public BasicAdapter<PropertyTreeAdapter,
|
||||
PropertyTreeArray,
|
||||
PropertyTreeObjectMember,
|
||||
PropertyTreeObject,
|
||||
PropertyTreeValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a PropertyTreeAdapter for an empty property tree
|
||||
PropertyTreeAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a PropertyTreeAdapter using a specific property tree
|
||||
PropertyTreeAdapter(const boost::property_tree::ptree &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* PropertyTreeAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PropertyTreeArray
|
||||
*/
|
||||
class PropertyTreeArrayValueIterator:
|
||||
public boost::iterator_facade<
|
||||
PropertyTreeArrayValueIterator, // name of derived type
|
||||
PropertyTreeAdapter, // value type
|
||||
boost::bidirectional_traversal_tag, // bi-directional iterator
|
||||
PropertyTreeAdapter> // type returned when dereferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new PropertyTreeArrayValueIterator using an existing
|
||||
* property tree iterator.
|
||||
*
|
||||
* @param itr property tree iterator to store
|
||||
*/
|
||||
PropertyTreeArrayValueIterator(
|
||||
const boost::property_tree::ptree::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a PropertyTreeAdapter that contains the value of the current
|
||||
/// element.
|
||||
PropertyTreeAdapter dereference() const
|
||||
{
|
||||
return PropertyTreeAdapter(itr->second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool equal(const PropertyTreeArrayValueIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
itr--;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
if (n > 0) {
|
||||
while (n-- > 0) {
|
||||
itr++;
|
||||
}
|
||||
} else {
|
||||
while (n++ < 0) {
|
||||
itr--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
boost::property_tree::ptree::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of PropertyTreeObjectMember representing one of the members of the object.
|
||||
* It has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PropertyTreeObject
|
||||
* @see PropertyTreeObjectMember
|
||||
*/
|
||||
class PropertyTreeObjectMemberIterator:
|
||||
public boost::iterator_facade<
|
||||
PropertyTreeObjectMemberIterator, // name of derived type
|
||||
PropertyTreeObjectMember, // value type
|
||||
boost::bidirectional_traversal_tag, // bi-directional iterator
|
||||
PropertyTreeObjectMember> // type returned when dereferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a PropertyTree iterator.
|
||||
*
|
||||
* @param itr PropertyTree iterator to store
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator(
|
||||
boost::property_tree::ptree::const_assoc_iterator itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a PropertyTreeObjectMember that contains the key and
|
||||
* value belonging to the object member identified by the iterator.
|
||||
*/
|
||||
PropertyTreeObjectMember dereference() const
|
||||
{
|
||||
return PropertyTreeObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool equal(const PropertyTreeObjectMemberIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
itr--;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
boost::property_tree::ptree::const_assoc_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for PropertyTreeAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::PropertyTreeAdapter>
|
||||
{
|
||||
typedef boost::property_tree::ptree DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "PropertyTreeAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool PropertyTreeFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return PropertyTreeAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline PropertyTreeArrayValueIterator PropertyTreeArray::begin() const
|
||||
{
|
||||
return array.begin();
|
||||
}
|
||||
|
||||
inline PropertyTreeArrayValueIterator PropertyTreeArray::end() const
|
||||
{
|
||||
return array.end();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::begin() const
|
||||
{
|
||||
return object.ordered_begin();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::end() const
|
||||
{
|
||||
return object.not_found();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
const boost::property_tree::ptree::const_assoc_iterator
|
||||
itr = object.find(propertyName);
|
||||
|
||||
if (itr != object.not_found()) {
|
||||
return itr;
|
||||
}
|
||||
|
||||
return object.not_found();
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
|
770
include/valijson/adapters/rapidjson_adapter.hpp
Normal file
770
include/valijson/adapters/rapidjson_adapter.hpp
Normal file
@ -0,0 +1,770 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the RapidJson parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for RapidJson.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - RapidJsonAdapter
|
||||
* - RapidJsonArray
|
||||
* - RapidJsonArrayValueIterator
|
||||
* - RapidJsonFrozenValue
|
||||
* - RapidJsonObject
|
||||
* - RapidJsonObjectMember
|
||||
* - RapidJsonObjectMemberIterator
|
||||
* - RapidJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is RapidJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#ifndef __VALIJSON_ADAPTERS_RAPIDJSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_RAPIDJSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class RapidJsonAdapter;
|
||||
class RapidJsonArrayValueIterator;
|
||||
class RapidJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, RapidJsonAdapter> RapidJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a RapidJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a RapidJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to an underlying
|
||||
* RapidJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class RapidJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef RapidJsonArrayValueIterator const_iterator;
|
||||
typedef RapidJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a RapidJsonArray referencing an empty array singleton.
|
||||
RapidJsonArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a RapidJsonArray referencing a specific RapidJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a RapidJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
RapidJsonArray(const rapidjson::Value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.IsArray()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator for the first element in the array.
|
||||
RapidJsonArrayValueIterator begin() const;
|
||||
|
||||
/// Return an iterator for one-past the last element of the array.
|
||||
RapidJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.Size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a RapidJson value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const rapidjson::Value & emptyArray()
|
||||
{
|
||||
static const rapidjson::Value array(rapidjson::kArrayType);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const rapidjson::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a RapidJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a RapidJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* RapidJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class RapidJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef RapidJsonObjectMemberIterator const_iterator;
|
||||
typedef RapidJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a RapidJsonObject referencing an empty object singleton.
|
||||
RapidJsonObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a RapidJsonObject referencing a specific RapidJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a RapidJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
RapidJsonObject(const rapidjson::Value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.IsObject()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the pointer value returned by the underlying RapidJson implementation.
|
||||
*/
|
||||
RapidJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the pointer value returned by the underlying RapidJson implementation.
|
||||
*/
|
||||
RapidJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param property property name to search for
|
||||
*/
|
||||
RapidJsonObjectMemberIterator find(const std::string &property) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.MemberEnd() - value.MemberBegin();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a RapidJson value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const rapidjson::Value & emptyObject()
|
||||
{
|
||||
static rapidjson::Value object(rapidjson::kObjectType);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const rapidjson::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a RapidJson value.
|
||||
*
|
||||
* This class allows a RapidJson value to be stored independent of its original
|
||||
* document. RapidJson makes this a bit harder than usual, because RapidJson
|
||||
* values are associated with a custom memory allocator. As such, RapidJson
|
||||
* values have to be copied recursively, referencing a custom allocator held
|
||||
* by this class.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class RapidJsonFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a RapidJson value
|
||||
*
|
||||
* @param source the RapidJson value to be copied
|
||||
*/
|
||||
RapidJsonFrozenValue(const rapidjson::Value &source)
|
||||
{
|
||||
if (!copy(source, value, allocator)) {
|
||||
throw std::runtime_error("Failed to copy rapidjson::Value");
|
||||
}
|
||||
}
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new RapidJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Recursively copy a RapidJson value using a separate allocator
|
||||
*
|
||||
* @param source value to copy from
|
||||
* @param dest value to copy into
|
||||
* @param allocator reference to an allocator held by this class
|
||||
*
|
||||
* @tparam Allocator type of RapidJson Allocator to be used
|
||||
*
|
||||
* @returns true if copied successfully, false otherwise.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
static bool copy(const rapidjson::Value &source,
|
||||
rapidjson::Value &dest,
|
||||
Allocator &allocator)
|
||||
{
|
||||
switch (source.GetType()) {
|
||||
case rapidjson::kNullType:
|
||||
dest.SetNull();
|
||||
return true;
|
||||
case rapidjson::kFalseType:
|
||||
dest.SetBool(false);
|
||||
return true;
|
||||
case rapidjson::kTrueType:
|
||||
dest.SetBool(true);
|
||||
return true;
|
||||
case rapidjson::kObjectType:
|
||||
dest.SetObject();
|
||||
for (rapidjson::Value::ConstMemberIterator itr = source.MemberBegin();
|
||||
itr != source.MemberEnd(); ++itr) {
|
||||
rapidjson::Value name(itr->name.GetString(), itr->name.GetStringLength(), allocator);
|
||||
rapidjson::Value value;
|
||||
copy(itr->value, value, allocator);
|
||||
dest.AddMember(name, value, allocator);
|
||||
}
|
||||
return true;
|
||||
case rapidjson::kArrayType:
|
||||
dest.SetArray();
|
||||
for (rapidjson::Value::ConstValueIterator itr = source.Begin(); itr != source.End(); ++itr) {
|
||||
rapidjson::Value value;
|
||||
copy(*itr, value, allocator);
|
||||
dest.PushBack(value, allocator);
|
||||
}
|
||||
return true;
|
||||
case rapidjson::kStringType:
|
||||
dest.SetString(source.GetString(), source.GetStringLength(), allocator);
|
||||
return true;
|
||||
case rapidjson::kNumberType:
|
||||
if (source.IsInt()) {
|
||||
dest.SetInt(source.GetInt());
|
||||
} else if (source.IsUint()) {
|
||||
dest.SetUint(source.GetUint());
|
||||
} else if (source.IsInt64()) {
|
||||
dest.SetInt64(source.GetInt64());
|
||||
} else if (source.IsUint64()) {
|
||||
dest.SetUint64(source.GetUint64());
|
||||
} else {
|
||||
dest.SetDouble(source.GetDouble());
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Local memory allocator for RapidJson value
|
||||
rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> allocator;
|
||||
|
||||
/// Local RapidJson value
|
||||
rapidjson::Value value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a RapidJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a RapidJson value. This class is responsible
|
||||
* for the mechanics of actually reading a RapidJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class RapidJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
RapidJsonValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific RapidJson value
|
||||
RapidJsonValue(const rapidjson::Value &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new RapidJsonFrozenValue instance that contains the
|
||||
* value referenced by this RapidJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new RapidJsonFrozenValue instance, belonging to
|
||||
* the caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new RapidJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a RapidJsonArray instance.
|
||||
*
|
||||
* If the referenced RapidJson value is an array, this function will return
|
||||
* a boost::optional containing a RapidJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return boost::none.
|
||||
*/
|
||||
boost::optional<RapidJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.IsArray()) {
|
||||
return boost::make_optional(RapidJsonArray(value));
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced RapidJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.IsArray()) {
|
||||
result = value.Size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.IsBool()) {
|
||||
result = value.GetBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.IsDouble()) {
|
||||
result = value.GetDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.IsInt()) {
|
||||
result = value.GetInt();
|
||||
return true;
|
||||
} else if (value.IsInt64()) {
|
||||
result = value.GetInt64();
|
||||
return true;
|
||||
} else if (value.IsUint()) {
|
||||
result = static_cast<int64_t>(value.GetUint());
|
||||
return true;
|
||||
} else if (value.IsUint64()) {
|
||||
result = static_cast<int64_t>(value.GetUint64());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a RapidJsonObject instance.
|
||||
*
|
||||
* If the referenced RapidJson value is an object, this function will return
|
||||
* a boost::optional containing a RapidJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return boost::none.
|
||||
*/
|
||||
boost::optional<RapidJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.IsObject()) {
|
||||
return boost::make_optional(RapidJsonObject(value));
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced RapidJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.IsObject()) {
|
||||
result = value.MemberEnd() - value.MemberBegin();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.IsString()) {
|
||||
result.assign(value.GetString(), value.GetStringLength());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.IsArray();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.IsBool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.IsDouble();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.IsInt() || value.IsInt64() || value.IsUint() ||
|
||||
value.IsUint64();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.IsNull();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.IsNumber();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.IsObject();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.IsString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const rapidjson::Value & emptyObject()
|
||||
{
|
||||
static const rapidjson::Value object(rapidjson::kObjectType);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained RapidJson value.
|
||||
const rapidjson::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting RapidJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class RapidJsonAdapter:
|
||||
public BasicAdapter<RapidJsonAdapter,
|
||||
RapidJsonArray,
|
||||
RapidJsonObjectMember,
|
||||
RapidJsonObject,
|
||||
RapidJsonValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a RapidJsonAdapter that contains an empty object
|
||||
RapidJsonAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a RapidJsonAdapter containing a specific RapidJson value
|
||||
RapidJsonAdapter(const rapidjson::Value &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* RapidJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see RapidJsonArray
|
||||
*/
|
||||
class RapidJsonArrayValueIterator:
|
||||
public boost::iterator_facade<
|
||||
RapidJsonArrayValueIterator, // name of derived type
|
||||
RapidJsonAdapter, // value type
|
||||
boost::bidirectional_traversal_tag, // bi-directional iterator
|
||||
RapidJsonAdapter> // type returned when dereferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new RapidJsonArrayValueIterator using an existing
|
||||
* RapidJson iterator.
|
||||
*
|
||||
* @param itr RapidJson iterator to store
|
||||
*/
|
||||
RapidJsonArrayValueIterator(
|
||||
const rapidjson::Value::ConstValueIterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a RapidJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
RapidJsonAdapter dereference() const
|
||||
{
|
||||
return RapidJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool equal(const RapidJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
itr--;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
std::ptrdiff_t difference(const RapidJsonArrayValueIterator &other)
|
||||
{
|
||||
return std::distance(itr, other.itr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
rapidjson::Value::ConstValueIterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of RapidJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see RapidJsonObject
|
||||
* @see RapidJsonObjectMember
|
||||
*/
|
||||
class RapidJsonObjectMemberIterator:
|
||||
public boost::iterator_facade<
|
||||
RapidJsonObjectMemberIterator, // name of derived type
|
||||
RapidJsonObjectMember, // value type
|
||||
boost::bidirectional_traversal_tag, // bi-directional iterator
|
||||
RapidJsonObjectMember> // type returned when dereferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a RapidJson iterator.
|
||||
*
|
||||
* @param itr RapidJson iterator to store
|
||||
*/
|
||||
RapidJsonObjectMemberIterator(
|
||||
const rapidjson::Value::ConstMemberIterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a RapidJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
RapidJsonObjectMember dereference() const
|
||||
{
|
||||
return RapidJsonObjectMember(
|
||||
std::string(itr->name.GetString(), itr->name.GetStringLength()),
|
||||
itr->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool equal(const RapidJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
itr--;
|
||||
}
|
||||
|
||||
std::ptrdiff_t difference(const RapidJsonObjectMemberIterator &other)
|
||||
{
|
||||
return std::distance(itr, other.itr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original RapidJson iterator
|
||||
rapidjson::Value::ConstMemberIterator itr;
|
||||
};
|
||||
|
||||
/// RapidJson specialisation of the AdapterTraits template struct.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::RapidJsonAdapter>
|
||||
{
|
||||
typedef rapidjson::Document DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "RapidJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool RapidJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return RapidJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline RapidJsonArrayValueIterator RapidJsonArray::begin() const
|
||||
{
|
||||
return value.Begin();
|
||||
}
|
||||
|
||||
inline RapidJsonArrayValueIterator RapidJsonArray::end() const
|
||||
{
|
||||
return value.End();
|
||||
}
|
||||
|
||||
inline RapidJsonObjectMemberIterator RapidJsonObject::begin() const
|
||||
{
|
||||
return value.MemberBegin();
|
||||
}
|
||||
|
||||
inline RapidJsonObjectMemberIterator RapidJsonObject::end() const
|
||||
{
|
||||
return value.MemberEnd();
|
||||
}
|
||||
|
||||
inline RapidJsonObjectMemberIterator RapidJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
const rapidjson::Value::ConstMemberIterator
|
||||
itr = value.FindMember(propertyName.c_str());
|
||||
|
||||
return itr ? itr : value.MemberEnd();
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
|
36
include/valijson/constraints/basic_constraint.hpp
Normal file
36
include/valijson/constraints/basic_constraint.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef __VALIJSON_CONSTRAINTS_BASIC_CONSTRAINT_HPP
|
||||
#define __VALIJSON_CONSTRAINTS_BASIC_CONSTRAINT_HPP
|
||||
|
||||
#include <valijson/constraints/constraint.hpp>
|
||||
#include <valijson/constraints/constraint_visitor.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace constraints {
|
||||
|
||||
/**
|
||||
* @brief Template class that implements the accept() and clone() functions of
|
||||
* the Constraint interface.
|
||||
*
|
||||
* @tparam ConstraintType name of the concrete constraint type, which must
|
||||
* provide a copy constructor.
|
||||
*/
|
||||
template<typename ConstraintType>
|
||||
struct BasicConstraint: Constraint
|
||||
{
|
||||
virtual ~BasicConstraint<ConstraintType>() { }
|
||||
|
||||
virtual bool accept(ConstraintVisitor &visitor) const
|
||||
{
|
||||
return visitor.visit(*static_cast<const ConstraintType*>(this));
|
||||
}
|
||||
|
||||
virtual Constraint * clone() const
|
||||
{
|
||||
return new ConstraintType(*static_cast<const ConstraintType*>(this));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace constraints
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
441
include/valijson/constraints/concrete_constraints.hpp
Normal file
441
include/valijson/constraints/concrete_constraints.hpp
Normal file
@ -0,0 +1,441 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Class definitions to support JSON Schema constraints
|
||||
*
|
||||
* This file contains class definitions for all of the constraints required to
|
||||
* support JSON Schema. These classes all inherit from the BasicConstraint
|
||||
* template class, which implements the common parts of the Constraint
|
||||
* interface.
|
||||
*
|
||||
* @see BasicConstraint
|
||||
* @see Constraint
|
||||
*/
|
||||
#ifndef __VALIJSON_CONSTRAINTS_CONCRETE_CONSTRAINTS_HPP
|
||||
#define __VALIJSON_CONSTRAINTS_CONCRETE_CONSTRAINTS_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <boost/ptr_container/ptr_map.hpp>
|
||||
#include <boost/ptr_container/ptr_vector.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
#include <valijson/constraints/basic_constraint.hpp>
|
||||
#include <valijson/schema.hpp>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
class Schema;
|
||||
|
||||
namespace constraints {
|
||||
|
||||
/**
|
||||
* @brief Represents an 'allOf' constraint.
|
||||
*
|
||||
* An allOf constraint provides a collection of sub-schemas that a value must
|
||||
* validate against. If a value fails to validate against any of these sub-
|
||||
* schemas, then validation fails.
|
||||
*/
|
||||
struct AllOfConstraint: BasicConstraint<AllOfConstraint>
|
||||
{
|
||||
typedef boost::ptr_vector<Schema> Schemas;
|
||||
|
||||
AllOfConstraint(const Schemas &schemas)
|
||||
: schemas(schemas) { }
|
||||
|
||||
/// Collection of schemas that must all be satisfied
|
||||
const Schemas schemas;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents an 'anyOf' constraint
|
||||
*
|
||||
* An anyOf constraint provides a collection of sub-schemas that a value can
|
||||
* validate against. If a value validates against one of these sub-schemas,
|
||||
* then the validation passes.
|
||||
*/
|
||||
struct AnyOfConstraint: BasicConstraint<AnyOfConstraint>
|
||||
{
|
||||
typedef boost::ptr_vector<Schema> Schemas;
|
||||
|
||||
AnyOfConstraint(const Schemas &schemas)
|
||||
: schemas(schemas) { }
|
||||
|
||||
/// Collection of schemas of which one must be satisfied
|
||||
const Schemas schemas;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'dependencies' constraint.
|
||||
*
|
||||
* A dependency constraint ensures that a given property is valid only if the
|
||||
* properties that it depends on are present.
|
||||
*/
|
||||
struct DependenciesConstraint: BasicConstraint<DependenciesConstraint>
|
||||
{
|
||||
// A mapping from property names to the set of names of their dependencies
|
||||
typedef std::set<std::string> Dependencies;
|
||||
typedef std::map<std::string, Dependencies> PropertyDependenciesMap;
|
||||
|
||||
// A mapping from property names to dependent schemas
|
||||
typedef boost::ptr_map<std::string, Schema> PropertyDependentSchemasMap;
|
||||
|
||||
DependenciesConstraint(const PropertyDependenciesMap &dependencies,
|
||||
const PropertyDependentSchemasMap &dependentSchemas)
|
||||
: dependencies(dependencies),
|
||||
dependentSchemas(dependentSchemas) { }
|
||||
|
||||
const PropertyDependenciesMap dependencies;
|
||||
const PropertyDependentSchemasMap dependentSchemas;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents an 'enum' constraint.
|
||||
*
|
||||
* An enum constraint provides a set of permissible values for a JSON node. The
|
||||
* node will only validate against this constraint if it matches one of the
|
||||
* values in the set.
|
||||
*/
|
||||
struct EnumConstraint: BasicConstraint<EnumConstraint>
|
||||
{
|
||||
typedef boost::ptr_vector<adapters::FrozenValue> Values;
|
||||
|
||||
EnumConstraint(const Values &values) // Copy each of the frozen values
|
||||
: values(values) { }
|
||||
|
||||
const Values values;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a pair of 'items' and 'additionalItems' constraints.
|
||||
*/
|
||||
struct ItemsConstraint: BasicConstraint<ItemsConstraint>
|
||||
{
|
||||
typedef boost::ptr_vector<Schema> Schemas;
|
||||
|
||||
/**
|
||||
* @brief Construct a singular item constraint that allows no additional
|
||||
* items
|
||||
*
|
||||
* @param itemSchema
|
||||
*/
|
||||
ItemsConstraint(const Schema &itemSchema)
|
||||
: itemSchema(new Schema(itemSchema)) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a singular item schema that allows additional items
|
||||
*
|
||||
* @param itemSchema
|
||||
* @param additionalItemsSchema
|
||||
*/
|
||||
ItemsConstraint(const Schema &itemSchema,
|
||||
const Schema &additionalItemsSchema)
|
||||
: itemSchema(new Schema(itemSchema)),
|
||||
additionalItemsSchema(new Schema(additionalItemsSchema)) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a plural items constraint that does not allow for
|
||||
* additional item schemas
|
||||
*
|
||||
* @param itemSchemas collection of item schemas
|
||||
*/
|
||||
ItemsConstraint(const Schemas &itemSchemas)
|
||||
: itemSchemas(new Schemas(itemSchemas)) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a plural items constraint that allows additional items
|
||||
*
|
||||
* @param itemSchemas
|
||||
* @param additionalItemsSchema
|
||||
*/
|
||||
ItemsConstraint(const Schemas &itemSchemas,
|
||||
const Schema &additionalItemsSchema)
|
||||
: itemSchemas(new Schemas(itemSchemas)),
|
||||
additionalItemsSchema(new Schema(additionalItemsSchema)) { }
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
ItemsConstraint(const ItemsConstraint &other)
|
||||
: itemSchema(other.itemSchema ? new Schema(*other.itemSchema.get()) : NULL),
|
||||
itemSchemas(other.itemSchemas ? new Schemas(*other.itemSchemas.get()) : NULL),
|
||||
additionalItemsSchema(other.additionalItemsSchema ? new Schema(*other.additionalItemsSchema.get()) : NULL) { }
|
||||
|
||||
const boost::scoped_ptr<const Schema> itemSchema;
|
||||
const boost::scoped_ptr<const Schemas> itemSchemas;
|
||||
const boost::scoped_ptr<const Schema> additionalItemsSchema;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'maximum' constraint.
|
||||
*/
|
||||
struct MaximumConstraint: BasicConstraint<MaximumConstraint>
|
||||
{
|
||||
MaximumConstraint(double maximum, bool exclusiveMaximum)
|
||||
: maximum(maximum),
|
||||
exclusiveMaximum(exclusiveMaximum) { }
|
||||
|
||||
const double maximum;
|
||||
const bool exclusiveMaximum;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'maxItems' constraint.
|
||||
*/
|
||||
struct MaxItemsConstraint: BasicConstraint<MaxItemsConstraint>
|
||||
{
|
||||
MaxItemsConstraint(int64_t maxItems)
|
||||
: maxItems(maxItems) { }
|
||||
|
||||
const int64_t maxItems;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'maxLength' constraint.
|
||||
*/
|
||||
struct MaxLengthConstraint: BasicConstraint<MaxLengthConstraint>
|
||||
{
|
||||
MaxLengthConstraint(int64_t maxLength)
|
||||
: maxLength(maxLength) { }
|
||||
|
||||
const int64_t maxLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'maxProperties' constraint.
|
||||
*/
|
||||
struct MaxPropertiesConstraint: BasicConstraint<MaxPropertiesConstraint>
|
||||
{
|
||||
MaxPropertiesConstraint(int64_t maxProperties)
|
||||
: maxProperties(maxProperties) { }
|
||||
|
||||
const int64_t maxProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a pair of 'minimum' and 'exclusiveMinimum' constraints.
|
||||
*/
|
||||
struct MinimumConstraint: BasicConstraint<MinimumConstraint>
|
||||
{
|
||||
MinimumConstraint(double minimum, bool exclusiveMinimum)
|
||||
: minimum(minimum),
|
||||
exclusiveMinimum(exclusiveMinimum) { }
|
||||
|
||||
const double minimum;
|
||||
const bool exclusiveMinimum;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'minItems' constraint.
|
||||
*/
|
||||
struct MinItemsConstraint: BasicConstraint<MinItemsConstraint>
|
||||
{
|
||||
MinItemsConstraint(int64_t minItems)
|
||||
: minItems(minItems) { }
|
||||
|
||||
const int64_t minItems;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'minLength' constraint.
|
||||
*/
|
||||
struct MinLengthConstraint: BasicConstraint<MinLengthConstraint>
|
||||
{
|
||||
MinLengthConstraint(int64_t minLength)
|
||||
: minLength(minLength) { }
|
||||
|
||||
const int64_t minLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'minProperties' constraint.
|
||||
*/
|
||||
struct MinPropertiesConstraint: BasicConstraint<MinPropertiesConstraint>
|
||||
{
|
||||
MinPropertiesConstraint(int64_t minProperties)
|
||||
: minProperties(minProperties) { }
|
||||
|
||||
const int64_t minProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'multipleOf' or 'divisibleBy' constraint.
|
||||
*/
|
||||
struct MultipleOfConstraint: BasicConstraint<MultipleOfConstraint>
|
||||
{
|
||||
MultipleOfConstraint(double multipleOf)
|
||||
: multipleOf(multipleOf),
|
||||
epsilon(std::numeric_limits<double>::epsilon()) { }
|
||||
|
||||
const double multipleOf;
|
||||
const double epsilon;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'not' constraint.
|
||||
*/
|
||||
struct NotConstraint: BasicConstraint<NotConstraint>
|
||||
{
|
||||
NotConstraint(const Schema &schema)
|
||||
: schema(new Schema(schema)) { }
|
||||
|
||||
NotConstraint(const NotConstraint &other)
|
||||
: schema(other.schema ? new Schema(*other.schema) : NULL) { }
|
||||
|
||||
const boost::scoped_ptr<const Schema> schema;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'oneOf' constraint.
|
||||
*/
|
||||
struct OneOfConstraint: BasicConstraint<OneOfConstraint>
|
||||
{
|
||||
typedef boost::ptr_vector<Schema> Schemas;
|
||||
|
||||
OneOfConstraint(const Schemas &schemas)
|
||||
: schemas(schemas) { }
|
||||
|
||||
/// Collection of schemas that must all be satisfied
|
||||
const Schemas schemas;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'pattern' constraint.
|
||||
*/
|
||||
struct PatternConstraint: BasicConstraint<PatternConstraint>
|
||||
{
|
||||
PatternConstraint(const std::string &pattern)
|
||||
: pattern(pattern) { }
|
||||
|
||||
const std::string pattern;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a tuple of 'properties', 'patternProperties' and
|
||||
* 'additionalProperties' constraints.
|
||||
*/
|
||||
struct PropertiesConstraint: BasicConstraint<PropertiesConstraint> {
|
||||
|
||||
typedef boost::ptr_map<std::string, Schema> PropertySchemaMap;
|
||||
|
||||
PropertiesConstraint(const PropertySchemaMap &properties,
|
||||
const PropertySchemaMap &patternProperties)
|
||||
: properties(properties),
|
||||
patternProperties(patternProperties) { }
|
||||
|
||||
PropertiesConstraint(const PropertySchemaMap &properties,
|
||||
const PropertySchemaMap &patternProperties,
|
||||
const Schema &additionalProperties)
|
||||
: properties(properties),
|
||||
patternProperties(patternProperties),
|
||||
additionalProperties(new Schema(additionalProperties)) { }
|
||||
|
||||
PropertiesConstraint(const PropertiesConstraint &other)
|
||||
: properties(other.properties),
|
||||
patternProperties(other.patternProperties),
|
||||
additionalProperties(other.additionalProperties ?
|
||||
new Schema(*other.additionalProperties.get()) : NULL) {}
|
||||
|
||||
const PropertySchemaMap properties;
|
||||
const PropertySchemaMap patternProperties;
|
||||
const boost::scoped_ptr<const Schema> additionalProperties;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'required' constraint.
|
||||
*/
|
||||
struct RequiredConstraint: BasicConstraint<RequiredConstraint>
|
||||
{
|
||||
typedef std::set<std::string> RequiredProperties;
|
||||
|
||||
RequiredConstraint(const RequiredProperties &requiredProperties)
|
||||
: requiredProperties(requiredProperties)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const RequiredProperties requiredProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'type' constraint.
|
||||
*/
|
||||
struct TypeConstraint: BasicConstraint<TypeConstraint>
|
||||
{
|
||||
enum JsonType {
|
||||
kAny,
|
||||
kArray,
|
||||
kBoolean,
|
||||
kInteger,
|
||||
kNull,
|
||||
kNumber,
|
||||
kObject,
|
||||
kString
|
||||
};
|
||||
|
||||
typedef std::set<JsonType> JsonTypes;
|
||||
|
||||
typedef boost::ptr_vector<Schema> Schemas;
|
||||
|
||||
TypeConstraint(const JsonType jsonType)
|
||||
: jsonTypes(makeJsonTypes(jsonType)) { }
|
||||
|
||||
TypeConstraint(const JsonTypes jsonTypes)
|
||||
: jsonTypes(jsonTypes) { }
|
||||
|
||||
TypeConstraint(const JsonTypes jsonTypes,
|
||||
const Schemas &schemas)
|
||||
: jsonTypes(jsonTypes),
|
||||
schemas(schemas) { }
|
||||
|
||||
static JsonTypes makeJsonTypes(const JsonType jsonType)
|
||||
{
|
||||
JsonTypes jsonTypes;
|
||||
jsonTypes.insert(jsonType);
|
||||
return jsonTypes;
|
||||
}
|
||||
|
||||
static JsonType jsonTypeFromString(const std::string &typeName)
|
||||
{
|
||||
if (typeName.compare("any") == 0) {
|
||||
return kAny;
|
||||
} else if (typeName.compare("array") == 0) {
|
||||
return kArray;
|
||||
} else if (typeName.compare("boolean") == 0) {
|
||||
return kBoolean;
|
||||
} else if (typeName.compare("integer") == 0) {
|
||||
return kInteger;
|
||||
} else if (typeName.compare("null") == 0) {
|
||||
return kNull;
|
||||
} else if (typeName.compare("number") == 0) {
|
||||
return kNumber;
|
||||
} else if (typeName.compare("object") == 0) {
|
||||
return kObject;
|
||||
} else if (typeName.compare("string") == 0) {
|
||||
return kString;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unrecognised JSON type name '" + typeName + "'");
|
||||
}
|
||||
|
||||
const JsonTypes jsonTypes;
|
||||
const Schemas schemas;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'uniqueItems' constraint.
|
||||
*/
|
||||
struct UniqueItemsConstraint: BasicConstraint<UniqueItemsConstraint>
|
||||
{
|
||||
// Don't need anything here.
|
||||
};
|
||||
|
||||
} // namespace constraints
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
52
include/valijson/constraints/constraint.hpp
Normal file
52
include/valijson/constraints/constraint.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef __VALIJSON_CONSTRAINTS_CONSTRAINT_HPP
|
||||
#define __VALIJSON_CONSTRAINTS_CONSTRAINT_HPP
|
||||
|
||||
namespace valijson {
|
||||
namespace constraints {
|
||||
|
||||
class ConstraintVisitor;
|
||||
|
||||
/**
|
||||
* @brief Interface that must be implemented by concrete constraint types.
|
||||
*
|
||||
* @todo Consider using something like the boost::cloneable concept here.
|
||||
*/
|
||||
struct Constraint {
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor.
|
||||
*/
|
||||
virtual ~Constraint() { }
|
||||
|
||||
/**
|
||||
* @brief Perform an action on the constraint using the visitor pattern.
|
||||
*
|
||||
* Note that Constraints cannot be modified by visitors.
|
||||
*
|
||||
* @param visitor Reference to a ConstraintVisitor object.
|
||||
*
|
||||
* @returns the boolean value returned by one of the visitor's visit
|
||||
* functions.
|
||||
*/
|
||||
virtual bool accept(ConstraintVisitor &visitor) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a constraint.
|
||||
*
|
||||
* Note that this should be a deep copy of the constraint.
|
||||
*
|
||||
* @returns an owning-pointer to the new constraint.
|
||||
*/
|
||||
virtual Constraint * clone() const = 0;
|
||||
|
||||
};
|
||||
|
||||
inline Constraint * new_clone(const Constraint &constraint)
|
||||
{
|
||||
return constraint.clone();
|
||||
}
|
||||
|
||||
} // namespace constraints
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
86
include/valijson/constraints/constraint_visitor.hpp
Normal file
86
include/valijson/constraints/constraint_visitor.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
#ifndef __VALIJSON_CONSTRAINTS_CONSTRAINT_VISITOR_HPP
|
||||
#define __VALIJSON_CONSTRAINTS_CONSTRAINT_VISITOR_HPP
|
||||
|
||||
namespace valijson {
|
||||
namespace constraints {
|
||||
|
||||
struct AllOfConstraint;
|
||||
struct AnyOfConstraint;
|
||||
struct DependenciesConstraint;
|
||||
struct EnumConstraint;
|
||||
struct ItemsConstraint;
|
||||
struct FormatConstraint;
|
||||
struct MaximumConstraint;
|
||||
struct MaxItemsConstraint;
|
||||
struct MaxLengthConstraint;
|
||||
struct MaxPropertiesConstraint;
|
||||
struct MinimumConstraint;
|
||||
struct MinItemsConstraint;
|
||||
struct MinLengthConstraint;
|
||||
struct MinPropertiesConstraint;
|
||||
struct MultipleOfConstraint;
|
||||
struct NotConstraint;
|
||||
struct OneOfConstraint;
|
||||
struct PatternConstraint;
|
||||
struct PropertiesConstraint;
|
||||
struct RequiredConstraint;
|
||||
struct TypeConstraint;
|
||||
struct UniqueItemsConstraint;
|
||||
|
||||
class ConstraintVisitor
|
||||
{
|
||||
protected:
|
||||
|
||||
// Shorten type names for derived classes outside of this namespace
|
||||
typedef constraints::AllOfConstraint AllOfConstraint;
|
||||
typedef constraints::AnyOfConstraint AnyOfConstraint;
|
||||
typedef constraints::DependenciesConstraint DependenciesConstraint;
|
||||
typedef constraints::EnumConstraint EnumConstraint;
|
||||
typedef constraints::ItemsConstraint ItemsConstraint;
|
||||
typedef constraints::MaximumConstraint MaximumConstraint;
|
||||
typedef constraints::MaxItemsConstraint MaxItemsConstraint;
|
||||
typedef constraints::MaxLengthConstraint MaxLengthConstraint;
|
||||
typedef constraints::MaxPropertiesConstraint MaxPropertiesConstraint;
|
||||
typedef constraints::MinimumConstraint MinimumConstraint;
|
||||
typedef constraints::MinItemsConstraint MinItemsConstraint;
|
||||
typedef constraints::MinLengthConstraint MinLengthConstraint;
|
||||
typedef constraints::MinPropertiesConstraint MinPropertiesConstraint;
|
||||
typedef constraints::MultipleOfConstraint MultipleOfConstraint;
|
||||
typedef constraints::NotConstraint NotConstraint;
|
||||
typedef constraints::OneOfConstraint OneOfConstraint;
|
||||
typedef constraints::PatternConstraint PatternConstraint;
|
||||
typedef constraints::PropertiesConstraint PropertiesConstraint;
|
||||
typedef constraints::RequiredConstraint RequiredConstraint;
|
||||
typedef constraints::TypeConstraint TypeConstraint;
|
||||
typedef constraints::UniqueItemsConstraint UniqueItemsConstraint;
|
||||
|
||||
public:
|
||||
|
||||
virtual bool visit(const AllOfConstraint &) = 0;
|
||||
virtual bool visit(const AnyOfConstraint &) = 0;
|
||||
virtual bool visit(const DependenciesConstraint &) = 0;
|
||||
virtual bool visit(const EnumConstraint &) = 0;
|
||||
virtual bool visit(const ItemsConstraint &) = 0;
|
||||
virtual bool visit(const MaximumConstraint &) = 0;
|
||||
virtual bool visit(const MaxItemsConstraint &) = 0;
|
||||
virtual bool visit(const MaxLengthConstraint &) = 0;
|
||||
virtual bool visit(const MaxPropertiesConstraint &) = 0;
|
||||
virtual bool visit(const MinimumConstraint &) = 0;
|
||||
virtual bool visit(const MinItemsConstraint &) = 0;
|
||||
virtual bool visit(const MinLengthConstraint &) = 0;
|
||||
virtual bool visit(const MinPropertiesConstraint &) = 0;
|
||||
virtual bool visit(const MultipleOfConstraint &) = 0;
|
||||
virtual bool visit(const NotConstraint &) = 0;
|
||||
virtual bool visit(const OneOfConstraint &) = 0;
|
||||
virtual bool visit(const PatternConstraint &) = 0;
|
||||
virtual bool visit(const PropertiesConstraint &) = 0;
|
||||
virtual bool visit(const RequiredConstraint &) = 0;
|
||||
virtual bool visit(const TypeConstraint &) = 0;
|
||||
virtual bool visit(const UniqueItemsConstraint &) = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace constraints
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
232
include/valijson/schema.hpp
Normal file
232
include/valijson/schema.hpp
Normal file
@ -0,0 +1,232 @@
|
||||
#ifndef __VALIJSON_SCHEMA_HPP
|
||||
#define __VALIJSON_SCHEMA_HPP
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/ptr_container/ptr_vector.hpp>
|
||||
|
||||
#include <valijson/constraints/constraint.hpp>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
/**
|
||||
* @brief Class that holds a list of Constraints that together form a schema.
|
||||
*
|
||||
* This class maintains an internal list of Constraint objects that define a
|
||||
* schema. It provides useful functionality such as the ability to easily make
|
||||
* independent copies of a schema.
|
||||
*
|
||||
* Schemas can be modified after construction by adding more constraints, or
|
||||
* by setting a schema title.
|
||||
*/
|
||||
class Schema
|
||||
{
|
||||
public:
|
||||
|
||||
/// Typedef the Constraint class into the local namespace for convenience
|
||||
typedef constraints::Constraint Constraint;
|
||||
|
||||
/// Typedef for a function that can be applied to each of the Constraint
|
||||
/// instances owned by a Schema.
|
||||
typedef boost::function<bool (const Constraint &)> ApplyFunction;
|
||||
|
||||
/**
|
||||
* @brief Construct a new Schema object with no constraints, using the
|
||||
* default scope.
|
||||
*
|
||||
* The constructed Schema object will have no constraints.
|
||||
*/
|
||||
Schema() { }
|
||||
|
||||
/**
|
||||
* @brief Construct a new Schema object with no constraints, and inherit
|
||||
* the scope of another Schema.
|
||||
*
|
||||
* The functions getScope(), getCanonicalURI() and getInheritedURI() use
|
||||
* the scope provided by this constructor, unless the URI for this schema
|
||||
* specifies its own scope.
|
||||
*
|
||||
* @param parentScope Scope to inherit.
|
||||
*/
|
||||
Schema(const std::string &parentScope)
|
||||
: parentScope(parentScope) {}
|
||||
|
||||
/**
|
||||
* @brief Construct a new Schema based an existing Schema object.
|
||||
*
|
||||
* The constructed Schema object will contain a copy of each constraint
|
||||
* defined in the referenced Schema.
|
||||
*
|
||||
* @param schema schema to copy constraints from
|
||||
*/
|
||||
Schema(const Schema &schema)
|
||||
: constraints(schema.constraints),
|
||||
parentScope(schema.parentScope),
|
||||
title(schema.title) { }
|
||||
|
||||
/**
|
||||
* @brief Add a constraint to the schema.
|
||||
*
|
||||
* A copy of the referenced Constraint object will be added to the Schema.
|
||||
*
|
||||
* @param constraint Reference to the constraint to copy.
|
||||
*/
|
||||
void addConstraint(const Constraint &constraint)
|
||||
{
|
||||
constraints.push_back(constraint.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a constraint to the schema.
|
||||
*
|
||||
* The schema will take ownership of the provided Constraint.
|
||||
*
|
||||
* @param constraint Pointer to the constraint to take ownership of.
|
||||
*/
|
||||
void addConstraint(Constraint *constraint)
|
||||
{
|
||||
constraints.push_back(constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invoke a function on each constraint in the schema.
|
||||
*
|
||||
* This function will apply the callback function to each constraint in
|
||||
* the schema, even if one of the invokations returns false. If a single
|
||||
* invokation returns false, this function will return false.
|
||||
*
|
||||
* @returns true if all invokations of the callback function are
|
||||
* successful, false otherwise.
|
||||
*/
|
||||
bool apply(ApplyFunction &applyFunction) const
|
||||
{
|
||||
bool allTrue = true;
|
||||
BOOST_FOREACH( const Constraint &constraint, constraints ) {
|
||||
allTrue = allTrue && applyFunction(constraint);
|
||||
}
|
||||
|
||||
return allTrue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invoke a function on each constraint in the schema.
|
||||
*
|
||||
* This is a stricter version of the apply() function that will return
|
||||
* immediately if any of the invokations return false.
|
||||
*
|
||||
* @returns true if all invokations of the callback function are
|
||||
* successful, false otherwise.
|
||||
*/
|
||||
bool applyStrict(ApplyFunction &applyFunction) const
|
||||
{
|
||||
BOOST_FOREACH( const Constraint &constraint, constraints ) {
|
||||
if (!applyFunction(constraint)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getId() const
|
||||
{
|
||||
if (id) {
|
||||
return *id;
|
||||
}
|
||||
|
||||
throw std::runtime_error("id has not been set");
|
||||
}
|
||||
|
||||
std::string getScope() const
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string getUri() const
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the title for this schema.
|
||||
*
|
||||
* If the title has not been set, this function will throw an exception.
|
||||
*
|
||||
* @throw std::runtime_error
|
||||
*
|
||||
* @return schema title string
|
||||
*/
|
||||
std::string getTitle() const
|
||||
{
|
||||
if (title) {
|
||||
return *title;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Schema does not have a title.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a boolean value that indicates whether the id has been
|
||||
* set or not.
|
||||
*
|
||||
* @return boolean value
|
||||
*/
|
||||
bool hasId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a boolean value that indicates whether the schema title
|
||||
* has been set or not.
|
||||
*
|
||||
* @return boolean value
|
||||
*/
|
||||
bool hasTitle() const
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
std::string resolveUri(const std::string &relative) const
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void setId(const std::string &id)
|
||||
{
|
||||
this->id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the title for this schema.
|
||||
*
|
||||
* The title is not used for validation, but can be used as part of the
|
||||
* validation error descriptions that are produced by the Validator and
|
||||
* ValidationVisitor classes.
|
||||
*
|
||||
* @param title new title
|
||||
*/
|
||||
void setTitle(const std::string &title)
|
||||
{
|
||||
this->title = title;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// List of pointers to constraints that apply to this schema.
|
||||
boost::ptr_vector<Constraint> constraints;
|
||||
|
||||
/// Id to apply when resolving the schema URI.
|
||||
boost::optional<std::string> id;
|
||||
|
||||
/// Scope inherited from a parent schema, or an empty string by default
|
||||
boost::optional<std::string> parentScope;
|
||||
|
||||
/// Title string associated with the schema (optional).
|
||||
boost::optional<std::string> title;
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
1083
include/valijson/schema_parser.hpp
Normal file
1083
include/valijson/schema_parser.hpp
Normal file
File diff suppressed because it is too large
Load Diff
41
include/valijson/utils/file_utils.hpp
Normal file
41
include/valijson/utils/file_utils.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef __VALIJSON_FILE_UTILS_HPP
|
||||
#define __VALIJSON_FILE_UTILS_HPP
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* Load a file into a string
|
||||
*
|
||||
* @param path path to the file to be loaded
|
||||
* @param dest string into which file should be loaded
|
||||
*
|
||||
* @return true if loaded, false otherwise
|
||||
*/
|
||||
inline bool loadFile(const std::string &path, std::string &dest)
|
||||
{
|
||||
// Open file for reading
|
||||
std::ifstream file(path.c_str());
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate space for file contents
|
||||
file.seekg(0, std::ios::end);
|
||||
dest.clear();
|
||||
dest.reserve(file.tellg());
|
||||
|
||||
// Assign file contents to destination string
|
||||
file.seekg(0, std::ios::beg);
|
||||
dest.assign(std::istreambuf_iterator<char>(file),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
34
include/valijson/utils/jsoncpp_utils.hpp
Normal file
34
include/valijson/utils/jsoncpp_utils.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef __VALIJSON_UTILS_JSONCPP_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_JSONCPP_UTILS_HPP
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, Json::Value &document)
|
||||
{
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
Json::Reader reader;
|
||||
bool parsingSuccessful = reader.parse(file, document);
|
||||
if (!parsingSuccessful) {
|
||||
std::cerr << "Jsoncpp parser failed to parse the document:" << std::endl
|
||||
<< reader.getFormatedErrorMessages();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
41
include/valijson/utils/property_tree_utils.hpp
Normal file
41
include/valijson/utils/property_tree_utils.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef __VALIJSON_UTILS_PROPERTY_TREE_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_PROPERTY_TREE_UTILS_HPP
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/detail/json_parser_error.hpp>
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, boost::property_tree::ptree &document)
|
||||
{
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::istringstream is(file);
|
||||
try {
|
||||
boost::property_tree::read_json(is, document);
|
||||
} catch (boost::property_tree::json_parser::json_parser_error &e) {
|
||||
std::cerr << "Boost Property Tree JSON parser failed to parse the document:" << std::endl;
|
||||
std::cerr << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
35
include/valijson/utils/rapidjson_utils.hpp
Normal file
35
include/valijson/utils/rapidjson_utils.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __VALIJSON_UTILS_RAPIDJSON_UTILS_HPP
|
||||
#define __VALIJSON_UTILS_RAPIDJSON_UTILS_HPP
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <valijson/utils/file_utils.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace utils {
|
||||
|
||||
inline bool loadDocument(const std::string &path, rapidjson::Document &document)
|
||||
{
|
||||
// Load schema JSON from file
|
||||
std::string file;
|
||||
if (!loadFile(path, file)) {
|
||||
std::cerr << "Failed to load json from file '" << path << "'." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse schema
|
||||
document.Parse<0>(file.c_str());
|
||||
if (document.HasParseError()) {
|
||||
std::cerr << "RapidJson failed to parse the document:" << std::endl;
|
||||
std::cerr << "Parse error: " << document.GetParseError() << std::endl;
|
||||
std::cerr << "Near: " << file.substr(std::max(size_t(0), document.GetErrorOffset() - 20), 40) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
108
include/valijson/validation_results.hpp
Normal file
108
include/valijson/validation_results.hpp
Normal file
@ -0,0 +1,108 @@
|
||||
#ifndef __VALIJSON_VALIDATION_RESULTS_HPP
|
||||
#define __VALIJSON_VALIDATION_RESULTS_HPP
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
/**
|
||||
* @brief Class that encapsulates the storage of validation errors.
|
||||
*
|
||||
* This class maintains an internal FIFO queue of errors that are reported
|
||||
* during validation. Errors are pushed on to the back of an internal
|
||||
* queue, and can retrieved by popping them from the front of the queue.
|
||||
*/
|
||||
class ValidationResults
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Describes a validation error.
|
||||
*
|
||||
* This struct is used to pass around the context and description of a
|
||||
* validation error.
|
||||
*/
|
||||
struct Error
|
||||
{
|
||||
/**
|
||||
* @brief Construct an Error object with no context or description.
|
||||
*/
|
||||
Error() { }
|
||||
|
||||
/**
|
||||
* @brief Construct an Error object using a context and description.
|
||||
*
|
||||
* @param context Context string to use
|
||||
* @param description Description string to use
|
||||
*/
|
||||
Error(const std::string &context, const std::string &description)
|
||||
: context(context),
|
||||
description(description) { }
|
||||
|
||||
/// Path to the node that failed validation.
|
||||
std::string context;
|
||||
|
||||
/// A detailed description of the validation error.
|
||||
std::string description;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return the number of errors in the queue.
|
||||
*/
|
||||
size_t numErrors() const
|
||||
{
|
||||
return errors.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy an Error and push it on to the back of the queue.
|
||||
*
|
||||
* @param error Reference to an Error object to be copied.
|
||||
*/
|
||||
void pushError(const Error &error)
|
||||
{
|
||||
errors.push_back(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Push an error onto the back of the queue.
|
||||
*
|
||||
* @param context Context of the validation error.
|
||||
* @param description Description of the validation error.
|
||||
*/
|
||||
void
|
||||
pushError(const std::string &context, const std::string &description)
|
||||
{
|
||||
errors.push_back(Error(context, description));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pop an error from the front of the queue.
|
||||
*
|
||||
* @param error Reference to an Error object to populate.
|
||||
*
|
||||
* @returns true if an Error was popped, false otherwise.
|
||||
*/
|
||||
bool
|
||||
popError(Error &error)
|
||||
{
|
||||
if (errors.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
error = errors.front();
|
||||
errors.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// FIFO queue of validation errors that have been reported
|
||||
std::deque<Error> errors;
|
||||
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif // __VALIJSON_VALIDATION_RESULTS_HPP
|
971
include/valijson/validation_visitor.hpp
Normal file
971
include/valijson/validation_visitor.hpp
Normal file
@ -0,0 +1,971 @@
|
||||
#ifndef __VALIJSON_VALIDATION_VISITOR_HPP
|
||||
#define __VALIJSON_VALIDATION_VISITOR_HPP
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <valijson/constraints/concrete_constraints.hpp>
|
||||
#include <valijson/constraints/constraint_visitor.hpp>
|
||||
#include <valijson/validation_results.hpp>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
class ValidationResults;
|
||||
|
||||
/**
|
||||
* @brief Implementation of the ConstraintVisitor interface that validates a
|
||||
* target document.
|
||||
*
|
||||
* @tparam AdapterType Adapter type for the target document.
|
||||
*/
|
||||
template<typename AdapterType>
|
||||
class ValidationVisitor: public constraints::ConstraintVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new validator for a given target value and context.
|
||||
*
|
||||
* @param target Target value to be validated
|
||||
* @param context Current context for validation error descriptions,
|
||||
* only used if results is set.
|
||||
* @param strictTypes Use strict type comparison
|
||||
* @param results Optional pointer to ValidationResults object, for
|
||||
* recording error descriptions. If this pointer is set
|
||||
* to NULL, validation errors will caused validation to
|
||||
* stop immediately.
|
||||
*/
|
||||
ValidationVisitor(const AdapterType &target,
|
||||
const std::string &context,
|
||||
const bool strictTypes,
|
||||
ValidationResults *results)
|
||||
: target(target),
|
||||
context(context),
|
||||
strictTypes(strictTypes),
|
||||
results(results) { }
|
||||
|
||||
/**
|
||||
* @brief Validate the target against a schema.
|
||||
*
|
||||
* When a ValidationResults object has been set via the 'results' member
|
||||
* variable, validation will proceed as long as no fatal errors occur,
|
||||
* with error descriptions added to the ValidationResults object.
|
||||
*
|
||||
* If a pointer to a ValidationResults instance is not provided, validation
|
||||
* will only continue for as long as the constraints are validated
|
||||
* successfully.
|
||||
*
|
||||
* @param schema Schema that the target must validate against
|
||||
*
|
||||
* @return true if validation passes, false otherwise
|
||||
*/
|
||||
bool validateSchema(const Schema &schema)
|
||||
{
|
||||
// Wrap the validationCallback() function below so that it will be
|
||||
// passed a reference to a constraint (_1), and a reference to the
|
||||
// visitor (*this).
|
||||
Schema::ApplyFunction fn(boost::bind(validationCallback, _1, *this));
|
||||
|
||||
// Perform validation against each constraint defined in the schema
|
||||
if (results == NULL) {
|
||||
// The applyStrict() function will return immediately if the
|
||||
// callback function returns false
|
||||
if (!schema.applyStrict(fn)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// The apply() function will iterate over all constraints in the
|
||||
// schema, even if the callback function returns false. Once
|
||||
// iteration is complete, the apply() function will return true
|
||||
// only if all invokations of the callback function returned true.
|
||||
if (!schema.apply(fn)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate the target against an allOf constraint.
|
||||
*
|
||||
* An allOf constraint provides a set of child schemas against which the
|
||||
* target must be validated in order for the constraint to the satifisfied.
|
||||
*
|
||||
* When a ValidationResults object has been set via the 'results' member
|
||||
* variable, validation will proceed as long as no fatal errors occur,
|
||||
* with error descriptions added to the ValidationResults object.
|
||||
*
|
||||
* If a pointer to a ValidationResults instance is not provided, validation
|
||||
* will only continue for as long as the child schemas are validated
|
||||
* successfully.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against
|
||||
*
|
||||
* @return true if validation passes, false otherwise
|
||||
*/
|
||||
virtual bool visit(const AllOfConstraint &constraint)
|
||||
{
|
||||
// Flag used to track validation status if errors are non-fatal
|
||||
bool validated = true;
|
||||
|
||||
// Validate against each child schema
|
||||
unsigned int index = 0;
|
||||
BOOST_FOREACH( const Schema &schema, constraint.schemas ) {
|
||||
|
||||
// Ensure that the target validates against child schema
|
||||
if (!validateSchema(schema)) {
|
||||
if (results) {
|
||||
validated = false;
|
||||
results->pushError(context,
|
||||
std::string("Failed to validate against child schema at index #") +
|
||||
boost::lexical_cast<std::string>(index) + " of allOf constraint.");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the anyOf constraint represented by an
|
||||
* AnyOfConstraint object.
|
||||
*
|
||||
* An anyOf constraint provides a set of child schemas, any of which the
|
||||
* target may be validated against in order for the constraint to the
|
||||
* satifisfied.
|
||||
*
|
||||
* Because an anyOf constraint does not require the target to validate
|
||||
* against all child schemas, if validation against a single schema fails,
|
||||
* the results will not be added to a ValidationResults object. Only if
|
||||
* validation fails for all child schemas will an error be added to the
|
||||
* ValidationResults object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if validation passes, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const AnyOfConstraint &constraint)
|
||||
{
|
||||
// Wrap the validationCallback() function below so that it will be
|
||||
// passed a reference to a constraint (_1), and a reference to the
|
||||
// visitor (*this).
|
||||
Schema::ApplyFunction fn(boost::bind(validationCallback, _1, *this));
|
||||
|
||||
BOOST_FOREACH( const Schema &schema, constraint.schemas ) {
|
||||
if (schema.apply(fn)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to validate against any child schemas.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the dependencies constraint represented by a
|
||||
* DependenciesConstraint object.
|
||||
*
|
||||
* A dependencies constraint can specify either a mapping of attribute names
|
||||
* to their dependencies, or a mapping of attribute names to child schemas
|
||||
* that must be satisfied if a given attribute is present.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if validation passes, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const DependenciesConstraint &constraint)
|
||||
{
|
||||
// Ignore non-objects
|
||||
if (!target.isObject()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Typedef and reference for conciseness in nested loops
|
||||
typedef DependenciesConstraint::PropertyDependenciesMap PDM;
|
||||
const PDM &deps = constraint.dependencies;
|
||||
|
||||
typedef DependenciesConstraint::PropertyDependentSchemasMap PDSM;
|
||||
const PDSM &depSchemas = constraint.dependentSchemas;
|
||||
|
||||
// Get access to the target as an object
|
||||
const typename AdapterType::Object object = target.getObject();
|
||||
|
||||
// Flag used to track validation status if errors are non-fatal
|
||||
bool validated = true;
|
||||
|
||||
// For each property in the object, check for a list of dependencies in
|
||||
// the constraint object and verify that the dependencies have been
|
||||
// satisfied.
|
||||
BOOST_FOREACH( const typename AdapterType::ObjectMember m, object ) {
|
||||
|
||||
// Check for this property in the dependency map. If it is not
|
||||
// present, we can move on to the next one...
|
||||
PDM::const_iterator itr = deps.find(m.first);
|
||||
if (itr != deps.end()) {
|
||||
BOOST_FOREACH( const std::string &name, itr->second ) {
|
||||
if (object.find(name) == object.end()) {
|
||||
if (!results) {
|
||||
return false;
|
||||
}
|
||||
validated = false;
|
||||
results->pushError(context, "Missing dependency '" + name + "'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for this property in the dependent schemas map. If it is
|
||||
// present then we need to validate the current target against the
|
||||
// dependent schema.
|
||||
PDSM::const_iterator depSchemasItr = depSchemas.find(m.first);
|
||||
if (depSchemasItr != depSchemas.end()) {
|
||||
const Schema *schema = depSchemasItr->second;
|
||||
if (!validateSchema(*schema)) {
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to validate against dependent schema.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the enum constraint represented by an
|
||||
* EnumConstraint object.
|
||||
*
|
||||
* Validation succeeds if the target is equal to one of the values provided
|
||||
* by the enum constraint.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if validation succeeds, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const EnumConstraint &constraint)
|
||||
{
|
||||
// Compare the target with each 'frozen' value in the enum constraint.
|
||||
BOOST_FOREACH( const adapters::FrozenValue &value, constraint.values ) {
|
||||
if (value.equalTo(target, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to match against any enum values.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the items and additionalItems constraints
|
||||
* represented by an ItemsConstraint object.
|
||||
*
|
||||
* An items constraint restricts the values in array to those that match a
|
||||
* given set of schemas. An item constraint can specify either an ordered
|
||||
* list of child schemas that will be used to validate the corresponding
|
||||
* value in the target array, or a single schema that will be used to
|
||||
* validate all items.
|
||||
*
|
||||
* If a list of child schemas is used, then the additionalItems constraint
|
||||
* will also be considered. If present, the schema derived from the
|
||||
* additionalItems constraint will be used to validate items that do not
|
||||
* have a corresponding child schema in the items constraint. If the
|
||||
* items constraint was not provided, then the additionalItems schema will
|
||||
* be used to validate all items in the array.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if validatation succeeds, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const ItemsConstraint &constraint)
|
||||
{
|
||||
// Ignore values that are not arrays
|
||||
if (!target.isArray()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validated = true;
|
||||
|
||||
if (constraint.itemSchema) {
|
||||
|
||||
// Validate all items against single schema
|
||||
unsigned int index = 0;
|
||||
BOOST_FOREACH( const AdapterType arrayItem, target.getArray() ) {
|
||||
ValidationVisitor<AdapterType> v(arrayItem,
|
||||
context + "[" + boost::lexical_cast<std::string>(index) + "]",
|
||||
strictTypes,
|
||||
results);
|
||||
if (!v.validateSchema(*constraint.itemSchema)) {
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to validate item #" + boost::lexical_cast<std::string>(index) + " in array.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
} else if (constraint.itemSchemas) {
|
||||
|
||||
if (!constraint.additionalItemsSchema) {
|
||||
// Check that the array length is <= length of the itemsSchema list
|
||||
if (target.getArray().size() > constraint.itemSchemas->size()) {
|
||||
if (results) {
|
||||
results->pushError(context, "Array contains more items than allowed by items constraint.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate items against the schema with the same index, or
|
||||
// additionalItems schema
|
||||
unsigned int index = 0;
|
||||
BOOST_FOREACH( const AdapterType arrayItem, target.getArray() ) {
|
||||
ValidationVisitor<AdapterType> v(arrayItem,
|
||||
context + "[" + boost::lexical_cast<std::string>(index) + "]",
|
||||
strictTypes,
|
||||
results);
|
||||
if (index >= constraint.itemSchemas->size()) {
|
||||
if (constraint.additionalItemsSchema) {
|
||||
if (!v.validateSchema(*constraint.additionalItemsSchema)) {
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to validate item #" +
|
||||
boost::lexical_cast<std::string>(index) + " against additional items schema.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
results->pushError(context, "Cannot validate item #" +
|
||||
boost::lexical_cast<std::string>(index) + " in array due to missing schema.");
|
||||
validated = false;
|
||||
}
|
||||
} else if (!v.validateSchema(constraint.itemSchemas->at(index))) {
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to validate item #" +
|
||||
boost::lexical_cast<std::string>(index) + " against corresponding item schema.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
|
||||
} else if (constraint.additionalItemsSchema) {
|
||||
|
||||
// Validate each item against additional items schema
|
||||
BOOST_FOREACH( const AdapterType arrayItem, target.getArray() ) {
|
||||
ValidationVisitor<AdapterType> v(arrayItem,
|
||||
context + "[" + boost::lexical_cast<std::string>(index) + "]",
|
||||
strictTypes,
|
||||
results);
|
||||
if (!v.validateSchema(*constraint.additionalItemsSchema)) {
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to validate item #" +
|
||||
boost::lexical_cast<std::string>(index) + " against additional items schema.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the maximum and exclusiveMaximum constraints
|
||||
* represented by a MaximumConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if constraints are satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MaximumConstraint &constraint)
|
||||
{
|
||||
if (!target.isNumber()) {
|
||||
// Ignore values that are not numbers
|
||||
return true;
|
||||
}
|
||||
|
||||
if (constraint.exclusiveMaximum) {
|
||||
if (target.getNumber() >= constraint.maximum) {
|
||||
if (results) {
|
||||
results->pushError(context,
|
||||
"Expected number less than " + boost::lexical_cast<std::string>(constraint.maximum));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (target.getNumber() > constraint.maximum) {
|
||||
if (results) {
|
||||
results->pushError(context,
|
||||
"Expected number less than or equal to" +
|
||||
boost::lexical_cast<std::string>(constraint.maximum));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the maxItems constraint represented by a
|
||||
* MaxItemsConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MaxItemsConstraint &constraint)
|
||||
{
|
||||
if (target.isArray() &&
|
||||
target.getArray().size() > constraint.maxItems) {
|
||||
if (results) {
|
||||
results->pushError(context, "Array should contain no more than " +
|
||||
boost::lexical_cast<std::string>(constraint.maxItems) +
|
||||
" elements.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the maxLength constraint represented by a
|
||||
* MaxLengthConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MaxLengthConstraint &constraint)
|
||||
{
|
||||
if (target.isString() &&
|
||||
target.getString().size() > constraint.maxLength) {
|
||||
if (results) {
|
||||
results->pushError(context, "String should be no more than " +
|
||||
boost::lexical_cast<std::string>(constraint.maxLength) +
|
||||
" characters in length.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the maxProperties constraint represented by a
|
||||
* MaxPropertiesConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MaxPropertiesConstraint &constraint)
|
||||
{
|
||||
if (target.isObject() &&
|
||||
target.getObject().size() > constraint.maxProperties) {
|
||||
if (results) {
|
||||
results->pushError(context, "Object should have no more than" +
|
||||
boost::lexical_cast<std::string>(constraint.maxProperties) +
|
||||
" properties.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the minimum constraint represented by a
|
||||
* MinimumConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MinimumConstraint &constraint)
|
||||
{
|
||||
if (!target.isNumber()) {
|
||||
// Ignore values that are not numbers
|
||||
return true;
|
||||
}
|
||||
|
||||
if (constraint.exclusiveMinimum) {
|
||||
if (target.getNumber() <= constraint.minimum) {
|
||||
if (results) {
|
||||
results->pushError(context,
|
||||
"Expected number greater than " +
|
||||
boost::lexical_cast<std::string>(constraint.minimum));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (target.getNumber() < constraint.minimum) {
|
||||
if (results) {
|
||||
results->pushError(context,
|
||||
"Expected number greater than or equal to" +
|
||||
boost::lexical_cast<std::string>(constraint.minimum));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the minItems constraint represented by a
|
||||
* MinItemsConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MinItemsConstraint &constraint)
|
||||
{
|
||||
if (target.isArray() &&
|
||||
target.getArray().size() < constraint.minItems) {
|
||||
if (results) {
|
||||
results->pushError(context, "Array should contain no fewer than " +
|
||||
boost::lexical_cast<std::string>(constraint.minItems) +
|
||||
" elements.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the minLength constraint represented by a
|
||||
* MinLengthConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MinLengthConstraint &constraint)
|
||||
{
|
||||
if (target.isString() &&
|
||||
target.getString().size() < constraint.minLength) {
|
||||
if (results) {
|
||||
results->pushError(context, "String should be no fewer than " +
|
||||
boost::lexical_cast<std::string>(constraint.minLength) +
|
||||
" characters in length.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the minProperties constraint represented by a
|
||||
* MinPropertiesConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MinPropertiesConstraint &constraint)
|
||||
{
|
||||
if (target.isObject() &&
|
||||
target.getObject().size() < constraint.minProperties) {
|
||||
if (results) {
|
||||
results->pushError(context, "Object should have no fewer than" +
|
||||
boost::lexical_cast<std::string>(constraint.minProperties) +
|
||||
" properties.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the multipleOf or divisibleBy constraints
|
||||
* represented by a MultipleOfConstraint object.
|
||||
*
|
||||
* @todo Not implemented.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const MultipleOfConstraint &constraint)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the not constraint represented by a
|
||||
* NotConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const NotConstraint &constraint)
|
||||
{
|
||||
ValidationVisitor<AdapterType> v(target, context, strictTypes, NULL);
|
||||
|
||||
if (v.validateSchema(*constraint.schema)) {
|
||||
if (results) {
|
||||
results->pushError(context, "Target should not validate against schema specified in 'not' constraint.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the oneOf constraint represented by a
|
||||
* OneOfConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const OneOfConstraint &constraint)
|
||||
{
|
||||
unsigned int numValidated = 0;
|
||||
|
||||
BOOST_FOREACH( const Schema &schema, constraint.schemas ) {
|
||||
if (validateSchema(schema)) {
|
||||
numValidated++;
|
||||
}
|
||||
}
|
||||
|
||||
if (numValidated != 1) {
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to validate against exactly one child schema.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the pattern constraint represented by a
|
||||
* PatternConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const PatternConstraint &constraint)
|
||||
{
|
||||
if (!target.isString()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const boost::regex r(constraint.pattern, boost::regex::perl);
|
||||
if (!boost::regex_search(target.getString(), r)) {
|
||||
if (results) {
|
||||
results->pushError(context, "Failed to match regex specified by 'pattern' constraint.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the properties, patternProperties, and
|
||||
* additionalProperties constraints represented by a
|
||||
* PatternConstraint object.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against.
|
||||
*
|
||||
* @return true if the constraint is satisfied, false otherwise.
|
||||
*/
|
||||
virtual bool visit(const PropertiesConstraint &constraint)
|
||||
{
|
||||
if (!target.isObject()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validated = true;
|
||||
|
||||
// Validate each property in the target object
|
||||
BOOST_FOREACH( const typename AdapterType::ObjectMember m, target.getObject() ) {
|
||||
|
||||
const std::string propertyName = m.first;
|
||||
bool propertyNameMatched = false;
|
||||
|
||||
ValidationVisitor<AdapterType> v(m.second, context + "." + m.first, strictTypes, results);
|
||||
|
||||
// Search for matching property name
|
||||
PropertiesConstraint::PropertySchemaMap::const_iterator itr =
|
||||
constraint.properties.find(propertyName);
|
||||
if (itr != constraint.properties.end()) {
|
||||
propertyNameMatched = true;
|
||||
if (!v.validateSchema(*itr->second)) {
|
||||
if (results) {
|
||||
results->pushError(context,
|
||||
"Failed to validate against 'properties' schema associated with property name '" +
|
||||
propertyName + "'.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search for a regex that matches the property name
|
||||
for (itr = constraint.patternProperties.begin(); itr != constraint.patternProperties.end(); ++itr) {
|
||||
const boost::regex r(itr->first, boost::regex::perl);
|
||||
if (boost::regex_search(propertyName, r)) {
|
||||
propertyNameMatched = true;
|
||||
// Check schema
|
||||
if (!v.validateSchema(*itr->second)) {
|
||||
if (results) {
|
||||
results->pushError(context,
|
||||
"Failed to validate against 'patternProperties' schema associated with regex '" +
|
||||
itr->first + "'.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the property name has been matched by a name in 'properties'
|
||||
// or a regex in 'patternProperties', then it should not be
|
||||
// validated against the 'additionalPatterns' schema.
|
||||
if (propertyNameMatched) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If an additionalProperties schema has been provided, the values
|
||||
// associated with unmatched property names should be validated
|
||||
// against that schema.
|
||||
if (constraint.additionalProperties) {
|
||||
if (v.validateSchema(*constraint.additionalProperties)) {
|
||||
continue;
|
||||
} else if (results) {
|
||||
results->pushError(context, "Failed to validate property '" +
|
||||
propertyName + "' against additionalProperties schema.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (results) {
|
||||
results->pushError(context, "Failed to match property name to any names in 'properties' or regexes in 'patternProperties'");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the required constraint represented by a
|
||||
* RequiredConstraint object.
|
||||
*
|
||||
* A required constraint specifies a list of properties that must be present
|
||||
* in the target.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against
|
||||
*
|
||||
* @return true if validation succeeds, false otherwise
|
||||
*/
|
||||
virtual bool visit(const RequiredConstraint &constraint)
|
||||
{
|
||||
if (!target.isObject()) {
|
||||
if (results) {
|
||||
results->pushError(context, "Object required to validate 'required' properties.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool validated = true;
|
||||
const typename AdapterType::Object object = target.getObject();
|
||||
BOOST_FOREACH( const std::string &requiredProperty, constraint.requiredProperties ) {
|
||||
if (object.find(requiredProperty) == object.end()) {
|
||||
if (results) {
|
||||
results->pushError(context, "Missing required property '" + requiredProperty + "'.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate against the type constraint represented by a
|
||||
* TypeConstraint object.
|
||||
*
|
||||
* Checks that the target is of the expected type.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against
|
||||
*
|
||||
* @return true if validation succeeds, false otherwise
|
||||
*/
|
||||
virtual bool visit(const TypeConstraint &constraint)
|
||||
{
|
||||
// Try to match the type to one of the types in the jsonTypes array
|
||||
BOOST_FOREACH( const TypeConstraint::JsonType jsonType, constraint.jsonTypes ) {
|
||||
switch (jsonType) {
|
||||
case TypeConstraint::kAny:
|
||||
return true;
|
||||
case TypeConstraint::kArray:
|
||||
if (target.isArray()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeConstraint::kBoolean:
|
||||
if (target.isBool() || (!strictTypes && target.maybeBool())) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeConstraint::kInteger:
|
||||
if (target.isInteger() || (!strictTypes && target.maybeInteger())) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeConstraint::kNull:
|
||||
if (target.isNull() || (!strictTypes && target.maybeNull())) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeConstraint::kNumber:
|
||||
if (target.isNumber() || (!strictTypes && target.maybeDouble())) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeConstraint::kObject:
|
||||
if (target.isObject()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeConstraint::kString:
|
||||
if (target.isString()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH( const Schema &schema, constraint.schemas ) {
|
||||
if (validateSchema(schema)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (results) {
|
||||
results->pushError(context, "Value type not permitted by 'type' constraint.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate the uniqueItems constraint represented by a
|
||||
* UniqueItems object.
|
||||
*
|
||||
* A uniqueItems constraint requires that each of the values in an array
|
||||
* are unique. Comparison is performed recursively.
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against
|
||||
*
|
||||
* @return true if validation succeeds, false otherwise
|
||||
*/
|
||||
virtual bool visit(const UniqueItemsConstraint &constraint)
|
||||
{
|
||||
if (!target.isArray()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validated = true;
|
||||
|
||||
const typename AdapterType::Array targetArray = target.getArray();
|
||||
const typename AdapterType::Array::const_iterator end = targetArray.end();
|
||||
const typename AdapterType::Array::const_iterator secondLast = end - 1;
|
||||
unsigned int outerIndex = 0;
|
||||
for (typename AdapterType::Array::const_iterator outerItr = targetArray.begin(); outerItr != secondLast; ++outerItr) {
|
||||
unsigned int innerIndex = 0;
|
||||
for (typename AdapterType::Array::const_iterator innerItr = outerItr + 1; innerItr != end; ++innerItr) {
|
||||
if (outerItr->equalTo(*innerItr, true)) {
|
||||
if (results) {
|
||||
results->pushError(context, "Elements at indexes #" +
|
||||
boost::lexical_cast<std::string>(outerIndex) + " and #" +
|
||||
boost::lexical_cast<std::string>(innerIndex) + " violate uniqueness constraint.");
|
||||
validated = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++innerIndex;
|
||||
}
|
||||
++outerIndex;
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Callback function that passes a visitor to a constraint.
|
||||
*
|
||||
* @param constraint Reference to constraint to be visited
|
||||
* @param visitor Reference to visitor to be applied
|
||||
*
|
||||
* @return true if the visitor returns successfully, false otherwise.
|
||||
*/
|
||||
static bool validationCallback(const constraints::Constraint &constraint,
|
||||
ValidationVisitor<AdapterType> &visitor)
|
||||
{
|
||||
return constraint.accept(visitor);
|
||||
}
|
||||
|
||||
/// Reference to the JSON value being validated
|
||||
const AdapterType ⌖
|
||||
|
||||
/// String describing the current object context
|
||||
const std::string context;
|
||||
|
||||
/// Optional pointer to a ValidationResults object to be populated
|
||||
ValidationResults *results;
|
||||
|
||||
/// Option to use strict type comparison
|
||||
const bool strictTypes;
|
||||
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
89
include/valijson/validator.hpp
Normal file
89
include/valijson/validator.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef __VALIJSON_VALIDATOR_HPP
|
||||
#define __VALIJSON_VALIDATOR_HPP
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include <valijson/schema.hpp>
|
||||
#include <valijson/validation_visitor.hpp>
|
||||
|
||||
namespace valijson {
|
||||
|
||||
class Schema;
|
||||
class ValidationResults;
|
||||
|
||||
/**
|
||||
* @brief Class that wraps a schema and provides validation functionality.
|
||||
*
|
||||
* This class wraps a Schema object, and encapsulates the logic required to
|
||||
* validate rapidjson values aginst the schema.
|
||||
*/
|
||||
class Validator {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a validator using the specified schema.
|
||||
*
|
||||
* The schema that is provided will be copied.
|
||||
*
|
||||
* @param schema A schema to use for validation
|
||||
*/
|
||||
Validator(const Schema &schema)
|
||||
: schema(new Schema(schema)),
|
||||
strictTypes(true) { }
|
||||
|
||||
/**
|
||||
* @brief Set flag to use strict comparison during validation.
|
||||
*
|
||||
* The default value is true, but this can be changed at any time. Future
|
||||
* invokations of validate() will make use of the new value.
|
||||
*
|
||||
* @param strictTypes whether or not to use strict comparison
|
||||
*/
|
||||
void setStrict(bool strictTypes)
|
||||
{
|
||||
this->strictTypes = strictTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate a JSON document and optionally return the results.
|
||||
*
|
||||
* When a ValidationResults object is provided via the \c results parameter,
|
||||
* validation will be performed against each constraint defined by the
|
||||
* schema, even if validation fails for some or all constraints.
|
||||
*
|
||||
* If a pointer to a ValidationResults instance is not provided, validation
|
||||
* will only continue for as long as the constraints are validated
|
||||
* successfully.
|
||||
*
|
||||
* @param target A rapidjson::Value to be validated.
|
||||
*
|
||||
* @param results An optional pointer to a ValidationResults instance that
|
||||
* will be used to report validation errors.
|
||||
*
|
||||
* @returns true if validation succeeds, false otherwise.
|
||||
*/
|
||||
template<typename AdapterType>
|
||||
bool validate(const AdapterType &target, ValidationResults *results)
|
||||
{
|
||||
// Construct a ValidationVisitor to perform validation at the root level
|
||||
ValidationVisitor<AdapterType> v(target, std::string(),
|
||||
strictTypes, results);
|
||||
|
||||
return v.validateSchema(*schema);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Pointer to an internal copy of a schema to use for validation
|
||||
boost::scoped_ptr<const Schema> schema;
|
||||
|
||||
/// Flag indicating that strict type comparisons should be used
|
||||
bool strictTypes;
|
||||
|
||||
};
|
||||
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
1
tests/data/documents/array_doubles_10_20_30_40.json
Normal file
1
tests/data/documents/array_doubles_10_20_30_40.json
Normal file
@ -0,0 +1 @@
|
||||
[10.0, 20.0, 30.0, 40.0]
|
1
tests/data/documents/array_doubles_1_2_3.json
Normal file
1
tests/data/documents/array_doubles_1_2_3.json
Normal file
@ -0,0 +1 @@
|
||||
[1.0, 2.0, 3.0]
|
1
tests/data/documents/array_doubles_1_2_3_4.json
Normal file
1
tests/data/documents/array_doubles_1_2_3_4.json
Normal file
@ -0,0 +1 @@
|
||||
[1.0, 2.0, 3.0, 4.0]
|
1
tests/data/documents/array_empty.json
Normal file
1
tests/data/documents/array_empty.json
Normal file
@ -0,0 +1 @@
|
||||
[]
|
1
tests/data/documents/array_integers_10_20_30_40.json
Normal file
1
tests/data/documents/array_integers_10_20_30_40.json
Normal file
@ -0,0 +1 @@
|
||||
[10, 20, 30, 40]
|
1
tests/data/documents/array_integers_1_2_3.json
Normal file
1
tests/data/documents/array_integers_1_2_3.json
Normal file
@ -0,0 +1 @@
|
||||
[1, 2, 3]
|
1
tests/data/documents/array_integers_1_2_3_4.json
Normal file
1
tests/data/documents/array_integers_1_2_3_4.json
Normal file
@ -0,0 +1 @@
|
||||
[1, 2, 3, 4]
|
1
tests/data/documents/array_strings_10_20_30_40.json
Normal file
1
tests/data/documents/array_strings_10_20_30_40.json
Normal file
@ -0,0 +1 @@
|
||||
["10", "20", "30", "40"]
|
1
tests/data/documents/array_strings_1_2_3.json
Normal file
1
tests/data/documents/array_strings_1_2_3.json
Normal file
@ -0,0 +1 @@
|
||||
["1", "2", "3"]
|
1
tests/data/documents/array_strings_1_2_3_4.json
Normal file
1
tests/data/documents/array_strings_1_2_3_4.json
Normal file
@ -0,0 +1 @@
|
||||
["1", "2", "3", "4"]
|
1
tests/data/documents/object_empty.json
Normal file
1
tests/data/documents/object_empty.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
20
tests/data/schemas/allof_integers_and_numbers.schema.json
Normal file
20
tests/data/schemas/allof_integers_and_numbers.schema.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"comment": "Document must contain an array of integers, all unique.",
|
||||
"allOf": [
|
||||
{
|
||||
"items": {
|
||||
"type": "integer"
|
||||
},
|
||||
"additionalItems": false,
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"type": "number"
|
||||
},
|
||||
"additionalItems": false,
|
||||
"type": "array",
|
||||
"uniqueItems": true
|
||||
}
|
||||
]
|
||||
}
|
157
tests/test_adapter_comparison.cpp
Normal file
157
tests/test_adapter_comparison.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <valijson/adapters/jsoncpp_adapter.hpp>
|
||||
#include <valijson/adapters/property_tree_adapter.hpp>
|
||||
#include <valijson/adapters/rapidjson_adapter.hpp>
|
||||
#include <valijson/utils/jsoncpp_utils.hpp>
|
||||
#include <valijson/utils/property_tree_utils.hpp>
|
||||
#include <valijson/utils/rapidjson_utils.hpp>
|
||||
|
||||
#define TEST_DATA_DIR "../tests/data/documents/"
|
||||
|
||||
using valijson::adapters::AdapterTraits;
|
||||
|
||||
class TestAdapterComparison : public testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
struct JsonFile
|
||||
{
|
||||
JsonFile(const std::string &path, int strictGroup, int looseGroup)
|
||||
: path(path),
|
||||
strictGroup(strictGroup),
|
||||
looseGroup(looseGroup) { }
|
||||
|
||||
const std::string path;
|
||||
|
||||
int strictGroup;
|
||||
int looseGroup;
|
||||
};
|
||||
|
||||
static void SetUpTestCase() {
|
||||
|
||||
const std::string testDataDir(TEST_DATA_DIR);
|
||||
|
||||
//
|
||||
// Each test is allocated to two groups. The first group is the strict
|
||||
// comparison group. All test files that have been assigned to the same
|
||||
// group should be equal, when compared using strict types. The second
|
||||
// group is the loose comparison group. All tests files in a loose
|
||||
// group should be equal, when compared without using strict types.
|
||||
//
|
||||
// As an example, the first three test files are in the same loose
|
||||
// group. This means they are expected to be equal when compared without
|
||||
// strict types. However, only the first two files in the same strict
|
||||
// group, which means that only they should be equal.
|
||||
//
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_doubles_1_2_3.json", 1, 1));
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_integers_1_2_3.json", 1, 1));
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_strings_1_2_3.json", 2, 1));
|
||||
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_doubles_1_2_3_4.json", 3, 2));
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_integers_1_2_3_4.json", 3, 2));
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_strings_1_2_3_4.json", 4, 2));
|
||||
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_doubles_10_20_30_40.json", 5, 3));
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_integers_10_20_30_40.json", 5, 3));
|
||||
jsonFiles.push_back(JsonFile(testDataDir + "array_strings_10_20_30_40.json", 6, 3));
|
||||
}
|
||||
|
||||
template<typename Adapter1, typename Adapter2>
|
||||
static void testComparison()
|
||||
{
|
||||
std::vector<JsonFile>::const_iterator outerItr, innerItr;
|
||||
|
||||
for(outerItr = jsonFiles.begin(); outerItr != jsonFiles.end() - 1; ++outerItr) {
|
||||
for(innerItr = outerItr; innerItr != jsonFiles.end(); ++innerItr) {
|
||||
|
||||
const bool expectedStrict = (outerItr->strictGroup == innerItr->strictGroup);
|
||||
const bool expectedLoose = (outerItr->looseGroup == innerItr->looseGroup);
|
||||
|
||||
typename AdapterTraits<Adapter1>::DocumentType document1;
|
||||
ASSERT_TRUE( valijson::utils::loadDocument(outerItr->path, document1) );
|
||||
const Adapter1 adapter1(document1);
|
||||
const std::string adapter1Name = AdapterTraits<Adapter1>::adapterName();
|
||||
|
||||
typename AdapterTraits<Adapter2>::DocumentType document2;
|
||||
ASSERT_TRUE( valijson::utils::loadDocument(innerItr->path, document2) );
|
||||
const Adapter2 adapter2(document2);
|
||||
const std::string adapter2Name = AdapterTraits<Adapter2>::adapterName();
|
||||
|
||||
// If either adapter does not support strict types, then strict
|
||||
// comparison should not be used, UNLESS the adapters are of the
|
||||
// same type. If they are of the same type, then the internal
|
||||
// type degradation should be the same, therefore strict testing
|
||||
// of equality makes sense.
|
||||
if (adapter1.hasStrictTypes() && adapter2.hasStrictTypes() && adapter1Name == adapter2Name) {
|
||||
EXPECT_EQ(expectedStrict, adapter1.equalTo(adapter2, true))
|
||||
<< "Comparing '" << outerItr->path << "' to '"
|
||||
<< innerItr->path << "' "
|
||||
<< "with strict comparison enabled";
|
||||
EXPECT_EQ(expectedStrict, adapter2.equalTo(adapter1, true))
|
||||
<< "Comparing '" << innerItr->path << "' to '"
|
||||
<< outerItr->path << "' "
|
||||
<< "with strict comparison enabled";
|
||||
}
|
||||
|
||||
EXPECT_EQ(expectedLoose, adapter1.equalTo(adapter2, false))
|
||||
<< "Comparing '" << outerItr->path << "' to '"
|
||||
<< innerItr->path << "' "
|
||||
<< "with strict comparison disabled";
|
||||
EXPECT_EQ(expectedLoose, adapter2.equalTo(adapter1, false))
|
||||
<< "Comparing '" << innerItr->path << "' to '"
|
||||
<< outerItr->path << "' "
|
||||
<< "with strict comparison disabled";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<JsonFile> jsonFiles;
|
||||
};
|
||||
|
||||
std::vector<TestAdapterComparison::JsonFile> TestAdapterComparison::jsonFiles;
|
||||
|
||||
TEST_F(TestAdapterComparison, JsonCppVsJsonCpp)
|
||||
{
|
||||
testComparison<
|
||||
valijson::adapters::JsonCppAdapter,
|
||||
valijson::adapters::JsonCppAdapter>();
|
||||
}
|
||||
|
||||
TEST_F(TestAdapterComparison, JsonCppVsPropertyTree)
|
||||
{
|
||||
testComparison<
|
||||
valijson::adapters::JsonCppAdapter,
|
||||
valijson::adapters::PropertyTreeAdapter>();
|
||||
}
|
||||
|
||||
TEST_F(TestAdapterComparison, JsonCppVsRapidJson)
|
||||
{
|
||||
testComparison<
|
||||
valijson::adapters::JsonCppAdapter,
|
||||
valijson::adapters::RapidJsonAdapter>();
|
||||
}
|
||||
|
||||
TEST_F(TestAdapterComparison, PropertyTreeVsPropertyTree)
|
||||
{
|
||||
testComparison<
|
||||
valijson::adapters::PropertyTreeAdapter,
|
||||
valijson::adapters::PropertyTreeAdapter>();
|
||||
}
|
||||
|
||||
TEST_F(TestAdapterComparison, PropertyTreeVsRapidJson)
|
||||
{
|
||||
testComparison<
|
||||
valijson::adapters::PropertyTreeAdapter,
|
||||
valijson::adapters::RapidJsonAdapter>();
|
||||
}
|
||||
|
||||
TEST_F(TestAdapterComparison, RapidJsonVsRapidJson)
|
||||
{
|
||||
testComparison<
|
||||
valijson::adapters::RapidJsonAdapter,
|
||||
valijson::adapters::RapidJsonAdapter>();
|
||||
}
|
15
tests/test_dereference_callback.cpp
Normal file
15
tests/test_dereference_callback.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <valijson/schema.hpp>
|
||||
|
||||
using valijson::Schema;
|
||||
|
||||
class TestDereferenceCallback : public ::testing::Test
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
TEST_F(TestDereferenceCallback, Basics)
|
||||
{
|
||||
|
||||
}
|
82
tests/test_jsoncpp_adapter.cpp
Normal file
82
tests/test_jsoncpp_adapter.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <valijson/adapters/jsoncpp_adapter.hpp>
|
||||
|
||||
class TestJsonCppAdapter : public testing::Test
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
TEST_F(TestJsonCppAdapter, BasicArrayIteration)
|
||||
{
|
||||
const unsigned int numElements = 10;
|
||||
|
||||
// Create a rapidjson document that consists of an array of numbers
|
||||
Json::Value document(Json::arrayValue);
|
||||
for (unsigned int i = 0; i < numElements; i++) {
|
||||
document.append(Json::Value(i));
|
||||
}
|
||||
|
||||
// Ensure that wrapping the document preserves the array and does not allow
|
||||
// it to be cast to other types
|
||||
valijson::adapters::JsonCppAdapter adapter(document);
|
||||
ASSERT_NO_THROW( adapter.getArray() );
|
||||
ASSERT_ANY_THROW( adapter.getBool() );
|
||||
ASSERT_ANY_THROW( adapter.getDouble() );
|
||||
ASSERT_ANY_THROW( adapter.getObject() );
|
||||
ASSERT_ANY_THROW( adapter.getString() );
|
||||
|
||||
// Ensure that the array contains the expected number of elements
|
||||
EXPECT_EQ( numElements, adapter.getArray().size() );
|
||||
|
||||
// Ensure that the elements are returned in the order they were inserted
|
||||
unsigned int expectedValue = 0;
|
||||
BOOST_FOREACH( const valijson::adapters::JsonCppAdapter value, adapter.getArray() ) {
|
||||
ASSERT_TRUE( value.isNumber() );
|
||||
EXPECT_EQ( double(expectedValue), value.getNumber() );
|
||||
expectedValue++;
|
||||
}
|
||||
|
||||
// Ensure that the correct number of elements were iterated over
|
||||
EXPECT_EQ(numElements, expectedValue);
|
||||
}
|
||||
|
||||
TEST_F(TestJsonCppAdapter, BasicObjectIteration)
|
||||
{
|
||||
const unsigned int numElements = 10;
|
||||
|
||||
// Create a rapidjson document that consists of an object that maps numeric
|
||||
// strings their corresponding numeric values
|
||||
Json::Value document(Json::objectValue);
|
||||
for (unsigned int i = 0; i < numElements; i++) {
|
||||
std::string name(boost::lexical_cast<std::string>(i));
|
||||
document[name] = Json::Value(double(i));
|
||||
}
|
||||
|
||||
// Ensure that wrapping the document preserves the object and does not
|
||||
// allow it to be cast to other types
|
||||
valijson::adapters::JsonCppAdapter adapter(document);
|
||||
ASSERT_NO_THROW( adapter.getObject() );
|
||||
ASSERT_ANY_THROW( adapter.getArray() );
|
||||
ASSERT_ANY_THROW( adapter.getBool() );
|
||||
ASSERT_ANY_THROW( adapter.getDouble() );
|
||||
ASSERT_ANY_THROW( adapter.getString() );
|
||||
|
||||
// Ensure that the object contains the expected number of members
|
||||
EXPECT_EQ( numElements, adapter.getObject().size() );
|
||||
|
||||
// Ensure that the members are returned in the order they were inserted
|
||||
unsigned int expectedValue = 0;
|
||||
BOOST_FOREACH( const valijson::adapters::JsonCppAdapter::ObjectMember member, adapter.getObject() ) {
|
||||
ASSERT_TRUE( member.second.isNumber() );
|
||||
EXPECT_EQ( boost::lexical_cast<std::string>(expectedValue), member.first );
|
||||
EXPECT_EQ( double(expectedValue), member.second.getDouble() );
|
||||
expectedValue++;
|
||||
}
|
||||
|
||||
// Ensure that the correct number of elements were iterated over
|
||||
EXPECT_EQ( numElements, expectedValue );
|
||||
}
|
89
tests/test_property_tree_adapter.cpp
Normal file
89
tests/test_property_tree_adapter.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <valijson/adapters/property_tree_adapter.hpp>
|
||||
|
||||
class TestPropertyTreeAdapter : public testing::Test
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
TEST_F(TestPropertyTreeAdapter, BasicArrayIteration)
|
||||
{
|
||||
const unsigned int numElements = 10;
|
||||
|
||||
// Create a boost property that is equivalent to a JSON array containing a
|
||||
// list of numbers.
|
||||
boost::property_tree::ptree document;
|
||||
for (unsigned int i = 0; i < numElements; i++) {
|
||||
document.push_back(std::make_pair(std::string(),
|
||||
boost::property_tree::ptree(boost::lexical_cast<std::string>(i))));
|
||||
}
|
||||
|
||||
// Ensure that wrapping the document preserves the array and does not allow
|
||||
// it to be cast to other types
|
||||
valijson::adapters::PropertyTreeAdapter adapter(document);
|
||||
ASSERT_NO_THROW( adapter.getArray() );
|
||||
ASSERT_ANY_THROW( adapter.getBool() );
|
||||
ASSERT_ANY_THROW( adapter.getDouble() );
|
||||
ASSERT_ANY_THROW( adapter.getObject() );
|
||||
ASSERT_ANY_THROW( adapter.getString() );
|
||||
|
||||
// Ensure that the array contains the expected number of elements
|
||||
EXPECT_EQ( numElements, adapter.getArray().size() );
|
||||
|
||||
// Ensure that the elements are returned in the order they were inserted
|
||||
unsigned int expectedValue = 0;
|
||||
BOOST_FOREACH( const valijson::adapters::PropertyTreeAdapter value, adapter.getArray() ) {
|
||||
ASSERT_TRUE( value.isString() );
|
||||
ASSERT_FALSE( value.isNumber() );
|
||||
ASSERT_TRUE( value.maybeDouble() );
|
||||
EXPECT_EQ( double(expectedValue), value.asDouble() );
|
||||
expectedValue++;
|
||||
}
|
||||
|
||||
// Ensure that the correct number of elements were iterated over
|
||||
EXPECT_EQ(numElements, expectedValue);
|
||||
}
|
||||
|
||||
TEST_F(TestPropertyTreeAdapter, BasicObjectIteration)
|
||||
{
|
||||
const unsigned int numElements = 10;
|
||||
|
||||
// Create a rapidjson document that consists of an object that maps numeric
|
||||
// strings their corresponding numeric values
|
||||
boost::property_tree::ptree document;
|
||||
for (unsigned int i = 0; i < numElements; i++) {
|
||||
std::string name(boost::lexical_cast<std::string>(i));
|
||||
document.push_back(std::make_pair(name, boost::property_tree::ptree(
|
||||
boost::lexical_cast<std::string>(double(i)))));
|
||||
}
|
||||
|
||||
// Ensure that wrapping the document preserves the object and does not
|
||||
// allow it to be cast to other types
|
||||
valijson::adapters::PropertyTreeAdapter adapter(document);
|
||||
ASSERT_NO_THROW( adapter.getObject() );
|
||||
ASSERT_ANY_THROW( adapter.getArray() );
|
||||
ASSERT_ANY_THROW( adapter.getBool() );
|
||||
ASSERT_ANY_THROW( adapter.getDouble() );
|
||||
ASSERT_ANY_THROW( adapter.getString() );
|
||||
|
||||
// Ensure that the object contains the expected number of members
|
||||
EXPECT_EQ( numElements, adapter.getObject().size() );
|
||||
|
||||
// Ensure that the members are returned in the order they were inserted
|
||||
unsigned int expectedValue = 0;
|
||||
BOOST_FOREACH( const valijson::adapters::PropertyTreeAdapter::ObjectMember member, adapter.getObject() ) {
|
||||
ASSERT_TRUE( member.second.isString() );
|
||||
ASSERT_FALSE( member.second.isNumber() );
|
||||
ASSERT_TRUE( member.second.maybeDouble() );
|
||||
EXPECT_EQ( boost::lexical_cast<std::string>(expectedValue), member.first );
|
||||
EXPECT_EQ( double(expectedValue), member.second.asDouble() );
|
||||
expectedValue++;
|
||||
}
|
||||
|
||||
// Ensure that the correct number of elements were iterated over
|
||||
EXPECT_EQ( numElements, expectedValue );
|
||||
}
|
88
tests/test_rapidjson_adapter.cpp
Normal file
88
tests/test_rapidjson_adapter.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <valijson/adapters/rapidjson_adapter.hpp>
|
||||
|
||||
class TestRapidJsonAdapter : public testing::Test
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
TEST_F(TestRapidJsonAdapter, BasicArrayIteration)
|
||||
{
|
||||
const unsigned int numElements = 10;
|
||||
|
||||
// Create a rapidjson document that consists of an array of numbers
|
||||
rapidjson::Document document;
|
||||
document.SetArray();
|
||||
for (unsigned int i = 0; i < numElements; i++) {
|
||||
rapidjson::Value value;
|
||||
value.SetDouble(i);
|
||||
document.PushBack(value, document.GetAllocator());
|
||||
}
|
||||
|
||||
// Ensure that wrapping the document preserves the array and does not allow
|
||||
// it to be cast to other types
|
||||
valijson::adapters::RapidJsonAdapter adapter(document);
|
||||
ASSERT_NO_THROW( adapter.getArray() );
|
||||
ASSERT_ANY_THROW( adapter.getBool() );
|
||||
ASSERT_ANY_THROW( adapter.getDouble() );
|
||||
ASSERT_ANY_THROW( adapter.getObject() );
|
||||
ASSERT_ANY_THROW( adapter.getString() );
|
||||
|
||||
// Ensure that the array contains the expected number of elements
|
||||
EXPECT_EQ( numElements, adapter.getArray().size() );
|
||||
|
||||
// Ensure that the elements are returned in the order they were inserted
|
||||
unsigned int expectedValue = 0;
|
||||
BOOST_FOREACH( const valijson::adapters::RapidJsonAdapter value, adapter.getArray() ) {
|
||||
ASSERT_TRUE( value.isNumber() );
|
||||
EXPECT_EQ( double(expectedValue), value.getDouble() );
|
||||
expectedValue++;
|
||||
}
|
||||
|
||||
// Ensure that the correct number of elements were iterated over
|
||||
EXPECT_EQ(numElements, expectedValue);
|
||||
}
|
||||
|
||||
TEST_F(TestRapidJsonAdapter, BasicObjectIteration)
|
||||
{
|
||||
const unsigned int numElements = 10;
|
||||
|
||||
// Create a rapidjson document that consists of an object that maps numeric
|
||||
// strings their corresponding numeric values
|
||||
rapidjson::Document document;
|
||||
document.SetObject();
|
||||
for (unsigned int i = 0; i < numElements; i++) {
|
||||
rapidjson::Value name, value;
|
||||
name.SetString(boost::lexical_cast<std::string>(i).c_str(), document.GetAllocator());
|
||||
value.SetDouble(i);
|
||||
document.AddMember(name, value, document.GetAllocator());
|
||||
}
|
||||
|
||||
// Ensure that wrapping the document preserves the object and does not
|
||||
// allow it to be cast to other types
|
||||
valijson::adapters::RapidJsonAdapter adapter(document);
|
||||
ASSERT_NO_THROW( adapter.getObject() );
|
||||
ASSERT_ANY_THROW( adapter.getArray() );
|
||||
ASSERT_ANY_THROW( adapter.getBool() );
|
||||
ASSERT_ANY_THROW( adapter.getDouble() );
|
||||
ASSERT_ANY_THROW( adapter.getString() );
|
||||
|
||||
// Ensure that the object contains the expected number of members
|
||||
EXPECT_EQ( numElements, adapter.getObject().size() );
|
||||
|
||||
// Ensure that the members are returned in the order they were inserted
|
||||
unsigned int expectedValue = 0;
|
||||
BOOST_FOREACH( const valijson::adapters::RapidJsonAdapter::ObjectMember member, adapter.getObject() ) {
|
||||
ASSERT_TRUE( member.second.isNumber() );
|
||||
EXPECT_EQ( boost::lexical_cast<std::string>(expectedValue), member.first );
|
||||
EXPECT_EQ( double(expectedValue), member.second.getDouble() );
|
||||
expectedValue++;
|
||||
}
|
||||
|
||||
// Ensure that the correct number of elements were iterated over
|
||||
EXPECT_EQ( numElements, expectedValue );
|
||||
}
|
19
tests/test_uri_resolution.cpp
Normal file
19
tests/test_uri_resolution.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <valijson/schema.hpp>
|
||||
|
||||
using valijson::Schema;
|
||||
|
||||
class TestUriResolution : public ::testing::Test
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
TEST_F(TestUriResolution, TestDefaultScopeAndUri)
|
||||
{
|
||||
Schema schema;
|
||||
EXPECT_FALSE( schema.hasId() );
|
||||
EXPECT_ANY_THROW( schema.getId() );
|
||||
EXPECT_EQ( "", schema.getUri() );
|
||||
EXPECT_EQ( "", schema.getScope() );
|
||||
}
|
89
tests/test_validation_errors.cpp
Normal file
89
tests/test_validation_errors.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <valijson/adapters/rapidjson_adapter.hpp>
|
||||
#include <valijson/utils/rapidjson_utils.hpp>
|
||||
#include <valijson/schema.hpp>
|
||||
#include <valijson/schema_parser.hpp>
|
||||
#include <valijson/validation_results.hpp>
|
||||
#include <valijson/validator.hpp>
|
||||
|
||||
#define TEST_DATA_DIR "../tests/data"
|
||||
|
||||
using std::string;
|
||||
|
||||
using valijson::adapters::AdapterTraits;
|
||||
using valijson::adapters::RapidJsonAdapter;
|
||||
using valijson::utils::loadDocument;
|
||||
using valijson::Schema;
|
||||
using valijson::SchemaParser;
|
||||
using valijson::Validator;
|
||||
using valijson::ValidationResults;
|
||||
|
||||
class TestValidationErrors : public ::testing::Test
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
TEST_F(TestValidationErrors, AllOfConstraintFailure)
|
||||
{
|
||||
// Load schema document
|
||||
rapidjson::Document schemaDocument;
|
||||
ASSERT_TRUE( loadDocument(TEST_DATA_DIR "/schemas/allof_integers_and_numbers.schema.json", schemaDocument) );
|
||||
RapidJsonAdapter schemaAdapter(schemaDocument);
|
||||
|
||||
// Parse schema document
|
||||
Schema schema;
|
||||
SchemaParser schemaParser;
|
||||
ASSERT_NO_THROW( schemaParser.populateSchema(schemaAdapter, schema) );
|
||||
|
||||
// Load test document
|
||||
rapidjson::Document testDocument;
|
||||
ASSERT_TRUE( loadDocument(TEST_DATA_DIR "/documents/array_doubles_1_2_3.json", testDocument) );
|
||||
RapidJsonAdapter testAdapter(testDocument);
|
||||
|
||||
Validator validator(schema);
|
||||
ValidationResults results;
|
||||
EXPECT_FALSE( validator.validate(testAdapter, &results) );
|
||||
|
||||
ValidationResults::Error error;
|
||||
|
||||
EXPECT_TRUE( results.popError(error) );
|
||||
EXPECT_EQ( "[0]", error.context );
|
||||
EXPECT_EQ( "Value type not permitted by 'type' constraint.", error.description );
|
||||
|
||||
EXPECT_TRUE( results.popError(error) );
|
||||
EXPECT_EQ( "", error.context );
|
||||
EXPECT_EQ( "Failed to validate item #0 in array.", error.description );
|
||||
|
||||
EXPECT_TRUE( results.popError(error) );
|
||||
EXPECT_EQ( "[1]", error.context );
|
||||
EXPECT_EQ( "Value type not permitted by 'type' constraint.", error.description );
|
||||
|
||||
EXPECT_TRUE( results.popError(error) );
|
||||
EXPECT_EQ( "", error.context );
|
||||
EXPECT_EQ( "Failed to validate item #1 in array.", error.description );
|
||||
|
||||
EXPECT_TRUE( results.popError(error) );
|
||||
EXPECT_EQ( "[2]", error.context );
|
||||
EXPECT_EQ( "Value type not permitted by 'type' constraint.", error.description );
|
||||
|
||||
EXPECT_TRUE( results.popError(error) );
|
||||
EXPECT_EQ( "", error.context );
|
||||
EXPECT_EQ( "Failed to validate item #2 in array.", error.description );
|
||||
|
||||
EXPECT_TRUE( results.popError(error) );
|
||||
EXPECT_EQ( "", error.context );
|
||||
EXPECT_EQ( "Failed to validate against child schema at index #0 of allOf constraint.", error.description );
|
||||
|
||||
EXPECT_FALSE( results.popError(error) );
|
||||
|
||||
while (results.popError(error)) {
|
||||
std::cerr << error.context << std::endl;
|
||||
std::cerr << error.description << std::endl;
|
||||
}
|
||||
}
|
325
tests/test_validator.cpp
Normal file
325
tests/test_validator.cpp
Normal file
@ -0,0 +1,325 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <valijson/adapters/jsoncpp_adapter.hpp>
|
||||
#include <valijson/adapters/rapidjson_adapter.hpp>
|
||||
#include <valijson/utils/jsoncpp_utils.hpp>
|
||||
#include <valijson/utils/rapidjson_utils.hpp>
|
||||
#include <valijson/schema.hpp>
|
||||
#include <valijson/schema_parser.hpp>
|
||||
#include <valijson/validation_results.hpp>
|
||||
#include <valijson/validator.hpp>
|
||||
|
||||
#define TEST_SUITE_DIR "../thirdparty/JSON-Schema-Test-Suite/tests/"
|
||||
|
||||
using valijson::adapters::AdapterTraits;
|
||||
using valijson::adapters::RapidJsonAdapter;
|
||||
using valijson::Schema;
|
||||
using valijson::SchemaParser;
|
||||
using valijson::Validator;
|
||||
|
||||
class TestValidator : public ::testing::TestWithParam<const char *>
|
||||
{
|
||||
protected:
|
||||
|
||||
template<typename AdapterType>
|
||||
static void processTestFile(const std::string &testFile,
|
||||
const SchemaParser::Version version)
|
||||
{
|
||||
std::string currentTestCase;
|
||||
std::string currentTest;
|
||||
|
||||
try {
|
||||
|
||||
// Load test document
|
||||
typename AdapterTraits<AdapterType>::DocumentType document;
|
||||
ASSERT_TRUE( valijson::utils::loadDocument(testFile, document) );
|
||||
AdapterType testCases(document);
|
||||
ASSERT_TRUE( testCases.isArray() );
|
||||
|
||||
// Process each test case in the file
|
||||
BOOST_FOREACH( const AdapterType testCase, testCases.getArray() ) {
|
||||
|
||||
currentTestCase.clear();
|
||||
currentTest.clear();
|
||||
|
||||
// Ensure that testCase is an object
|
||||
ASSERT_TRUE( testCase.isObject() );
|
||||
const typename AdapterType::Object object = testCase.getObject();
|
||||
|
||||
// Get test case description
|
||||
typename AdapterType::Object::const_iterator itr = object.find("description");
|
||||
ASSERT_NE( object.end(), itr );
|
||||
currentTestCase = itr->second.getString();
|
||||
|
||||
// Ensure that 'schema' property is present
|
||||
itr = object.find("schema");
|
||||
ASSERT_NE( object.end(), itr );
|
||||
|
||||
// Parse schema
|
||||
Schema schema;
|
||||
SchemaParser parser(version);
|
||||
parser.populateSchema(itr->second, schema);
|
||||
|
||||
// For each test in the 'tests' array
|
||||
itr = object.find("tests");
|
||||
ASSERT_NE( object.end(), itr );
|
||||
ASSERT_TRUE( itr->second.isArray() );
|
||||
BOOST_FOREACH( const AdapterType test, itr->second.getArray() ) {
|
||||
|
||||
const bool strict = itr->second.hasStrictTypes();
|
||||
|
||||
ASSERT_TRUE( test.isObject() );
|
||||
const typename AdapterType::Object testObject = test.getObject();
|
||||
|
||||
typename AdapterType::Object::const_iterator testItr = testObject.find("valid");
|
||||
ASSERT_NE( testObject.end(), testItr );
|
||||
ASSERT_TRUE( testItr->second.maybeBool() );
|
||||
const bool shouldValidate = testItr->second.getBool();
|
||||
|
||||
testItr = testObject.find("description");
|
||||
ASSERT_NE( testObject.end(), testItr );
|
||||
currentTest = testItr->second.getString();
|
||||
|
||||
testItr = testObject.find("data");
|
||||
ASSERT_NE( testObject.end(), testItr );
|
||||
Validator validator(schema);
|
||||
validator.setStrict(strict);
|
||||
|
||||
EXPECT_EQ( shouldValidate, validator.validate(testItr->second, NULL) )
|
||||
<< "Failed while testing validate() function in '"
|
||||
<< currentTest << "' of test case '"
|
||||
<< currentTestCase << "' with adapter '"
|
||||
<< AdapterTraits<AdapterType>::adapterName() << "'";
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
FAIL() << "Exception thrown with message '" << e.what()
|
||||
<< "' in '" << currentTest << "' of test case '"
|
||||
<< currentTestCase << "' with adapter '"
|
||||
<< AdapterTraits<AdapterType>::adapterName() << "'";
|
||||
}
|
||||
}
|
||||
|
||||
void processTestFile(const std::string &testFile,
|
||||
const SchemaParser::Version version)
|
||||
{
|
||||
processTestFile<valijson::adapters::JsonCppAdapter>(testFile, version);
|
||||
processTestFile<valijson::adapters::RapidJsonAdapter>(testFile, version);
|
||||
}
|
||||
|
||||
void processDraft3TestFile(const std::string &testFile)
|
||||
{
|
||||
return processTestFile(testFile, SchemaParser::kDraft3);
|
||||
}
|
||||
|
||||
void processDraft4TestFile(const std::string &testFile)
|
||||
{
|
||||
return processTestFile(testFile, SchemaParser::kDraft4);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestValidator, Draft3_AdditionalItems)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/additionalItems.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_AdditionalProperties)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/additionalProperties.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Dependencies)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/dependencies.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Enum)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/enum.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Items)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/items.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Maximum)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/maximum.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_MaxItems)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/maxItems.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_MaxLength)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/maxLength.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Minimum)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/minimum.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_MinItems)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/minItems.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_MinLength)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/minLength.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Pattern)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/pattern.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_PatternProperties)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/patternProperties.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Properties)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/properties.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Required)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/required.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_Type)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/type.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft3_UniqueItems)
|
||||
{
|
||||
processDraft3TestFile(TEST_SUITE_DIR "draft3/uniqueItems.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_AdditionalItems)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/additionalItems.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_AdditionalProperties)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/additionalProperties.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_AllOf)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/allOf.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_AnyOf)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/anyOf.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Dependencies)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/dependencies.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Enum)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/enum.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Items)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/items.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Maximum)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/maximum.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_MaxItems)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/maxItems.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_MaxLength)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/maxLength.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_MaxProperties)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/maxProperties.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Minimum)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/minimum.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_MinItems)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/minItems.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_MinLength)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/minLength.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_MinProperties)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/minProperties.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Not)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/not.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_OneOf)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/oneOf.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Pattern)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/pattern.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_PatternProperties)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/patternProperties.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Properties)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/properties.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Required)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/required.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_Type)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/type.json");
|
||||
}
|
||||
|
||||
TEST_F(TestValidator, Draft4_UniqueItems)
|
||||
{
|
||||
processDraft4TestFile(TEST_SUITE_DIR "draft4/uniqueItems.json");
|
||||
}
|
19
thirdparty/JSON-Schema-Test-Suite/LICENSE
vendored
Executable file
19
thirdparty/JSON-Schema-Test-Suite/LICENSE
vendored
Executable file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2012 Julian Berman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
80
thirdparty/JSON-Schema-Test-Suite/README.md
vendored
Executable file
80
thirdparty/JSON-Schema-Test-Suite/README.md
vendored
Executable file
@ -0,0 +1,80 @@
|
||||
JSON Schema Test Suite
|
||||
======================
|
||||
|
||||
This repository contains a set of JSON objects that implementors of JSON Schema
|
||||
validation libraries can use to test their validators.
|
||||
|
||||
It is meant to be language agnostic and should require only a JSON parser.
|
||||
|
||||
The conversion of the JSON objects into tests within your test framework of
|
||||
choice is still the job of the validator implementor.
|
||||
|
||||
Structure of a Test
|
||||
-------------------
|
||||
|
||||
If you're going to use this suite, you need to know how tests are laid out. The
|
||||
tests are contained in the `tests` directory at the root of this repository.
|
||||
|
||||
Inside that directory is a subdirectory for each draft or version of the
|
||||
schema. We'll use `draft3` as an example.
|
||||
|
||||
If you look inside the draft directory, there are a number of `.json` files,
|
||||
which logically group a set of test cases together. Often the grouping is by
|
||||
property under test, but not always, especially within optional test files
|
||||
(discussed below).
|
||||
|
||||
Inside each `.json` file is a single array containing objects. It's easiest to
|
||||
illustrate the structure of these with an example:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "the description of the test case",
|
||||
"schema": {"the schema that should" : "be validated against"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a specific test of a valid instance",
|
||||
"data": "the instance",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "another specific test this time, invalid",
|
||||
"data": 15,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
So a description, a schema, and some tests, where tests is an array containing
|
||||
one or more objects with descriptions, data, and a boolean indicating whether
|
||||
they should be valid or invalid.
|
||||
|
||||
Coverage
|
||||
--------
|
||||
|
||||
Draft 3 and 4 should have full coverage. If you see anything missing or think
|
||||
there is a useful test missing, please send a pull request or open an issue.
|
||||
|
||||
Who Uses the Test Suite
|
||||
-----------------------
|
||||
|
||||
This suite is being used by:
|
||||
|
||||
* [json-schema-validator (Java)](https://github.com/fge/json-schema-validator)
|
||||
* [jsonschema (python)](https://github.com/Julian/jsonschema)
|
||||
* [aeson-schema (haskell)](https://github.com/timjb/aeson-schema)
|
||||
* [direct-schema (javascript)](https://github.com/IreneKnapp/direct-schema)
|
||||
* [jsonschema (javascript)](https://github.com/tdegrunt/jsonschema)
|
||||
* [JaySchema (javascript)](https://github.com/natesilva/jayschema)
|
||||
* [z-schema (javascript)](https://github.com/zaggino/z-schema)
|
||||
* [jesse (Erlang)](https://github.com/klarna/jesse)
|
||||
* [json-schema (PHP)](https://github.com/justinrainbow/json-schema)
|
||||
* [gojsonschema (Go)](https://github.com/sigu-399/gojsonschema)
|
||||
|
||||
If you use it as well, please fork and send a pull request adding yourself to
|
||||
the list :).
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you see something missing or incorrect, a pull request is most welcome!
|
82
thirdparty/JSON-Schema-Test-Suite/tests/draft3/additionalItems.json
vendored
Executable file
82
thirdparty/JSON-Schema-Test-Suite/tests/draft3/additionalItems.json
vendored
Executable file
@ -0,0 +1,82 @@
|
||||
[
|
||||
{
|
||||
"description": "additionalItems as schema",
|
||||
"schema": {
|
||||
"items": [],
|
||||
"additionalItems": {"type": "integer"}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "additional items match schema",
|
||||
"data": [ 1, 2, 3, 4 ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "additional items do not match schema",
|
||||
"data": [ 1, 2, 3, "foo" ],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "items is schema, no additionalItems",
|
||||
"schema": {
|
||||
"items": {},
|
||||
"additionalItems": false
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "all items match schema",
|
||||
"data": [ 1, 2, 3, 4, 5 ],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "array of items with no additionalItems",
|
||||
"schema": {
|
||||
"items": [{}, {}, {}],
|
||||
"additionalItems": false
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "no additional items present",
|
||||
"data": [ 1, 2, 3 ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "additional items are not permitted",
|
||||
"data": [ 1, 2, 3, 4 ],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "additionalItems as false without items",
|
||||
"schema": {"additionalItems": false},
|
||||
"tests": [
|
||||
{
|
||||
"description":
|
||||
"items defaults to empty schema so everything is valid",
|
||||
"data": [ 1, 2, 3, 4, 5 ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "ignores non-arrays",
|
||||
"data": {"foo" : "bar"},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "additionalItems are allowed by default",
|
||||
"schema": {"items": []},
|
||||
"tests": [
|
||||
{
|
||||
"description": "only the first items are validated",
|
||||
"data": [1, "foo", false],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
63
thirdparty/JSON-Schema-Test-Suite/tests/draft3/additionalProperties.json
vendored
Executable file
63
thirdparty/JSON-Schema-Test-Suite/tests/draft3/additionalProperties.json
vendored
Executable file
@ -0,0 +1,63 @@
|
||||
[
|
||||
{
|
||||
"description":
|
||||
"additionalProperties being false does not allow other properties",
|
||||
"schema": {
|
||||
"properties": {"foo": {}, "bar": {}},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "no additional properties is valid",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an additional property is invalid",
|
||||
"data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-objects",
|
||||
"data": [1, 2, 3],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":
|
||||
"additionalProperties allows a schema which should validate",
|
||||
"schema": {
|
||||
"properties": {"foo": {}, "bar": {}},
|
||||
"additionalProperties": {"type": "boolean"}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "no additional properties is valid",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an additional valid property is valid",
|
||||
"data": {"foo" : 1, "bar" : 2, "quux" : true},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an additional invalid property is invalid",
|
||||
"data": {"foo" : 1, "bar" : 2, "quux" : 12},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "additionalProperties are allowed by default",
|
||||
"schema": {"properties": {"foo": {}, "bar": {}}},
|
||||
"tests": [
|
||||
{
|
||||
"description": "additional properties are allowed",
|
||||
"data": {"foo": 1, "bar": 2, "quux": true},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
108
thirdparty/JSON-Schema-Test-Suite/tests/draft3/dependencies.json
vendored
Executable file
108
thirdparty/JSON-Schema-Test-Suite/tests/draft3/dependencies.json
vendored
Executable file
@ -0,0 +1,108 @@
|
||||
[
|
||||
{
|
||||
"description": "dependencies",
|
||||
"schema": {
|
||||
"dependencies": {"bar": "foo"}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "neither",
|
||||
"data": {},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "nondependant",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "with dependency",
|
||||
"data": {"foo": 1, "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "missing dependency",
|
||||
"data": {"bar": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-objects",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple dependencies",
|
||||
"schema": {
|
||||
"dependencies": {"quux": ["foo", "bar"]}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "neither",
|
||||
"data": {},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "nondependants",
|
||||
"data": {"foo": 1, "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "with dependencies",
|
||||
"data": {"foo": 1, "bar": 2, "quux": 3},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "missing dependency",
|
||||
"data": {"foo": 1, "quux": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "missing other dependency",
|
||||
"data": {"bar": 1, "quux": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "missing both dependencies",
|
||||
"data": {"quux": 1},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple dependencies subschema",
|
||||
"schema": {
|
||||
"dependencies": {
|
||||
"bar": {
|
||||
"properties": {
|
||||
"foo": {"type": "integer"},
|
||||
"bar": {"type": "integer"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid",
|
||||
"data": {"foo": 1, "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "wrong type",
|
||||
"data": {"foo": "quux", "bar": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "wrong type other",
|
||||
"data": {"foo": 2, "bar": "quux"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "wrong type both",
|
||||
"data": {"foo": "quux", "bar": "quux"},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
80
thirdparty/JSON-Schema-Test-Suite/tests/draft3/disallow.json
vendored
Executable file
80
thirdparty/JSON-Schema-Test-Suite/tests/draft3/disallow.json
vendored
Executable file
@ -0,0 +1,80 @@
|
||||
[
|
||||
{
|
||||
"description": "disallow",
|
||||
"schema": {
|
||||
"disallow": "integer"
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "allowed",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "disallowed",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple disallow",
|
||||
"schema": {
|
||||
"disallow": ["integer", "boolean"]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "other mismatch",
|
||||
"data": true,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple disallow subschema",
|
||||
"schema": {
|
||||
"disallow":
|
||||
["string",
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"foo": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "match",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "other match",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "other mismatch",
|
||||
"data": {"foo": "bar"},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
60
thirdparty/JSON-Schema-Test-Suite/tests/draft3/divisibleBy.json
vendored
Executable file
60
thirdparty/JSON-Schema-Test-Suite/tests/draft3/divisibleBy.json
vendored
Executable file
@ -0,0 +1,60 @@
|
||||
[
|
||||
{
|
||||
"description": "by int",
|
||||
"schema": {"divisibleBy": 2},
|
||||
"tests": [
|
||||
{
|
||||
"description": "int by int",
|
||||
"data": 10,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "int by int fail",
|
||||
"data": 7,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-numbers",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "by number",
|
||||
"schema": {"divisibleBy": 1.5},
|
||||
"tests": [
|
||||
{
|
||||
"description": "zero is divisible by anything (except 0)",
|
||||
"data": 0,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "4.5 is divisible by 1.5",
|
||||
"data": 4.5,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "35 is not divisible by 1.5",
|
||||
"data": 35,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "by small number",
|
||||
"schema": {"divisibleBy": 0.0001},
|
||||
"tests": [
|
||||
{
|
||||
"description": "0.0075 is divisible by 0.0001",
|
||||
"data": 0.0075,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "0.00751 is not divisible by 0.0001",
|
||||
"data": 0.00751,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
39
thirdparty/JSON-Schema-Test-Suite/tests/draft3/enum.json
vendored
Executable file
39
thirdparty/JSON-Schema-Test-Suite/tests/draft3/enum.json
vendored
Executable file
@ -0,0 +1,39 @@
|
||||
[
|
||||
{
|
||||
"description": "simple enum validation",
|
||||
"schema": {"enum": [1, 2, 3]},
|
||||
"tests": [
|
||||
{
|
||||
"description": "one of the enum is valid",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "something else is invalid",
|
||||
"data": 4,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "heterogeneous enum validation",
|
||||
"schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
|
||||
"tests": [
|
||||
{
|
||||
"description": "one of the enum is valid",
|
||||
"data": [],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "something else is invalid",
|
||||
"data": null,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "objects are deep compared",
|
||||
"data": {"foo": false},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
94
thirdparty/JSON-Schema-Test-Suite/tests/draft3/extends.json
vendored
Executable file
94
thirdparty/JSON-Schema-Test-Suite/tests/draft3/extends.json
vendored
Executable file
@ -0,0 +1,94 @@
|
||||
[
|
||||
{
|
||||
"description": "extends",
|
||||
"schema": {
|
||||
"properties": {"bar": {"type": "integer", "required": true}},
|
||||
"extends": {
|
||||
"properties": {
|
||||
"foo": {"type": "string", "required": true}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "extends",
|
||||
"data": {"foo": "baz", "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch extends",
|
||||
"data": {"foo": "baz"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "mismatch extended",
|
||||
"data": {"bar": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "wrong type",
|
||||
"data": {"foo": "baz", "bar": "quux"},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple extends",
|
||||
"schema": {
|
||||
"properties": {"bar": {"type": "integer", "required": true}},
|
||||
"extends" : [
|
||||
{
|
||||
"properties": {
|
||||
"foo": {"type": "string", "required": true}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"baz": {"type": "null", "required": true}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid",
|
||||
"data": {"foo": "quux", "bar": 2, "baz": null},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch first extends",
|
||||
"data": {"bar": 2, "baz": null},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "mismatch second extends",
|
||||
"data": {"foo": "quux", "bar": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "mismatch both",
|
||||
"data": {"bar": 2},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "extends simple types",
|
||||
"schema": {
|
||||
"minimum": 20,
|
||||
"extends": {"maximum": 30}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid",
|
||||
"data": 25,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch extends",
|
||||
"data": 35,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
46
thirdparty/JSON-Schema-Test-Suite/tests/draft3/items.json
vendored
Executable file
46
thirdparty/JSON-Schema-Test-Suite/tests/draft3/items.json
vendored
Executable file
@ -0,0 +1,46 @@
|
||||
[
|
||||
{
|
||||
"description": "a schema given for items",
|
||||
"schema": {
|
||||
"items": {"type": "integer"}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid items",
|
||||
"data": [ 1, 2, 3 ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "wrong type of items",
|
||||
"data": [1, "x"],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-arrays",
|
||||
"data": {"foo" : "bar"},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "an array of schemas for items",
|
||||
"schema": {
|
||||
"items": [
|
||||
{"type": "integer"},
|
||||
{"type": "string"}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "correct types",
|
||||
"data": [ 1, "foo" ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "wrong types",
|
||||
"data": [ "foo", 1 ],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft3/maxItems.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft3/maxItems.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "maxItems validation",
|
||||
"schema": {"maxItems": 2},
|
||||
"tests": [
|
||||
{
|
||||
"description": "shorter is valid",
|
||||
"data": [1],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": [1, 2],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too long is invalid",
|
||||
"data": [1, 2, 3],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-arrays",
|
||||
"data": "foobar",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft3/maxLength.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft3/maxLength.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "maxLength validation",
|
||||
"schema": {"maxLength": 2},
|
||||
"tests": [
|
||||
{
|
||||
"description": "shorter is valid",
|
||||
"data": "f",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": "fo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too long is invalid",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-strings",
|
||||
"data": 10,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
42
thirdparty/JSON-Schema-Test-Suite/tests/draft3/maximum.json
vendored
Executable file
42
thirdparty/JSON-Schema-Test-Suite/tests/draft3/maximum.json
vendored
Executable file
@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"description": "maximum validation",
|
||||
"schema": {"maximum": 3.0},
|
||||
"tests": [
|
||||
{
|
||||
"description": "below the maximum is valid",
|
||||
"data": 2.6,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "above the maximum is invalid",
|
||||
"data": 3.5,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-numbers",
|
||||
"data": "x",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "exclusiveMaximum validation",
|
||||
"schema": {
|
||||
"maximum": 3.0,
|
||||
"exclusiveMaximum": true
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "below the maximum is still valid",
|
||||
"data": 2.2,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "boundary point is invalid",
|
||||
"data": 3.0,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft3/minItems.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft3/minItems.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "minItems validation",
|
||||
"schema": {"minItems": 1},
|
||||
"tests": [
|
||||
{
|
||||
"description": "longer is valid",
|
||||
"data": [1, 2],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": [1],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too short is invalid",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-arrays",
|
||||
"data": "",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft3/minLength.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft3/minLength.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "minLength validation",
|
||||
"schema": {"minLength": 2},
|
||||
"tests": [
|
||||
{
|
||||
"description": "longer is valid",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": "fo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too short is invalid",
|
||||
"data": "f",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-strings",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
42
thirdparty/JSON-Schema-Test-Suite/tests/draft3/minimum.json
vendored
Executable file
42
thirdparty/JSON-Schema-Test-Suite/tests/draft3/minimum.json
vendored
Executable file
@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"description": "minimum validation",
|
||||
"schema": {"minimum": 1.1},
|
||||
"tests": [
|
||||
{
|
||||
"description": "above the minimum is valid",
|
||||
"data": 2.6,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "below the minimum is invalid",
|
||||
"data": 0.6,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-numbers",
|
||||
"data": "x",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "exclusiveMinimum validation",
|
||||
"schema": {
|
||||
"minimum": 1.1,
|
||||
"exclusiveMinimum": true
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "above the minimum is still valid",
|
||||
"data": 1.2,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "boundary point is invalid",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
49
thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/bignum.json
vendored
Executable file
49
thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/bignum.json
vendored
Executable file
@ -0,0 +1,49 @@
|
||||
[
|
||||
{
|
||||
"description": "integer",
|
||||
"schema": {"type": "integer"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a bignum is an integer",
|
||||
"data": 12345678910111213141516171819202122232425262728293031,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "number",
|
||||
"schema": {"type": "number"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a bignum is a number",
|
||||
"data": 98249283749234923498293171823948729348710298301928331,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "string",
|
||||
"schema": {"type": "string"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a bignum is not a string",
|
||||
"data": 98249283749234923498293171823948729348710298301928331,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "float comparison with high precision",
|
||||
"schema": {
|
||||
"maximum": 972783798187987123879878123.18878137,
|
||||
"exclusiveMaximum": true
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "comparison works for high numbers",
|
||||
"data": 972783798187987123879878123.188781371,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
212
thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/format.json
vendored
Executable file
212
thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/format.json
vendored
Executable file
@ -0,0 +1,212 @@
|
||||
[
|
||||
{
|
||||
"description": "validation of regular expressions",
|
||||
"schema": {"format": "regex"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid regular expression",
|
||||
"data": "([abc])+\\s+$",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a regular expression with unclosed parens is invalid",
|
||||
"data": "^(abc]",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of date-time strings",
|
||||
"schema": {"format": "date-time"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid date-time string",
|
||||
"data": "1963-06-19T08:30:06.283185Z",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an invalid date-time string",
|
||||
"data": "06/19/1963 08:30:06 PST",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "only RFC3339 not all of ISO 8601 are valid",
|
||||
"data": "2013-350T01:01:01",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of date strings",
|
||||
"schema": {"format": "date"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid date string",
|
||||
"data": "1963-06-19",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an invalid date string",
|
||||
"data": "06/19/1963",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of time strings",
|
||||
"schema": {"format": "time"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid time string",
|
||||
"data": "08:30:06",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an invalid time string",
|
||||
"data": "8:30 AM",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of URIs",
|
||||
"schema": {"format": "uri"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid URI",
|
||||
"data": "http://foo.bar/?baz=qux#quux",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an invalid URI",
|
||||
"data": "\\\\WINDOWS\\fileshare",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of e-mail addresses",
|
||||
"schema": {"format": "email"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid e-mail address",
|
||||
"data": "joe.bloggs@example.com",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an invalid e-mail address",
|
||||
"data": "2962",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of IP addresses",
|
||||
"schema": {"format": "ip-address"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid IP address",
|
||||
"data": "192.168.0.1",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an IP address with too many components",
|
||||
"data": "127.0.0.0.1",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an IP address with out-of-range values",
|
||||
"data": "256.256.256.256",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of IPv6 addresses",
|
||||
"schema": {"format": "ipv6"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid IPv6 address",
|
||||
"data": "::1",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an IPv6 address with out-of-range values",
|
||||
"data": "12345::",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an IPv6 address with too many components",
|
||||
"data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an IPv6 address containing illegal characters",
|
||||
"data": "::laptop",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of host names",
|
||||
"schema": {"format": "host-name"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid host name",
|
||||
"data": "www.example.com",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a host name starting with an illegal character",
|
||||
"data": "-a-host-name-that-starts-with--",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a host name containing illegal characters",
|
||||
"data": "not_a_valid_host_name",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a host name with a component too long",
|
||||
"data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "validation of CSS colors",
|
||||
"schema": {"format": "color"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a valid CSS color name",
|
||||
"data": "fuchsia",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a valid six-digit CSS color code",
|
||||
"data": "#CC8899",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a valid three-digit CSS color code",
|
||||
"data": "#C89",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an invalid CSS color code",
|
||||
"data": "#00332520",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an invalid CSS color name",
|
||||
"data": "puce",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a CSS color name containing invalid characters",
|
||||
"data": "light_grayish_red-violet",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
18
thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/jsregex.json
vendored
Executable file
18
thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/jsregex.json
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"description": "ECMA 262 regex dialect recognition",
|
||||
"schema": { "format": "regex" },
|
||||
"tests": [
|
||||
{
|
||||
"description": "[^] is a valid regex",
|
||||
"data": "[^]",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "ECMA 262 has no support for lookbehind",
|
||||
"data": "(?<=foo)bar",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
15
thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/zeroTerminatedFloats.json
vendored
Executable file
15
thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/zeroTerminatedFloats.json
vendored
Executable file
@ -0,0 +1,15 @@
|
||||
[
|
||||
{
|
||||
"description": "some languages do not distinguish between different types of numeric value",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a float is not an integer even without fractional part",
|
||||
"data": 1.0,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
23
thirdparty/JSON-Schema-Test-Suite/tests/draft3/pattern.json
vendored
Executable file
23
thirdparty/JSON-Schema-Test-Suite/tests/draft3/pattern.json
vendored
Executable file
@ -0,0 +1,23 @@
|
||||
[
|
||||
{
|
||||
"description": "pattern validation",
|
||||
"schema": {"pattern": "^a*$"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a matching pattern is valid",
|
||||
"data": "aaa",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a non-matching pattern is invalid",
|
||||
"data": "abc",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-strings",
|
||||
"data": true,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
110
thirdparty/JSON-Schema-Test-Suite/tests/draft3/patternProperties.json
vendored
Executable file
110
thirdparty/JSON-Schema-Test-Suite/tests/draft3/patternProperties.json
vendored
Executable file
@ -0,0 +1,110 @@
|
||||
[
|
||||
{
|
||||
"description":
|
||||
"patternProperties validates properties matching a regex",
|
||||
"schema": {
|
||||
"patternProperties": {
|
||||
"f.*o": {"type": "integer"}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a single valid match is valid",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "multiple valid matches is valid",
|
||||
"data": {"foo": 1, "foooooo" : 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a single invalid match is invalid",
|
||||
"data": {"foo": "bar", "fooooo": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "multiple invalid matches is invalid",
|
||||
"data": {"foo": "bar", "foooooo" : "baz"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-objects",
|
||||
"data": 12,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple simultaneous patternProperties are validated",
|
||||
"schema": {
|
||||
"patternProperties": {
|
||||
"a*": {"type": "integer"},
|
||||
"aaa*": {"maximum": 20}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "a single valid match is valid",
|
||||
"data": {"a": 21},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a simultaneous match is valid",
|
||||
"data": {"aaaa": 18},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "multiple matches is valid",
|
||||
"data": {"a": 21, "aaaa": 18},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an invalid due to one is invalid",
|
||||
"data": {"a": "bar"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an invalid due to the other is invalid",
|
||||
"data": {"aaaa": 31},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an invalid due to both is invalid",
|
||||
"data": {"aaa": "foo", "aaaa": 31},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "regexes are not anchored by default and are case sensitive",
|
||||
"schema": {
|
||||
"patternProperties": {
|
||||
"[0-9]{2,}": { "type": "boolean" },
|
||||
"X_": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "non recognized members are ignored",
|
||||
"data": { "answer 1": "42" },
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "recognized members are accounted for",
|
||||
"data": { "a31b": null },
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "regexes are case sensitive",
|
||||
"data": { "a_x_3": 3 },
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "regexes are case sensitive, 2",
|
||||
"data": { "a_X_3": 3 },
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
92
thirdparty/JSON-Schema-Test-Suite/tests/draft3/properties.json
vendored
Executable file
92
thirdparty/JSON-Schema-Test-Suite/tests/draft3/properties.json
vendored
Executable file
@ -0,0 +1,92 @@
|
||||
[
|
||||
{
|
||||
"description": "object properties validation",
|
||||
"schema": {
|
||||
"properties": {
|
||||
"foo": {"type": "integer"},
|
||||
"bar": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "both properties present and valid is valid",
|
||||
"data": {"foo": 1, "bar": "baz"},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "one property invalid is invalid",
|
||||
"data": {"foo": 1, "bar": {}},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "both properties invalid is invalid",
|
||||
"data": {"foo": [], "bar": {}},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "doesn't invalidate other properties",
|
||||
"data": {"quux": []},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "ignores non-objects",
|
||||
"data": [],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":
|
||||
"properties, patternProperties, additionalProperties interaction",
|
||||
"schema": {
|
||||
"properties": {
|
||||
"foo": {"type": "array", "maxItems": 3},
|
||||
"bar": {"type": "array"}
|
||||
},
|
||||
"patternProperties": {"f.o": {"minItems": 2}},
|
||||
"additionalProperties": {"type": "integer"}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "property validates property",
|
||||
"data": {"foo": [1, 2]},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "property invalidates property",
|
||||
"data": {"foo": [1, 2, 3, 4]},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "patternProperty invalidates property",
|
||||
"data": {"foo": []},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "patternProperty validates nonproperty",
|
||||
"data": {"fxo": [1, 2]},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "patternProperty invalidates nonproperty",
|
||||
"data": {"fxo": []},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "additionalProperty ignores property",
|
||||
"data": {"bar": []},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "additionalProperty validates others",
|
||||
"data": {"quux": 3},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "additionalProperty invalidates others",
|
||||
"data": {"quux": "foo"},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
144
thirdparty/JSON-Schema-Test-Suite/tests/draft3/ref.json
vendored
Executable file
144
thirdparty/JSON-Schema-Test-Suite/tests/draft3/ref.json
vendored
Executable file
@ -0,0 +1,144 @@
|
||||
[
|
||||
{
|
||||
"description": "root pointer ref",
|
||||
"schema": {
|
||||
"properties": {
|
||||
"foo": {"$ref": "#"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "match",
|
||||
"data": {"foo": false},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "recursive match",
|
||||
"data": {"foo": {"foo": false}},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch",
|
||||
"data": {"bar": false},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "recursive mismatch",
|
||||
"data": {"foo": {"bar": false}},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "relative pointer ref to object",
|
||||
"schema": {
|
||||
"properties": {
|
||||
"foo": {"type": "integer"},
|
||||
"bar": {"$ref": "#/properties/foo"}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "match",
|
||||
"data": {"bar": 3},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch",
|
||||
"data": {"bar": true},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "relative pointer ref to array",
|
||||
"schema": {
|
||||
"items": [
|
||||
{"type": "integer"},
|
||||
{"$ref": "#/items/0"}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "match array",
|
||||
"data": [1, 2],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch array",
|
||||
"data": [1, "foo"],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "escaped pointer ref",
|
||||
"schema": {
|
||||
"tilda~field": {"type": "integer"},
|
||||
"slash/field": {"type": "integer"},
|
||||
"percent%field": {"type": "integer"},
|
||||
"properties": {
|
||||
"tilda": {"$ref": "#/tilda~0field"},
|
||||
"slash": {"$ref": "#/slash~1field"},
|
||||
"percent": {"$ref": "#/percent%25field"}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "slash",
|
||||
"data": {"slash": "aoeu"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "tilda",
|
||||
"data": {"tilda": "aoeu"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "percent",
|
||||
"data": {"percent": "aoeu"},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "nested refs",
|
||||
"schema": {
|
||||
"definitions": {
|
||||
"a": {"type": "integer"},
|
||||
"b": {"$ref": "#/definitions/a"},
|
||||
"c": {"$ref": "#/definitions/b"}
|
||||
},
|
||||
"$ref": "#/definitions/c"
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "nested ref valid",
|
||||
"data": 5,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "nested ref invalid",
|
||||
"data": "a",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "remote ref, containing refs itself",
|
||||
"schema": {"$ref": "http://json-schema.org/draft-03/schema#"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "remote ref valid",
|
||||
"data": {"items": {"type": "integer"}},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "remote ref invalid",
|
||||
"data": {"items": {"type": 1}},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
74
thirdparty/JSON-Schema-Test-Suite/tests/draft3/refRemote.json
vendored
Executable file
74
thirdparty/JSON-Schema-Test-Suite/tests/draft3/refRemote.json
vendored
Executable file
@ -0,0 +1,74 @@
|
||||
[
|
||||
{
|
||||
"description": "remote ref",
|
||||
"schema": {"$ref": "http://localhost:1234/integer.json"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "remote ref valid",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "remote ref invalid",
|
||||
"data": "a",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "fragment within remote ref",
|
||||
"schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "remote fragment valid",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "remote fragment invalid",
|
||||
"data": "a",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "ref within remote ref",
|
||||
"schema": {
|
||||
"$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "ref within ref valid",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "ref within ref invalid",
|
||||
"data": "a",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "change resolution scope",
|
||||
"schema": {
|
||||
"id": "http://localhost:1234/",
|
||||
"items": {
|
||||
"id": "folder/",
|
||||
"items": {"$ref": "folderInteger.json"}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "changed scope ref valid",
|
||||
"data": [[1]],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "changed scope ref invalid",
|
||||
"data": [["a"]],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
53
thirdparty/JSON-Schema-Test-Suite/tests/draft3/required.json
vendored
Executable file
53
thirdparty/JSON-Schema-Test-Suite/tests/draft3/required.json
vendored
Executable file
@ -0,0 +1,53 @@
|
||||
[
|
||||
{
|
||||
"description": "required validation",
|
||||
"schema": {
|
||||
"properties": {
|
||||
"foo": {"required" : true},
|
||||
"bar": {}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "present required property is valid",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "non-present required property is invalid",
|
||||
"data": {"bar": 1},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "required default validation",
|
||||
"schema": {
|
||||
"properties": {
|
||||
"foo": {}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "not required by default",
|
||||
"data": {},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "required explicitly false validation",
|
||||
"schema": {
|
||||
"properties": {
|
||||
"foo": {"required": false}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "not required if required is false",
|
||||
"data": {},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
474
thirdparty/JSON-Schema-Test-Suite/tests/draft3/type.json
vendored
Executable file
474
thirdparty/JSON-Schema-Test-Suite/tests/draft3/type.json
vendored
Executable file
@ -0,0 +1,474 @@
|
||||
[
|
||||
{
|
||||
"description": "integer type matches integers",
|
||||
"schema": {"type": "integer"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is an integer",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a float is not an integer",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a string is not an integer",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an object is not an integer",
|
||||
"data": {},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an array is not an integer",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a boolean is not an integer",
|
||||
"data": true,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "null is not an integer",
|
||||
"data": null,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "number type matches numbers",
|
||||
"schema": {"type": "number"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is a number",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a float is a number",
|
||||
"data": 1.1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a string is not a number",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an object is not a number",
|
||||
"data": {},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an array is not a number",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a boolean is not a number",
|
||||
"data": true,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "null is not a number",
|
||||
"data": null,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "string type matches strings",
|
||||
"schema": {"type": "string"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "1 is not a string",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a float is not a string",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a string is a string",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an object is not a string",
|
||||
"data": {},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an array is not a string",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a boolean is not a string",
|
||||
"data": true,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "null is not a string",
|
||||
"data": null,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "object type matches objects",
|
||||
"schema": {"type": "object"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is not an object",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a float is not an object",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a string is not an object",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an object is an object",
|
||||
"data": {},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an array is not an object",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a boolean is not an object",
|
||||
"data": true,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "null is not an object",
|
||||
"data": null,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "array type matches arrays",
|
||||
"schema": {"type": "array"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is not an array",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a float is not an array",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a string is not an array",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an object is not an array",
|
||||
"data": {},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an array is not an array",
|
||||
"data": [],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a boolean is not an array",
|
||||
"data": true,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "null is not an array",
|
||||
"data": null,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "boolean type matches booleans",
|
||||
"schema": {"type": "boolean"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is not a boolean",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a float is not a boolean",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a string is not a boolean",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an object is not a boolean",
|
||||
"data": {},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an array is not a boolean",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a boolean is not a boolean",
|
||||
"data": true,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "null is not a boolean",
|
||||
"data": null,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "null type matches only the null object",
|
||||
"schema": {"type": "null"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is not null",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a float is not null",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a string is not null",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an object is not null",
|
||||
"data": {},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an array is not null",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a boolean is not null",
|
||||
"data": true,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "null is null",
|
||||
"data": null,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "any type matches any type",
|
||||
"schema": {"type": "any"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "any type includes integers",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "any type includes float",
|
||||
"data": 1.1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "any type includes string",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "any type includes object",
|
||||
"data": {},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "any type includes array",
|
||||
"data": [],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "any type includes boolean",
|
||||
"data": true,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "any type includes null",
|
||||
"data": null,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple types can be specified in an array",
|
||||
"schema": {"type": ["integer", "string"]},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is valid",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a string is valid",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a float is invalid",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an object is invalid",
|
||||
"data": {},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an array is invalid",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a boolean is invalid",
|
||||
"data": true,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "null is invalid",
|
||||
"data": null,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "types can include schemas",
|
||||
"schema": {
|
||||
"type": [
|
||||
"array",
|
||||
{"type": "object"}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is invalid",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a string is invalid",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a float is invalid",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "an object is valid",
|
||||
"data": {},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an array is valid",
|
||||
"data": [],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "a boolean is invalid",
|
||||
"data": true,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "null is invalid",
|
||||
"data": null,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":
|
||||
"when types includes a schema it should fully validate the schema",
|
||||
"schema": {
|
||||
"type": [
|
||||
"integer",
|
||||
{
|
||||
"properties": {
|
||||
"foo": {"type": "null"}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is valid",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an object is valid only if it is fully valid",
|
||||
"data": {"foo": null},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an object is invalid otherwise",
|
||||
"data": {"foo": "bar"},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "types from separate schemas are merged",
|
||||
"schema": {
|
||||
"type": [
|
||||
{"type": ["string"]},
|
||||
{"type": ["array", "null"]}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "an integer is invalid",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "a string is valid",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an array is valid",
|
||||
"data": [1, 2, 3],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
79
thirdparty/JSON-Schema-Test-Suite/tests/draft3/uniqueItems.json
vendored
Executable file
79
thirdparty/JSON-Schema-Test-Suite/tests/draft3/uniqueItems.json
vendored
Executable file
@ -0,0 +1,79 @@
|
||||
[
|
||||
{
|
||||
"description": "uniqueItems validation",
|
||||
"schema": {"uniqueItems": true},
|
||||
"tests": [
|
||||
{
|
||||
"description": "unique array of integers is valid",
|
||||
"data": [1, 2],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "non-unique array of integers is invalid",
|
||||
"data": [1, 1],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "numbers are unique if mathematically unequal",
|
||||
"data": [1.0, 1.00, 1],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "unique array of objects is valid",
|
||||
"data": [{"foo": "bar"}, {"foo": "baz"}],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "non-unique array of objects is invalid",
|
||||
"data": [{"foo": "bar"}, {"foo": "bar"}],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "unique array of nested objects is valid",
|
||||
"data": [
|
||||
{"foo": {"bar" : {"baz" : true}}},
|
||||
{"foo": {"bar" : {"baz" : false}}}
|
||||
],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "non-unique array of nested objects is invalid",
|
||||
"data": [
|
||||
{"foo": {"bar" : {"baz" : true}}},
|
||||
{"foo": {"bar" : {"baz" : true}}}
|
||||
],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "unique array of arrays is valid",
|
||||
"data": [["foo"], ["bar"]],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "non-unique array of arrays is invalid",
|
||||
"data": [["foo"], ["foo"]],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "1 and true are unique",
|
||||
"data": [1, true],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "0 and false are unique",
|
||||
"data": [0, false],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "unique heterogeneous types are valid",
|
||||
"data": [{}, [1], true, null, 1],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "non-unique heterogeneous types are invalid",
|
||||
"data": [{}, [1], true, null, {}, 1],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
82
thirdparty/JSON-Schema-Test-Suite/tests/draft4/additionalItems.json
vendored
Executable file
82
thirdparty/JSON-Schema-Test-Suite/tests/draft4/additionalItems.json
vendored
Executable file
@ -0,0 +1,82 @@
|
||||
[
|
||||
{
|
||||
"description": "additionalItems as schema",
|
||||
"schema": {
|
||||
"items": [{}],
|
||||
"additionalItems": {"type": "integer"}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "additional items match schema",
|
||||
"data": [ null, 2, 3, 4 ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "additional items do not match schema",
|
||||
"data": [ null, 2, 3, "foo" ],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "items is schema, no additionalItems",
|
||||
"schema": {
|
||||
"items": {},
|
||||
"additionalItems": false
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "all items match schema",
|
||||
"data": [ 1, 2, 3, 4, 5 ],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "array of items with no additionalItems",
|
||||
"schema": {
|
||||
"items": [{}, {}, {}],
|
||||
"additionalItems": false
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "no additional items present",
|
||||
"data": [ 1, 2, 3 ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "additional items are not permitted",
|
||||
"data": [ 1, 2, 3, 4 ],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "additionalItems as false without items",
|
||||
"schema": {"additionalItems": false},
|
||||
"tests": [
|
||||
{
|
||||
"description":
|
||||
"items defaults to empty schema so everything is valid",
|
||||
"data": [ 1, 2, 3, 4, 5 ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "ignores non-arrays",
|
||||
"data": {"foo" : "bar"},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "additionalItems are allowed by default",
|
||||
"schema": {"items": [{"type": "integer"}]},
|
||||
"tests": [
|
||||
{
|
||||
"description": "only the first item is validated",
|
||||
"data": [1, "foo", false],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
63
thirdparty/JSON-Schema-Test-Suite/tests/draft4/additionalProperties.json
vendored
Executable file
63
thirdparty/JSON-Schema-Test-Suite/tests/draft4/additionalProperties.json
vendored
Executable file
@ -0,0 +1,63 @@
|
||||
[
|
||||
{
|
||||
"description":
|
||||
"additionalProperties being false does not allow other properties",
|
||||
"schema": {
|
||||
"properties": {"foo": {}, "bar": {}},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "no additional properties is valid",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an additional property is invalid",
|
||||
"data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-objects",
|
||||
"data": [1, 2, 3],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":
|
||||
"additionalProperties allows a schema which should validate",
|
||||
"schema": {
|
||||
"properties": {"foo": {}, "bar": {}},
|
||||
"additionalProperties": {"type": "boolean"}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "no additional properties is valid",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an additional valid property is valid",
|
||||
"data": {"foo" : 1, "bar" : 2, "quux" : true},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "an additional invalid property is invalid",
|
||||
"data": {"foo" : 1, "bar" : 2, "quux" : 12},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "additionalProperties are allowed by default",
|
||||
"schema": {"properties": {"foo": {}, "bar": {}}},
|
||||
"tests": [
|
||||
{
|
||||
"description": "additional properties are allowed",
|
||||
"data": {"foo": 1, "bar": 2, "quux": true},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
112
thirdparty/JSON-Schema-Test-Suite/tests/draft4/allOf.json
vendored
Executable file
112
thirdparty/JSON-Schema-Test-Suite/tests/draft4/allOf.json
vendored
Executable file
@ -0,0 +1,112 @@
|
||||
[
|
||||
{
|
||||
"description": "allOf",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"properties": {
|
||||
"bar": {"type": "integer"}
|
||||
},
|
||||
"required": ["bar"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"foo": {"type": "string"}
|
||||
},
|
||||
"required": ["foo"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "allOf",
|
||||
"data": {"foo": "baz", "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch second",
|
||||
"data": {"foo": "baz"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "mismatch first",
|
||||
"data": {"bar": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "wrong type",
|
||||
"data": {"foo": "baz", "bar": "quux"},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "allOf with base schema",
|
||||
"schema": {
|
||||
"properties": {"bar": {"type": "integer"}},
|
||||
"required": ["bar"],
|
||||
"allOf" : [
|
||||
{
|
||||
"properties": {
|
||||
"foo": {"type": "string"}
|
||||
},
|
||||
"required": ["foo"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"baz": {"type": "null"}
|
||||
},
|
||||
"required": ["baz"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid",
|
||||
"data": {"foo": "quux", "bar": 2, "baz": null},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch base schema",
|
||||
"data": {"foo": "quux", "baz": null},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "mismatch first allOf",
|
||||
"data": {"bar": 2, "baz": null},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "mismatch second allOf",
|
||||
"data": {"foo": "quux", "bar": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "mismatch both",
|
||||
"data": {"bar": 2},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "allOf simple types",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{"maximum": 30},
|
||||
{"minimum": 20}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid",
|
||||
"data": 25,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "mismatch one",
|
||||
"data": 35,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
68
thirdparty/JSON-Schema-Test-Suite/tests/draft4/anyOf.json
vendored
Executable file
68
thirdparty/JSON-Schema-Test-Suite/tests/draft4/anyOf.json
vendored
Executable file
@ -0,0 +1,68 @@
|
||||
[
|
||||
{
|
||||
"description": "anyOf",
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"minimum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "first anyOf valid",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "second anyOf valid",
|
||||
"data": 2.5,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "both anyOf valid",
|
||||
"data": 3,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "neither anyOf valid",
|
||||
"data": 1.5,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "anyOf with base schema",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"anyOf" : [
|
||||
{
|
||||
"maxLength": 2
|
||||
},
|
||||
{
|
||||
"minLength": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "mismatch base schema",
|
||||
"data": 3,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "one anyOf valid",
|
||||
"data": "foobar",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "both anyOf invalid",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
32
thirdparty/JSON-Schema-Test-Suite/tests/draft4/definitions.json
vendored
Executable file
32
thirdparty/JSON-Schema-Test-Suite/tests/draft4/definitions.json
vendored
Executable file
@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"description": "valid definition",
|
||||
"schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid definition schema",
|
||||
"data": {
|
||||
"definitions": {
|
||||
"foo": {"type": "integer"}
|
||||
}
|
||||
},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "invalid definition",
|
||||
"schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
|
||||
"tests": [
|
||||
{
|
||||
"description": "invalid definition schema",
|
||||
"data": {
|
||||
"definitions": {
|
||||
"foo": {"type": 1}
|
||||
}
|
||||
},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
113
thirdparty/JSON-Schema-Test-Suite/tests/draft4/dependencies.json
vendored
Executable file
113
thirdparty/JSON-Schema-Test-Suite/tests/draft4/dependencies.json
vendored
Executable file
@ -0,0 +1,113 @@
|
||||
[
|
||||
{
|
||||
"description": "dependencies",
|
||||
"schema": {
|
||||
"dependencies": {"bar": ["foo"]}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "neither",
|
||||
"data": {},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "nondependant",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "with dependency",
|
||||
"data": {"foo": 1, "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "missing dependency",
|
||||
"data": {"bar": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-objects",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple dependencies",
|
||||
"schema": {
|
||||
"dependencies": {"quux": ["foo", "bar"]}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "neither",
|
||||
"data": {},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "nondependants",
|
||||
"data": {"foo": 1, "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "with dependencies",
|
||||
"data": {"foo": 1, "bar": 2, "quux": 3},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "missing dependency",
|
||||
"data": {"foo": 1, "quux": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "missing other dependency",
|
||||
"data": {"bar": 1, "quux": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "missing both dependencies",
|
||||
"data": {"quux": 1},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "multiple dependencies subschema",
|
||||
"schema": {
|
||||
"dependencies": {
|
||||
"bar": {
|
||||
"properties": {
|
||||
"foo": {"type": "integer"},
|
||||
"bar": {"type": "integer"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid",
|
||||
"data": {"foo": 1, "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "no dependency",
|
||||
"data": {"foo": "quux"},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "wrong type",
|
||||
"data": {"foo": "quux", "bar": 2},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "wrong type other",
|
||||
"data": {"foo": 2, "bar": "quux"},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "wrong type both",
|
||||
"data": {"foo": "quux", "bar": "quux"},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
39
thirdparty/JSON-Schema-Test-Suite/tests/draft4/enum.json
vendored
Executable file
39
thirdparty/JSON-Schema-Test-Suite/tests/draft4/enum.json
vendored
Executable file
@ -0,0 +1,39 @@
|
||||
[
|
||||
{
|
||||
"description": "simple enum validation",
|
||||
"schema": {"enum": [1, 2, 3]},
|
||||
"tests": [
|
||||
{
|
||||
"description": "one of the enum is valid",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "something else is invalid",
|
||||
"data": 4,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "heterogeneous enum validation",
|
||||
"schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
|
||||
"tests": [
|
||||
{
|
||||
"description": "one of the enum is valid",
|
||||
"data": [],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "something else is invalid",
|
||||
"data": null,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "objects are deep compared",
|
||||
"data": {"foo": false},
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
46
thirdparty/JSON-Schema-Test-Suite/tests/draft4/items.json
vendored
Executable file
46
thirdparty/JSON-Schema-Test-Suite/tests/draft4/items.json
vendored
Executable file
@ -0,0 +1,46 @@
|
||||
[
|
||||
{
|
||||
"description": "a schema given for items",
|
||||
"schema": {
|
||||
"items": {"type": "integer"}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid items",
|
||||
"data": [ 1, 2, 3 ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "wrong type of items",
|
||||
"data": [1, "x"],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-arrays",
|
||||
"data": {"foo" : "bar"},
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "an array of schemas for items",
|
||||
"schema": {
|
||||
"items": [
|
||||
{"type": "integer"},
|
||||
{"type": "string"}
|
||||
]
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "correct types",
|
||||
"data": [ 1, "foo" ],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "wrong types",
|
||||
"data": [ "foo", 1 ],
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxItems.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxItems.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "maxItems validation",
|
||||
"schema": {"maxItems": 2},
|
||||
"tests": [
|
||||
{
|
||||
"description": "shorter is valid",
|
||||
"data": [1],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": [1, 2],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too long is invalid",
|
||||
"data": [1, 2, 3],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-arrays",
|
||||
"data": "foobar",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxLength.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxLength.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "maxLength validation",
|
||||
"schema": {"maxLength": 2},
|
||||
"tests": [
|
||||
{
|
||||
"description": "shorter is valid",
|
||||
"data": "f",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": "fo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too long is invalid",
|
||||
"data": "foo",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-strings",
|
||||
"data": 10,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxProperties.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxProperties.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "maxProperties validation",
|
||||
"schema": {"maxProperties": 2},
|
||||
"tests": [
|
||||
{
|
||||
"description": "shorter is valid",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": {"foo": 1, "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too long is invalid",
|
||||
"data": {"foo": 1, "bar": 2, "baz": 3},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-objects",
|
||||
"data": "foobar",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
42
thirdparty/JSON-Schema-Test-Suite/tests/draft4/maximum.json
vendored
Executable file
42
thirdparty/JSON-Schema-Test-Suite/tests/draft4/maximum.json
vendored
Executable file
@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"description": "maximum validation",
|
||||
"schema": {"maximum": 3.0},
|
||||
"tests": [
|
||||
{
|
||||
"description": "below the maximum is valid",
|
||||
"data": 2.6,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "above the maximum is invalid",
|
||||
"data": 3.5,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-numbers",
|
||||
"data": "x",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "exclusiveMaximum validation",
|
||||
"schema": {
|
||||
"maximum": 3.0,
|
||||
"exclusiveMaximum": true
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "below the maximum is still valid",
|
||||
"data": 2.2,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "boundary point is invalid",
|
||||
"data": 3.0,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/minItems.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/minItems.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "minItems validation",
|
||||
"schema": {"minItems": 1},
|
||||
"tests": [
|
||||
{
|
||||
"description": "longer is valid",
|
||||
"data": [1, 2],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": [1],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too short is invalid",
|
||||
"data": [],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-arrays",
|
||||
"data": "",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/minLength.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/minLength.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "minLength validation",
|
||||
"schema": {"minLength": 2},
|
||||
"tests": [
|
||||
{
|
||||
"description": "longer is valid",
|
||||
"data": "foo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": "fo",
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too short is invalid",
|
||||
"data": "f",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-strings",
|
||||
"data": 1,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/minProperties.json
vendored
Executable file
28
thirdparty/JSON-Schema-Test-Suite/tests/draft4/minProperties.json
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"description": "minProperties validation",
|
||||
"schema": {"minProperties": 1},
|
||||
"tests": [
|
||||
{
|
||||
"description": "longer is valid",
|
||||
"data": {"foo": 1, "bar": 2},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "exact length is valid",
|
||||
"data": {"foo": 1},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "too short is invalid",
|
||||
"data": {},
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-objects",
|
||||
"data": "",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
42
thirdparty/JSON-Schema-Test-Suite/tests/draft4/minimum.json
vendored
Executable file
42
thirdparty/JSON-Schema-Test-Suite/tests/draft4/minimum.json
vendored
Executable file
@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"description": "minimum validation",
|
||||
"schema": {"minimum": 1.1},
|
||||
"tests": [
|
||||
{
|
||||
"description": "above the minimum is valid",
|
||||
"data": 2.6,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "below the minimum is invalid",
|
||||
"data": 0.6,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "ignores non-numbers",
|
||||
"data": "x",
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "exclusiveMinimum validation",
|
||||
"schema": {
|
||||
"minimum": 1.1,
|
||||
"exclusiveMinimum": true
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "above the minimum is still valid",
|
||||
"data": 1.2,
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "boundary point is invalid",
|
||||
"data": 1.1,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user