Codebase list golang-github-xeipuuv-gojsonschema / 3bf7f0e
New upstream version 1.2.0 Shengjing Zhu 4 years ago
518 changed file(s) with 13886 addition(s) and 1708 deletion(s). Raw diff Collapse all Expand all
00 *.sw[nop]
1 *.iml
2 .vscode/
00 language: go
11 go:
2 - 1.3
2 - "1.11"
3 - "1.12"
4 - "1.13"
35 before_install:
4 - go get github.com/sigu-399/gojsonreference
5 - go get github.com/sigu-399/gojsonpointer
6 - go get github.com/xeipuuv/gojsonreference
7 - go get github.com/xeipuuv/gojsonpointer
68 - go get github.com/stretchr/testify/assert
0 [![GoDoc](https://godoc.org/github.com/xeipuuv/gojsonschema?status.svg)](https://godoc.org/github.com/xeipuuv/gojsonschema)
01 [![Build Status](https://travis-ci.org/xeipuuv/gojsonschema.svg)](https://travis-ci.org/xeipuuv/gojsonschema)
2 [![Go Report Card](https://goreportcard.com/badge/github.com/xeipuuv/gojsonschema)](https://goreportcard.com/report/github.com/xeipuuv/gojsonschema)
13
24 # gojsonschema
35
46 ## Description
57
6 An implementation of JSON Schema, based on IETF's draft v4 - Go language
8 An implementation of JSON Schema for the Go programming language. Supports draft-04, draft-06 and draft-07.
79
810 References :
911
5355 fmt.Printf("- %s\n", desc)
5456 }
5557 }
56
5758 }
5859
5960
147148 }
148149 ```
149150
151
152 ## Loading local schemas
153
154 By default `file` and `http(s)` references to external schemas are loaded automatically via the file system or via http(s). An external schema can also be loaded using a `SchemaLoader`.
155
156 ```go
157 sl := gojsonschema.NewSchemaLoader()
158 loader1 := gojsonschema.NewStringLoader(`{ "type" : "string" }`)
159 err := sl.AddSchema("http://some_host.com/string.json", loader1)
160 ```
161
162 Alternatively if your schema already has an `$id` you can use the `AddSchemas` function
163 ```go
164 loader2 := gojsonschema.NewStringLoader(`{
165 "$id" : "http://some_host.com/maxlength.json",
166 "maxLength" : 5
167 }`)
168 err = sl.AddSchemas(loader2)
169 ```
170
171 The main schema should be passed to the `Compile` function. This main schema can then directly reference the added schemas without needing to download them.
172 ```go
173 loader3 := gojsonschema.NewStringLoader(`{
174 "$id" : "http://some_host.com/main.json",
175 "allOf" : [
176 { "$ref" : "http://some_host.com/string.json" },
177 { "$ref" : "http://some_host.com/maxlength.json" }
178 ]
179 }`)
180
181 schema, err := sl.Compile(loader3)
182
183 documentLoader := gojsonschema.NewStringLoader(`"hello world"`)
184
185 result, err := schema.Validate(documentLoader)
186 ```
187
188 It's also possible to pass a `ReferenceLoader` to the `Compile` function that references a loaded schema.
189
190 ```go
191 err = sl.AddSchemas(loader3)
192 schema, err := sl.Compile(gojsonschema.NewReferenceLoader("http://some_host.com/main.json"))
193 ```
194
195 Schemas added by `AddSchema` and `AddSchemas` are only validated when the entire schema is compiled, unless meta-schema validation is used.
196
197 ## Using a specific draft
198 By default `gojsonschema` will try to detect the draft of a schema by using the `$schema` keyword and parse it in a strict draft-04, draft-06 or draft-07 mode. If `$schema` is missing, or the draft version is not explicitely set, a hybrid mode is used which merges together functionality of all drafts into one mode.
199
200 Autodectection can be turned off with the `AutoDetect` property. Specific draft versions can be specified with the `Draft` property.
201
202 ```go
203 sl := gojsonschema.NewSchemaLoader()
204 sl.Draft = gojsonschema.Draft7
205 sl.AutoDetect = false
206 ```
207
208 If autodetection is on (default), a draft-07 schema can savely reference draft-04 schemas and vice-versa, as long as `$schema` is specified in all schemas.
209
210 ## Meta-schema validation
211 Schemas that are added using the `AddSchema`, `AddSchemas` and `Compile` can be validated against their meta-schema by setting the `Validate` property.
212
213 The following example will produce an error as `multipleOf` must be a number. If `Validate` is off (default), this error is only returned at the `Compile` step.
214
215 ```go
216 sl := gojsonschema.NewSchemaLoader()
217 sl.Validate = true
218 err := sl.AddSchemas(gojsonschema.NewStringLoader(`{
219 $id" : "http://some_host.com/invalid.json",
220 "$schema": "http://json-schema.org/draft-07/schema#",
221 "multipleOf" : true
222 }`))
223 ```
224 ```
225 ```
226
227 Errors returned by meta-schema validation are more readable and contain more information, which helps significantly if you are developing a schema.
228
229 Meta-schema validation also works with a custom `$schema`. In case `$schema` is missing, or `AutoDetect` is set to `false`, the meta-schema of the used draft is used.
230
231
150232 ## Working with Errors
151233
152234 The library handles string error codes which you can customize by creating your own gojsonschema.locale and setting it
154236 gojsonschema.Locale = YourCustomLocale{}
155237 ```
156238
157 However, each error contains additional contextual information.
239 However, each error contains additional contextual information.
240
241 Newer versions of `gojsonschema` may have new additional errors, so code that uses a custom locale will need to be updated when this happens.
158242
159243 **err.Type()**: *string* Returns the "type" of error that occurred. Note you can also type check. See below
160244
168252 "number_not": NumberNotError
169253 "missing_dependency": MissingDependencyError
170254 "internal": InternalError
255 "const": ConstEror
171256 "enum": EnumError
172257 "array_no_additional_items": ArrayNoAdditionalItemsError
173258 "array_min_items": ArrayMinItemsError
174259 "array_max_items": ArrayMaxItemsError
175260 "unique": ItemsMustBeUniqueError
261 "contains" : ArrayContainsError
176262 "array_min_properties": ArrayMinPropertiesError
177263 "array_max_properties": ArrayMaxPropertiesError
178264 "additional_property_not_allowed": AdditionalPropertyNotAllowedError
179265 "invalid_property_pattern": InvalidPropertyPatternError
266 "invalid_property_name": InvalidPropertyNameError
180267 "string_gte": StringLengthGTEError
181268 "string_lte": StringLengthLTEError
182269 "pattern": DoesNotMatchPatternError
185272 "number_gt": NumberGTError
186273 "number_lte": NumberLTEError
187274 "number_lt": NumberLTError
275 "condition_then" : ConditionThenError
276 "condition_else" : ConditionElseError
188277
189278 **err.Value()**: *interface{}* Returns the value given
190279
191 **err.Context()**: *gojsonschema.jsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName
280 **err.Context()**: *gojsonschema.JsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName
192281
193282 **err.Field()**: *string* Returns the fieldname in the format firstName, or for embedded properties, person.firstName. This returns the same as the String() method on *err.Context()* but removes the (root). prefix.
194283
195284 **err.Description()**: *string* The error description. This is based on the locale you are using. See the beginning of this section for overwriting the locale with a custom implementation.
196285
286 **err.DescriptionFormat()**: *string* The error description format. This is relevant if you are adding custom validation errors afterwards to the result.
287
197288 **err.Details()**: *gojsonschema.ErrorDetails* Returns a map[string]interface{} of additional error details specific to the error. For example, GTE errors will have a "min" value, LTE will have a "max" value. See errors.go for a full description of all the error details. Every error always contains a "field" key that holds the value of *err.Field()*
198289
199290 Note in most cases, the err.Details() will be used to generate replacement strings in your locales, and not used directly. These strings follow the text/template format i.e.
201292 {{.field}} must be greater than or equal to {{.min}}
202293 ```
203294
295 The library allows you to specify custom template functions, should you require more complex error message handling.
296 ```go
297 gojsonschema.ErrorTemplateFuncs = map[string]interface{}{
298 "allcaps": func(s string) string {
299 return strings.ToUpper(s)
300 },
301 }
302 ```
303
304 Given the above definition, you can use the custom function `"allcaps"` in your localization templates:
305 ```
306 {{allcaps .field}} must be greater than or equal to {{.min}}
307 ```
308
309 The above error message would then be rendered with the `field` value in capital letters. For example:
310 ```
311 "PASSWORD must be greater than or equal to 8"
312 ```
313
314 Learn more about what types of template functions you can use in `ErrorTemplateFuncs` by referring to Go's [text/template FuncMap](https://golang.org/pkg/text/template/#FuncMap) type.
315
204316 ## Formats
205 JSON Schema allows for optional "format" property to validate strings against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this:
317 JSON Schema allows for optional "format" property to validate instances against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this:
318
206319 ````json
207320 {"type": "string", "format": "email"}
208321 ````
209 Available formats: date-time, hostname, email, ipv4, ipv6, uri.
322
323 Not all formats defined in draft-07 are available. Implemented formats are:
324
325 * `date`
326 * `time`
327 * `date-time`
328 * `hostname`. Subdomains that start with a number are also supported, but this means that it doesn't strictly follow [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5) and has the implication that ipv4 addresses are also recognized as valid hostnames.
329 * `email`. Go's email parser deviates slightly from [RFC5322](https://tools.ietf.org/html/rfc5322). Includes unicode support.
330 * `idn-email`. Same caveat as `email`.
331 * `ipv4`
332 * `ipv6`
333 * `uri`. Includes unicode support.
334 * `uri-reference`. Includes unicode support.
335 * `iri`
336 * `iri-reference`
337 * `uri-template`
338 * `uuid`
339 * `regex`. Go uses the [RE2](https://github.com/google/re2/wiki/Syntax) engine and is not [ECMA262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) compatible.
340 * `json-pointer`
341 * `relative-json-pointer`
342
343 `email`, `uri` and `uri-reference` use the same validation code as their unicode counterparts `idn-email`, `iri` and `iri-reference`. If you rely on unicode support you should use the specific
344 unicode enabled formats for the sake of interoperability as other implementations might not support unicode in the regular formats.
345
346 The validation code for `uri`, `idn-email` and their relatives use mostly standard library code.
210347
211348 For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this:
212349
215352 type RoleFormatChecker struct {}
216353
217354 // Ensure it meets the gojsonschema.FormatChecker interface
218 func (f RoleFormatChecker) IsFormat(input string) bool {
219 return strings.HasPrefix("ROLE_", input)
355 func (f RoleFormatChecker) IsFormat(input interface{}) bool {
356
357 asString, ok := input.(string)
358 if ok == false {
359 return false
360 }
361
362 return strings.HasPrefix("ROLE_", asString)
220363 }
221364
222365 // Add it to the library
228371 {"type": "string", "format": "role"}
229372 ````
230373
374 Another example would be to check if the provided integer matches an id on database:
375
376 JSON schema:
377 ```json
378 {"type": "integer", "format": "ValidUserId"}
379 ```
380
381 ```go
382 // Define the format checker
383 type ValidUserIdFormatChecker struct {}
384
385 // Ensure it meets the gojsonschema.FormatChecker interface
386 func (f ValidUserIdFormatChecker) IsFormat(input interface{}) bool {
387
388 asFloat64, ok := input.(float64) // Numbers are always float64 here
389 if ok == false {
390 return false
391 }
392
393 // XXX
394 // do the magic on the database looking for the int(asFloat64)
395
396 return true
397 }
398
399 // Add it to the library
400 gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{})
401 ````
402
403 Formats can also be removed, for example if you want to override one of the formats that is defined by default.
404
405 ```go
406 gojsonschema.FormatCheckers.Remove("hostname")
407 ```
408
409
410 ## Additional custom validation
411 After the validation has run and you have the results, you may add additional
412 errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead
413 of having to add special exceptions for your own errors. Below is an example.
414
415 ```go
416 type AnswerInvalidError struct {
417 gojsonschema.ResultErrorFields
418 }
419
420 func newAnswerInvalidError(context *gojsonschema.JsonContext, value interface{}, details gojsonschema.ErrorDetails) *AnswerInvalidError {
421 err := AnswerInvalidError{}
422 err.SetContext(context)
423 err.SetType("custom_invalid_error")
424 // it is important to use SetDescriptionFormat() as this is used to call SetDescription() after it has been parsed
425 // using the description of err will be overridden by this.
426 err.SetDescriptionFormat("Answer to the Ultimate Question of Life, the Universe, and Everything is {{.answer}}")
427 err.SetValue(value)
428 err.SetDetails(details)
429
430 return &err
431 }
432
433 func main() {
434 // ...
435 schema, err := gojsonschema.NewSchema(schemaLoader)
436 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
437
438 if true { // some validation
439 jsonContext := gojsonschema.NewJsonContext("question", nil)
440 errDetail := gojsonschema.ErrorDetails{
441 "answer": 42,
442 }
443 result.AddError(
444 newAnswerInvalidError(
445 gojsonschema.NewJsonContext("answer", jsonContext),
446 52,
447 errDetail,
448 ),
449 errDetail,
450 )
451 }
452
453 return result, err
454
455 }
456 ```
457
458 This is especially useful if you want to add validation beyond what the
459 json schema drafts can provide such business specific logic.
460
231461 ## Uses
232462
233463 gojsonschema uses the following test suite :
0 // Copyright 2018 johandorland ( https://github.com/johandorland )
1 //
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package gojsonschema
15
16 import (
17 "errors"
18 "math"
19 "reflect"
20
21 "github.com/xeipuuv/gojsonreference"
22 )
23
24 // Draft is a JSON-schema draft version
25 type Draft int
26
27 // Supported Draft versions
28 const (
29 Draft4 Draft = 4
30 Draft6 Draft = 6
31 Draft7 Draft = 7
32 Hybrid Draft = math.MaxInt32
33 )
34
35 type draftConfig struct {
36 Version Draft
37 MetaSchemaURL string
38 MetaSchema string
39 }
40 type draftConfigs []draftConfig
41
42 var drafts draftConfigs
43
44 func init() {
45 drafts = []draftConfig{
46 {
47 Version: Draft4,
48 MetaSchemaURL: "http://json-schema.org/draft-04/schema",
49 MetaSchema: `{"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"},"$schema":{"type":"string"},"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}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"dependencies":{"exclusiveMaximum":["maximum"],"exclusiveMinimum":["minimum"]},"default":{}}`,
50 },
51 {
52 Version: Draft6,
53 MetaSchemaURL: "http://json-schema.org/draft-06/schema",
54 MetaSchema: `{"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"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"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}`,
55 },
56 {
57 Version: Draft7,
58 MetaSchemaURL: "http://json-schema.org/draft-07/schema",
59 MetaSchema: `{"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}`,
60 },
61 }
62 }
63
64 func (dc draftConfigs) GetMetaSchema(url string) string {
65 for _, config := range dc {
66 if config.MetaSchemaURL == url {
67 return config.MetaSchema
68 }
69 }
70 return ""
71 }
72 func (dc draftConfigs) GetDraftVersion(url string) *Draft {
73 for _, config := range dc {
74 if config.MetaSchemaURL == url {
75 return &config.Version
76 }
77 }
78 return nil
79 }
80 func (dc draftConfigs) GetSchemaURL(draft Draft) string {
81 for _, config := range dc {
82 if config.Version == draft {
83 return config.MetaSchemaURL
84 }
85 }
86 return ""
87 }
88
89 func parseSchemaURL(documentNode interface{}) (string, *Draft, error) {
90
91 if isKind(documentNode, reflect.Bool) {
92 return "", nil, nil
93 }
94
95 if !isKind(documentNode, reflect.Map) {
96 return "", nil, errors.New("schema is invalid")
97 }
98
99 m := documentNode.(map[string]interface{})
100
101 if existsMapKey(m, KEY_SCHEMA) {
102 if !isKind(m[KEY_SCHEMA], reflect.String) {
103 return "", nil, errors.New(formatErrorDescription(
104 Locale.MustBeOfType(),
105 ErrorDetails{
106 "key": KEY_SCHEMA,
107 "type": TYPE_STRING,
108 },
109 ))
110 }
111
112 schemaReference, err := gojsonreference.NewJsonReference(m[KEY_SCHEMA].(string))
113
114 if err != nil {
115 return "", nil, err
116 }
117
118 schema := schemaReference.String()
119
120 return schema, drafts.GetDraftVersion(schema), nil
121 }
122
123 return "", nil, nil
124 }
55 "text/template"
66 )
77
8 var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"),sync.RWMutex{}}
8 var errorTemplates = errorTemplate{template.New("errors-new"), sync.RWMutex{}}
99
1010 // template.Template is not thread-safe for writing, so some locking is done
1111 // sync.RWMutex is used for efficiently locking when new templates are created
1515 }
1616
1717 type (
18 // RequiredError. ErrorDetails: property string
18
19 // FalseError. ErrorDetails: -
20 FalseError struct {
21 ResultErrorFields
22 }
23
24 // RequiredError indicates that a required field is missing
25 // ErrorDetails: property string
1926 RequiredError struct {
2027 ResultErrorFields
2128 }
2229
23 // InvalidTypeError. ErrorDetails: expected, given
30 // InvalidTypeError indicates that a field has the incorrect type
31 // ErrorDetails: expected, given
2432 InvalidTypeError struct {
2533 ResultErrorFields
2634 }
2735
28 // NumberAnyOfError. ErrorDetails: -
36 // NumberAnyOfError is produced in case of a failing "anyOf" validation
37 // ErrorDetails: -
2938 NumberAnyOfError struct {
3039 ResultErrorFields
3140 }
3241
33 // NumberOneOfError. ErrorDetails: -
42 // NumberOneOfError is produced in case of a failing "oneOf" validation
43 // ErrorDetails: -
3444 NumberOneOfError struct {
3545 ResultErrorFields
3646 }
3747
38 // NumberAllOfError. ErrorDetails: -
48 // NumberAllOfError is produced in case of a failing "allOf" validation
49 // ErrorDetails: -
3950 NumberAllOfError struct {
4051 ResultErrorFields
4152 }
4253
43 // NumberNotError. ErrorDetails: -
54 // NumberNotError is produced if a "not" validation failed
55 // ErrorDetails: -
4456 NumberNotError struct {
4557 ResultErrorFields
4658 }
4759
48 // MissingDependencyError. ErrorDetails: dependency
60 // MissingDependencyError is produced in case of a "missing dependency" problem
61 // ErrorDetails: dependency
4962 MissingDependencyError struct {
5063 ResultErrorFields
5164 }
5265
53 // InternalError. ErrorDetails: error
66 // InternalError indicates an internal error
67 // ErrorDetails: error
5468 InternalError struct {
5569 ResultErrorFields
5670 }
5771
58 // EnumError. ErrorDetails: allowed
72 // ConstError indicates a const error
73 // ErrorDetails: allowed
74 ConstError struct {
75 ResultErrorFields
76 }
77
78 // EnumError indicates an enum error
79 // ErrorDetails: allowed
5980 EnumError struct {
6081 ResultErrorFields
6182 }
6283
63 // ArrayNoAdditionalItemsError. ErrorDetails: -
84 // ArrayNoAdditionalItemsError is produced if additional items were found, but not allowed
85 // ErrorDetails: -
6486 ArrayNoAdditionalItemsError struct {
6587 ResultErrorFields
6688 }
6789
68 // ArrayMinItemsError. ErrorDetails: min
90 // ArrayMinItemsError is produced if an array contains less items than the allowed minimum
91 // ErrorDetails: min
6992 ArrayMinItemsError struct {
7093 ResultErrorFields
7194 }
7295
73 // ArrayMaxItemsError. ErrorDetails: max
96 // ArrayMaxItemsError is produced if an array contains more items than the allowed maximum
97 // ErrorDetails: max
7498 ArrayMaxItemsError struct {
7599 ResultErrorFields
76100 }
77101
78 // ItemsMustBeUniqueError. ErrorDetails: type
102 // ItemsMustBeUniqueError is produced if an array requires unique items, but contains non-unique items
103 // ErrorDetails: type, i, j
79104 ItemsMustBeUniqueError struct {
80105 ResultErrorFields
81106 }
82107
83 // ArrayMinPropertiesError. ErrorDetails: min
108 // ArrayContainsError is produced if an array contains invalid items
109 // ErrorDetails:
110 ArrayContainsError struct {
111 ResultErrorFields
112 }
113
114 // ArrayMinPropertiesError is produced if an object contains less properties than the allowed minimum
115 // ErrorDetails: min
84116 ArrayMinPropertiesError struct {
85117 ResultErrorFields
86118 }
87119
88 // ArrayMaxPropertiesError. ErrorDetails: max
120 // ArrayMaxPropertiesError is produced if an object contains more properties than the allowed maximum
121 // ErrorDetails: max
89122 ArrayMaxPropertiesError struct {
90123 ResultErrorFields
91124 }
92125
93 // AdditionalPropertyNotAllowedError. ErrorDetails: property
126 // AdditionalPropertyNotAllowedError is produced if an object has additional properties, but not allowed
127 // ErrorDetails: property
94128 AdditionalPropertyNotAllowedError struct {
95129 ResultErrorFields
96130 }
97131
98 // InvalidPropertyPatternError. ErrorDetails: property, pattern
132 // InvalidPropertyPatternError is produced if an pattern was found
133 // ErrorDetails: property, pattern
99134 InvalidPropertyPatternError struct {
100135 ResultErrorFields
101136 }
102137
103 // StringLengthGTEError. ErrorDetails: min
138 // InvalidPropertyNameError is produced if an invalid-named property was found
139 // ErrorDetails: property
140 InvalidPropertyNameError struct {
141 ResultErrorFields
142 }
143
144 // StringLengthGTEError is produced if a string is shorter than the minimum required length
145 // ErrorDetails: min
104146 StringLengthGTEError struct {
105147 ResultErrorFields
106148 }
107149
108 // StringLengthLTEError. ErrorDetails: max
150 // StringLengthLTEError is produced if a string is longer than the maximum allowed length
151 // ErrorDetails: max
109152 StringLengthLTEError struct {
110153 ResultErrorFields
111154 }
112155
113 // DoesNotMatchPatternError. ErrorDetails: pattern
156 // DoesNotMatchPatternError is produced if a string does not match the defined pattern
157 // ErrorDetails: pattern
114158 DoesNotMatchPatternError struct {
115159 ResultErrorFields
116160 }
117161
118 // DoesNotMatchFormatError. ErrorDetails: format
162 // DoesNotMatchFormatError is produced if a string does not match the defined format
163 // ErrorDetails: format
119164 DoesNotMatchFormatError struct {
120165 ResultErrorFields
121166 }
122167
123 // MultipleOfError. ErrorDetails: multiple
168 // MultipleOfError is produced if a number is not a multiple of the defined multipleOf
169 // ErrorDetails: multiple
124170 MultipleOfError struct {
125171 ResultErrorFields
126172 }
127173
128 // NumberGTEError. ErrorDetails: min
174 // NumberGTEError is produced if a number is lower than the allowed minimum
175 // ErrorDetails: min
129176 NumberGTEError struct {
130177 ResultErrorFields
131178 }
132179
133 // NumberGTError. ErrorDetails: min
180 // NumberGTError is produced if a number is lower than, or equal to the specified minimum, and exclusiveMinimum is set
181 // ErrorDetails: min
134182 NumberGTError struct {
135183 ResultErrorFields
136184 }
137185
138 // NumberLTEError. ErrorDetails: max
186 // NumberLTEError is produced if a number is higher than the allowed maximum
187 // ErrorDetails: max
139188 NumberLTEError struct {
140189 ResultErrorFields
141190 }
142191
143 // NumberLTError. ErrorDetails: max
192 // NumberLTError is produced if a number is higher than, or equal to the specified maximum, and exclusiveMaximum is set
193 // ErrorDetails: max
144194 NumberLTError struct {
145195 ResultErrorFields
146196 }
197
198 // ConditionThenError is produced if a condition's "then" validation is invalid
199 // ErrorDetails: -
200 ConditionThenError struct {
201 ResultErrorFields
202 }
203
204 // ConditionElseError is produced if a condition's "else" condition is invalid
205 // ErrorDetails: -
206 ConditionElseError struct {
207 ResultErrorFields
208 }
147209 )
148210
149211 // newError takes a ResultError type and sets the type, context, description, details, value, and field
150 func newError(err ResultError, context *jsonContext, value interface{}, locale locale, details ErrorDetails) {
212 func newError(err ResultError, context *JsonContext, value interface{}, locale locale, details ErrorDetails) {
151213 var t string
152214 var d string
153215 switch err.(type) {
216 case *FalseError:
217 t = "false"
218 d = locale.False()
154219 case *RequiredError:
155220 t = "required"
156221 d = locale.Required()
175240 case *InternalError:
176241 t = "internal"
177242 d = locale.Internal()
243 case *ConstError:
244 t = "const"
245 d = locale.Const()
178246 case *EnumError:
179247 t = "enum"
180248 d = locale.Enum()
190258 case *ItemsMustBeUniqueError:
191259 t = "unique"
192260 d = locale.Unique()
261 case *ArrayContainsError:
262 t = "contains"
263 d = locale.ArrayContains()
193264 case *ArrayMinPropertiesError:
194265 t = "array_min_properties"
195266 d = locale.ArrayMinProperties()
202273 case *InvalidPropertyPatternError:
203274 t = "invalid_property_pattern"
204275 d = locale.InvalidPropertyPattern()
276 case *InvalidPropertyNameError:
277 t = "invalid_property_name"
278 d = locale.InvalidPropertyName()
205279 case *StringLengthGTEError:
206280 t = "string_gte"
207281 d = locale.StringGTE()
229303 case *NumberLTError:
230304 t = "number_lt"
231305 d = locale.NumberLT()
306 case *ConditionThenError:
307 t = "condition_then"
308 d = locale.ConditionThen()
309 case *ConditionElseError:
310 t = "condition_else"
311 d = locale.ConditionElse()
232312 }
233313
234314 err.SetType(t)
235315 err.SetContext(context)
236316 err.SetValue(value)
237317 err.SetDetails(details)
318 err.SetDescriptionFormat(d)
238319 details["field"] = err.Field()
239 err.SetDescription(formatErrorDescription(d, details))
320
321 if _, exists := details["context"]; !exists && context != nil {
322 details["context"] = context.String()
323 }
324
325 err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
240326 }
241327
242328 // formatErrorDescription takes a string in the default text/template
256342 errorTemplates.Lock()
257343 tpl = errorTemplates.New(s)
258344
345 if ErrorTemplateFuncs != nil {
346 tpl.Funcs(ErrorTemplateFuncs)
347 }
348
259349 tpl, err = tpl.Parse(s)
260350 errorTemplates.Unlock()
261351
11
22 import (
33 "net"
4 "net/mail"
45 "net/url"
5 "reflect"
66 "regexp"
77 "strings"
8 "sync"
89 "time"
910 )
1011
1112 type (
1213 // FormatChecker is the interface all formatters added to FormatCheckerChain must implement
1314 FormatChecker interface {
14 IsFormat(input string) bool
15 // IsFormat checks if input has the correct format and type
16 IsFormat(input interface{}) bool
1517 }
1618
1719 // FormatCheckerChain holds the formatters
1921 formatters map[string]FormatChecker
2022 }
2123
22 // EmailFormatter verifies email address formats
24 // EmailFormatChecker verifies email address formats
2325 EmailFormatChecker struct{}
2426
25 // IPV4FormatChecker verifies IP addresses in the ipv4 format
27 // IPV4FormatChecker verifies IP addresses in the IPv4 format
2628 IPV4FormatChecker struct{}
2729
28 // IPV6FormatChecker verifies IP addresses in the ipv6 format
30 // IPV6FormatChecker verifies IP addresses in the IPv6 format
2931 IPV6FormatChecker struct{}
3032
3133 // DateTimeFormatChecker verifies date/time formats per RFC3339 5.6
5153 // http://tools.ietf.org/html/rfc3339#section-5.6
5254 DateTimeFormatChecker struct{}
5355
54 // URIFormatCheckers validates a URI with a valid Scheme per RFC3986
56 // DateFormatChecker verifies date formats
57 //
58 // Valid format:
59 // Full Date: YYYY-MM-DD
60 //
61 // Where
62 // YYYY = 4DIGIT year
63 // MM = 2DIGIT month ; 01-12
64 // DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year
65 DateFormatChecker struct{}
66
67 // TimeFormatChecker verifies time formats
68 //
69 // Valid formats:
70 // Partial Time: HH:MM:SS
71 // Full Time: HH:MM:SSZ-07:00
72 //
73 // Where
74 // HH = 2DIGIT hour ; 00-23
75 // MM = 2DIGIT ; 00-59
76 // SS = 2DIGIT ; 00-58, 00-60 based on leap second rules
77 // T = Literal
78 // Z = Literal
79 TimeFormatChecker struct{}
80
81 // URIFormatChecker validates a URI with a valid Scheme per RFC3986
5582 URIFormatChecker struct{}
83
84 // URIReferenceFormatChecker validates a URI or relative-reference per RFC3986
85 URIReferenceFormatChecker struct{}
86
87 // URITemplateFormatChecker validates a URI template per RFC6570
88 URITemplateFormatChecker struct{}
5689
5790 // HostnameFormatChecker validates a hostname is in the correct format
5891 HostnameFormatChecker struct{}
6295
6396 // RegexFormatChecker validates a regex is in the correct format
6497 RegexFormatChecker struct{}
98
99 // JSONPointerFormatChecker validates a JSON Pointer per RFC6901
100 JSONPointerFormatChecker struct{}
101
102 // RelativeJSONPointerFormatChecker validates a relative JSON Pointer is in the correct format
103 RelativeJSONPointerFormatChecker struct{}
65104 )
66105
67106 var (
68 // Formatters holds the valid formatters, and is a public variable
107 // FormatCheckers holds the valid formatters, and is a public variable
69108 // so library users can add custom formatters
70109 FormatCheckers = FormatCheckerChain{
71110 formatters: map[string]FormatChecker{
72 "date-time": DateTimeFormatChecker{},
73 "hostname": HostnameFormatChecker{},
74 "email": EmailFormatChecker{},
75 "ipv4": IPV4FormatChecker{},
76 "ipv6": IPV6FormatChecker{},
77 "uri": URIFormatChecker{},
78 "uuid": UUIDFormatChecker{},
79 "regex": RegexFormatChecker{},
111 "date": DateFormatChecker{},
112 "time": TimeFormatChecker{},
113 "date-time": DateTimeFormatChecker{},
114 "hostname": HostnameFormatChecker{},
115 "email": EmailFormatChecker{},
116 "idn-email": EmailFormatChecker{},
117 "ipv4": IPV4FormatChecker{},
118 "ipv6": IPV6FormatChecker{},
119 "uri": URIFormatChecker{},
120 "uri-reference": URIReferenceFormatChecker{},
121 "iri": URIFormatChecker{},
122 "iri-reference": URIReferenceFormatChecker{},
123 "uri-template": URITemplateFormatChecker{},
124 "uuid": UUIDFormatChecker{},
125 "regex": RegexFormatChecker{},
126 "json-pointer": JSONPointerFormatChecker{},
127 "relative-json-pointer": RelativeJSONPointerFormatChecker{},
80128 },
81129 }
82
83 // Regex credit: https://github.com/asaskevich/govalidator
84 rxEmail = regexp.MustCompile("^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$")
85130
86131 // Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname
87132 rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)
88133
134 // Use a regex to make sure curly brackets are balanced properly after validating it as a AURI
135 rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$")
136
89137 rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
138
139 rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$")
140
141 rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$")
142
143 lock = new(sync.RWMutex)
90144 )
91145
92146 // Add adds a FormatChecker to the FormatCheckerChain
93147 // The name used will be the value used for the format key in your json schema
94148 func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain {
149 lock.Lock()
95150 c.formatters[name] = f
151 lock.Unlock()
96152
97153 return c
98154 }
99155
100156 // Remove deletes a FormatChecker from the FormatCheckerChain (if it exists)
101157 func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain {
158 lock.Lock()
102159 delete(c.formatters, name)
160 lock.Unlock()
103161
104162 return c
105163 }
106164
107165 // Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name
108166 func (c *FormatCheckerChain) Has(name string) bool {
167 lock.RLock()
109168 _, ok := c.formatters[name]
169 lock.RUnlock()
110170
111171 return ok
112172 }
114174 // IsFormat will check an input against a FormatChecker with the given name
115175 // to see if it is the correct format
116176 func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool {
177 lock.RLock()
117178 f, ok := c.formatters[name]
118
119 if !ok {
120 return false
121 }
122
123 if !isKind(input, reflect.String) {
124 return false
125 }
126
127 inputString := input.(string)
128
129 return f.IsFormat(inputString)
130 }
131
132 func (f EmailFormatChecker) IsFormat(input string) bool {
133 return rxEmail.MatchString(input)
134 }
135
136 // Credit: https://github.com/asaskevich/govalidator
137 func (f IPV4FormatChecker) IsFormat(input string) bool {
138 ip := net.ParseIP(input)
139 return ip != nil && strings.Contains(input, ".")
140 }
141
142 // Credit: https://github.com/asaskevich/govalidator
143 func (f IPV6FormatChecker) IsFormat(input string) bool {
144 ip := net.ParseIP(input)
145 return ip != nil && strings.Contains(input, ":")
146 }
147
148 func (f DateTimeFormatChecker) IsFormat(input string) bool {
179 lock.RUnlock()
180
181 // If a format is unrecognized it should always pass validation
182 if !ok {
183 return true
184 }
185
186 return f.IsFormat(input)
187 }
188
189 // IsFormat checks if input is a correctly formatted e-mail address
190 func (f EmailFormatChecker) IsFormat(input interface{}) bool {
191 asString, ok := input.(string)
192 if !ok {
193 return false
194 }
195
196 _, err := mail.ParseAddress(asString)
197 return err == nil
198 }
199
200 // IsFormat checks if input is a correctly formatted IPv4-address
201 func (f IPV4FormatChecker) IsFormat(input interface{}) bool {
202 asString, ok := input.(string)
203 if !ok {
204 return false
205 }
206
207 // Credit: https://github.com/asaskevich/govalidator
208 ip := net.ParseIP(asString)
209 return ip != nil && strings.Contains(asString, ".")
210 }
211
212 // IsFormat checks if input is a correctly formatted IPv6=address
213 func (f IPV6FormatChecker) IsFormat(input interface{}) bool {
214 asString, ok := input.(string)
215 if !ok {
216 return false
217 }
218
219 // Credit: https://github.com/asaskevich/govalidator
220 ip := net.ParseIP(asString)
221 return ip != nil && strings.Contains(asString, ":")
222 }
223
224 // IsFormat checks if input is a correctly formatted date/time per RFC3339 5.6
225 func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {
226 asString, ok := input.(string)
227 if !ok {
228 return false
229 }
230
149231 formats := []string{
150232 "15:04:05",
151233 "15:04:05Z07:00",
155237 }
156238
157239 for _, format := range formats {
158 if _, err := time.Parse(format, input); err == nil {
240 if _, err := time.Parse(format, asString); err == nil {
159241 return true
160242 }
161243 }
163245 return false
164246 }
165247
166 func (f URIFormatChecker) IsFormat(input string) bool {
167 u, err := url.Parse(input)
248 // IsFormat checks if input is a correctly formatted date (YYYY-MM-DD)
249 func (f DateFormatChecker) IsFormat(input interface{}) bool {
250 asString, ok := input.(string)
251 if !ok {
252 return false
253 }
254 _, err := time.Parse("2006-01-02", asString)
255 return err == nil
256 }
257
258 // IsFormat checks if input correctly formatted time (HH:MM:SS or HH:MM:SSZ-07:00)
259 func (f TimeFormatChecker) IsFormat(input interface{}) bool {
260 asString, ok := input.(string)
261 if !ok {
262 return false
263 }
264
265 if _, err := time.Parse("15:04:05Z07:00", asString); err == nil {
266 return true
267 }
268
269 _, err := time.Parse("15:04:05", asString)
270 return err == nil
271 }
272
273 // IsFormat checks if input is correctly formatted URI with a valid Scheme per RFC3986
274 func (f URIFormatChecker) IsFormat(input interface{}) bool {
275 asString, ok := input.(string)
276 if !ok {
277 return false
278 }
279
280 u, err := url.Parse(asString)
281
168282 if err != nil || u.Scheme == "" {
169283 return false
170284 }
171285
172 return true
173 }
174
175 func (f HostnameFormatChecker) IsFormat(input string) bool {
176 return rxHostname.MatchString(input) && len(input) < 256
177 }
178
179 func (f UUIDFormatChecker) IsFormat(input string) bool {
180 return rxUUID.MatchString(input)
181 }
182
183 // IsFormat implements FormatChecker interface.
184 func (f RegexFormatChecker) IsFormat(input string) bool {
185 if input == "" {
286 return !strings.Contains(asString, `\`)
287 }
288
289 // IsFormat checks if input is a correctly formatted URI or relative-reference per RFC3986
290 func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
291 asString, ok := input.(string)
292 if !ok {
293 return false
294 }
295
296 _, err := url.Parse(asString)
297 return err == nil && !strings.Contains(asString, `\`)
298 }
299
300 // IsFormat checks if input is a correctly formatted URI template per RFC6570
301 func (f URITemplateFormatChecker) IsFormat(input interface{}) bool {
302 asString, ok := input.(string)
303 if !ok {
304 return false
305 }
306
307 u, err := url.Parse(asString)
308 if err != nil || strings.Contains(asString, `\`) {
309 return false
310 }
311
312 return rxURITemplate.MatchString(u.Path)
313 }
314
315 // IsFormat checks if input is a correctly formatted hostname
316 func (f HostnameFormatChecker) IsFormat(input interface{}) bool {
317 asString, ok := input.(string)
318 if !ok {
319 return false
320 }
321
322 return rxHostname.MatchString(asString) && len(asString) < 256
323 }
324
325 // IsFormat checks if input is a correctly formatted UUID
326 func (f UUIDFormatChecker) IsFormat(input interface{}) bool {
327 asString, ok := input.(string)
328 if !ok {
329 return false
330 }
331
332 return rxUUID.MatchString(asString)
333 }
334
335 // IsFormat checks if input is a correctly formatted regular expression
336 func (f RegexFormatChecker) IsFormat(input interface{}) bool {
337 asString, ok := input.(string)
338 if !ok {
339 return false
340 }
341
342 if asString == "" {
186343 return true
187344 }
188 _, err := regexp.Compile(input)
189 if err != nil {
190 return false
191 }
192 return true
193 }
345 _, err := regexp.Compile(asString)
346 return err == nil
347 }
348
349 // IsFormat checks if input is a correctly formatted JSON Pointer per RFC6901
350 func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
351 asString, ok := input.(string)
352 if !ok {
353 return false
354 }
355
356 return rxJSONPointer.MatchString(asString)
357 }
358
359 // IsFormat checks if input is a correctly formatted relative JSON Pointer
360 func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool {
361 asString, ok := input.(string)
362 if !ok {
363 return false
364 }
365
366 return rxRelJSONPointer.MatchString(asString)
367 }
1313 assert.False(t, checker.IsFormat("not-a-uuid"))
1414 assert.False(t, checker.IsFormat("g1234567-89ab-cdef-0123-456789abcdef"))
1515 }
16
17 func TestURIReferenceFormatCheckerIsFormat(t *testing.T) {
18 checker := URIReferenceFormatChecker{}
19
20 assert.True(t, checker.IsFormat("relative"))
21 assert.True(t, checker.IsFormat("https://dummyhost.com/dummy-path?dummy-qp-name=dummy-qp-value"))
22 }
66
77 - package: github.com/xeipuuv/gojsonreference
88
9 - package: github.com/stretchr/testify/assert
10 version: ^1.1.3
11
9 testImport:
10 - package: github.com/stretchr/testify
11 subpackages:
12 - assert
0 module github.com/xeipuuv/gojsonschema
1
2 require (
3 github.com/stretchr/testify v1.3.0
4 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
5 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415
6 )
0 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
1 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
5 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
6 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
7 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
8 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
9 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
10 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
2525
2626 import "bytes"
2727
28 // jsonContext implements a persistent linked-list of strings
29 type jsonContext struct {
28 // JsonContext implements a persistent linked-list of strings
29 type JsonContext struct {
3030 head string
31 tail *jsonContext
31 tail *JsonContext
3232 }
3333
34 func newJsonContext(head string, tail *jsonContext) *jsonContext {
35 return &jsonContext{head, tail}
34 // NewJsonContext creates a new JsonContext
35 func NewJsonContext(head string, tail *JsonContext) *JsonContext {
36 return &JsonContext{head, tail}
3637 }
3738
3839 // String displays the context in reverse.
3940 // This plays well with the data structure's persistent nature with
4041 // Cons and a json document's tree structure.
41 func (c *jsonContext) String(del ...string) string {
42 func (c *JsonContext) String(del ...string) string {
4243 byteArr := make([]byte, 0, c.stringLen())
4344 buf := bytes.NewBuffer(byteArr)
4445 c.writeStringToBuffer(buf, del)
4647 return buf.String()
4748 }
4849
49 func (c *jsonContext) stringLen() int {
50 func (c *JsonContext) stringLen() int {
5051 length := 0
5152 if c.tail != nil {
5253 length = c.tail.stringLen() + 1 // add 1 for "."
5657 return length
5758 }
5859
59 func (c *jsonContext) writeStringToBuffer(buf *bytes.Buffer, del []string) {
60 func (c *JsonContext) writeStringToBuffer(buf *bytes.Buffer, del []string) {
6061 if c.tail != nil {
6162 c.tail.writeStringToBuffer(buf, del)
6263
3232 "io"
3333 "io/ioutil"
3434 "net/http"
35 "net/url"
3536 "os"
3637 "path/filepath"
3738 "runtime"
4243
4344 var osFS = osFileSystem(os.Open)
4445
45 // JSON loader interface
46
46 // JSONLoader defines the JSON loader interface
4747 type JSONLoader interface {
4848 JsonSource() interface{}
4949 LoadJSON() (interface{}, error)
5151 LoaderFactory() JSONLoaderFactory
5252 }
5353
54 // JSONLoaderFactory defines the JSON loader factory interface
5455 type JSONLoaderFactory interface {
56 // New creates a new JSON loader for the given source
5557 New(source string) JSONLoader
5658 }
5759
60 // DefaultJSONLoaderFactory is the default JSON loader factory
5861 type DefaultJSONLoaderFactory struct {
5962 }
6063
64 // FileSystemJSONLoaderFactory is a JSON loader factory that uses http.FileSystem
6165 type FileSystemJSONLoaderFactory struct {
6266 fs http.FileSystem
6367 }
6468
69 // New creates a new JSON loader for the given source
6570 func (d DefaultJSONLoaderFactory) New(source string) JSONLoader {
6671 return &jsonReferenceLoader{
6772 fs: osFS,
6974 }
7075 }
7176
77 // New creates a new JSON loader for the given source
7278 func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader {
7379 return &jsonReferenceLoader{
7480 fs: f.fs,
7985 // osFileSystem is a functional wrapper for os.Open that implements http.FileSystem.
8086 type osFileSystem func(string) (*os.File, error)
8187
88 // Opens a file with the given name
8289 func (o osFileSystem) Open(name string) (http.File, error) {
8390 return o(name)
8491 }
106113 }
107114
108115 // NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system.
109 func NewReferenceLoader(source string) *jsonReferenceLoader {
116 func NewReferenceLoader(source string) JSONLoader {
110117 return &jsonReferenceLoader{
111118 fs: osFS,
112119 source: source,
114121 }
115122
116123 // NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system.
117 func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) *jsonReferenceLoader {
124 func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) JSONLoader {
118125 return &jsonReferenceLoader{
119126 fs: fs,
120127 source: source,
130137 return nil, err
131138 }
132139
133 refToUrl := reference
134 refToUrl.GetUrl().Fragment = ""
140 refToURL := reference
141 refToURL.GetUrl().Fragment = ""
135142
136143 var document interface{}
137144
138145 if reference.HasFileScheme {
139146
140 filename := strings.Replace(refToUrl.GetUrl().Path, "file://", "", -1)
147 filename := strings.TrimPrefix(refToURL.String(), "file://")
148 filename, err = url.QueryUnescape(filename)
149
150 if err != nil {
151 return nil, err
152 }
153
141154 if runtime.GOOS == "windows" {
142155 // on Windows, a file URL may have an extra leading slash, use slashes
143156 // instead of backslashes, and have spaces escaped
144 if strings.HasPrefix(filename, "/") {
145 filename = filename[1:]
146 }
157 filename = strings.TrimPrefix(filename, "/")
147158 filename = filepath.FromSlash(filename)
148159 }
149160
154165
155166 } else {
156167
157 document, err = l.loadFromHTTP(refToUrl.String())
168 document, err = l.loadFromHTTP(refToURL.String())
158169 if err != nil {
159170 return nil, err
160171 }
167178
168179 func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
169180
181 // returned cached versions for metaschemas for drafts 4, 6 and 7
182 // for performance and allow for easier offline use
183 if metaSchema := drafts.GetMetaSchema(address); metaSchema != "" {
184 return decodeJSONUsingNumber(strings.NewReader(metaSchema))
185 }
186
170187 resp, err := http.Get(address)
171188 if err != nil {
172189 return nil, err
174191
175192 // must return HTTP Status 200 OK
176193 if resp.StatusCode != http.StatusOK {
177 return nil, errors.New(formatErrorDescription(Locale.httpBadStatus(), ErrorDetails{"status": resp.Status}))
194 return nil, errors.New(formatErrorDescription(Locale.HttpBadStatus(), ErrorDetails{"status": resp.Status}))
178195 }
179196
180197 bodyBuff, err := ioutil.ReadAll(resp.Body)
182199 return nil, err
183200 }
184201
185 return decodeJsonUsingNumber(bytes.NewReader(bodyBuff))
186
202 return decodeJSONUsingNumber(bytes.NewReader(bodyBuff))
187203 }
188204
189205 func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
198214 return nil, err
199215 }
200216
201 return decodeJsonUsingNumber(bytes.NewReader(bodyBuff))
217 return decodeJSONUsingNumber(bytes.NewReader(bodyBuff))
202218
203219 }
204220
220236 return &DefaultJSONLoaderFactory{}
221237 }
222238
223 func NewStringLoader(source string) *jsonStringLoader {
239 // NewStringLoader creates a new JSONLoader, taking a string as source
240 func NewStringLoader(source string) JSONLoader {
224241 return &jsonStringLoader{source: source}
225242 }
226243
227244 func (l *jsonStringLoader) LoadJSON() (interface{}, error) {
228245
229 return decodeJsonUsingNumber(strings.NewReader(l.JsonSource().(string)))
246 return decodeJSONUsingNumber(strings.NewReader(l.JsonSource().(string)))
230247
231248 }
232249
248265 return &DefaultJSONLoaderFactory{}
249266 }
250267
251 func NewBytesLoader(source []byte) *jsonBytesLoader {
268 // NewBytesLoader creates a new JSONLoader, taking a `[]byte` as source
269 func NewBytesLoader(source []byte) JSONLoader {
252270 return &jsonBytesLoader{source: source}
253271 }
254272
255273 func (l *jsonBytesLoader) LoadJSON() (interface{}, error) {
256 return decodeJsonUsingNumber(bytes.NewReader(l.JsonSource().([]byte)))
274 return decodeJSONUsingNumber(bytes.NewReader(l.JsonSource().([]byte)))
257275 }
258276
259277 // JSON Go (types) loader
275293 return &DefaultJSONLoaderFactory{}
276294 }
277295
278 func NewGoLoader(source interface{}) *jsonGoLoader {
296 // NewGoLoader creates a new JSONLoader from a given Go struct
297 func NewGoLoader(source interface{}) JSONLoader {
279298 return &jsonGoLoader{source: source}
280299 }
281300
288307 return nil, err
289308 }
290309
291 return decodeJsonUsingNumber(bytes.NewReader(jsonBytes))
310 return decodeJSONUsingNumber(bytes.NewReader(jsonBytes))
292311
293312 }
294313
296315 buf *bytes.Buffer
297316 }
298317
299 func NewReaderLoader(source io.Reader) (*jsonIOLoader, io.Reader) {
318 // NewReaderLoader creates a new JSON loader using the provided io.Reader
319 func NewReaderLoader(source io.Reader) (JSONLoader, io.Reader) {
300320 buf := &bytes.Buffer{}
301321 return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf)
302322 }
303323
304 func NewWriterLoader(source io.Writer) (*jsonIOLoader, io.Writer) {
324 // NewWriterLoader creates a new JSON loader using the provided io.Writer
325 func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) {
305326 buf := &bytes.Buffer{}
306327 return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf)
307328 }
311332 }
312333
313334 func (l *jsonIOLoader) LoadJSON() (interface{}, error) {
314 return decodeJsonUsingNumber(l.buf)
335 return decodeJSONUsingNumber(l.buf)
315336 }
316337
317338 func (l *jsonIOLoader) JsonReference() (gojsonreference.JsonReference, error) {
322343 return &DefaultJSONLoaderFactory{}
323344 }
324345
325 func decodeJsonUsingNumber(r io.Reader) (interface{}, error) {
346 // JSON raw loader
347 // In case the JSON is already marshalled to interface{} use this loader
348 // This is used for testing as otherwise there is no guarantee the JSON is marshalled
349 // "properly" by using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
350 type jsonRawLoader struct {
351 source interface{}
352 }
353
354 // NewRawLoader creates a new JSON raw loader for the given source
355 func NewRawLoader(source interface{}) JSONLoader {
356 return &jsonRawLoader{source: source}
357 }
358 func (l *jsonRawLoader) JsonSource() interface{} {
359 return l.source
360 }
361 func (l *jsonRawLoader) LoadJSON() (interface{}, error) {
362 return l.source, nil
363 }
364 func (l *jsonRawLoader) JsonReference() (gojsonreference.JsonReference, error) {
365 return gojsonreference.NewJsonReference("#")
366 }
367 func (l *jsonRawLoader) LoaderFactory() JSONLoaderFactory {
368 return &DefaultJSONLoaderFactory{}
369 }
370
371 func decodeJSONUsingNumber(r io.Reader) (interface{}, error) {
326372
327373 var document interface{}
328374
+0
-19
json_schema_test_suite/LICENSE less more
0 Copyright (c) 2012 Julian Berman
1
2 Permission is hereby granted, free of charge, to any person obtaining a copy
3 of this software and associated documentation files (the "Software"), to deal
4 in the Software without restriction, including without limitation the rights
5 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
6 copies of the Software, and to permit persons to whom the Software is
7 furnished to do so, subject to the following conditions:
8
9 The above copyright notice and this permission notice shall be included in
10 all copies or substantial portions of the Software.
11
12 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
18 THE SOFTWARE.
+0
-1
json_schema_test_suite/additionalItems/data_00.json less more
0 [null,2,3,4]
+0
-1
json_schema_test_suite/additionalItems/data_01.json less more
0 [null,2,3,"foo"]
+0
-1
json_schema_test_suite/additionalItems/data_10.json less more
0 [1,2,3,4,5]
+0
-1
json_schema_test_suite/additionalItems/data_20.json less more
0 [1,2,3]
+0
-1
json_schema_test_suite/additionalItems/data_21.json less more
0 [1,2,3,4]
+0
-1
json_schema_test_suite/additionalItems/data_30.json less more
0 [1,2,3,4,5]
+0
-1
json_schema_test_suite/additionalItems/data_31.json less more
0 {"foo":"bar"}
+0
-1
json_schema_test_suite/additionalItems/data_40.json less more
0 [1,"foo",false]
+0
-1
json_schema_test_suite/additionalItems/schema_0.json less more
0 {"additionalItems":{"type":"integer"},"items":[{}]}
+0
-1
json_schema_test_suite/additionalItems/schema_1.json less more
0 {"additionalItems":false,"items":{}}
+0
-1
json_schema_test_suite/additionalItems/schema_2.json less more
0 {"additionalItems":false,"items":[{},{},{}]}
+0
-1
json_schema_test_suite/additionalItems/schema_3.json less more
0 {"additionalItems":false}
+0
-1
json_schema_test_suite/additionalItems/schema_4.json less more
0 {"items":[{"type":"integer"}]}
+0
-1
json_schema_test_suite/additionalProperties/data_00.json less more
0 {"foo":1}
+0
-1
json_schema_test_suite/additionalProperties/data_01.json less more
0 {"bar":2,"foo":1,"quux":"boom"}
+0
-1
json_schema_test_suite/additionalProperties/data_02.json less more
0 [1,2,3]
+0
-1
json_schema_test_suite/additionalProperties/data_03.json less more
0 {"foo":1, "vroom": 2}
+0
-1
json_schema_test_suite/additionalProperties/data_10.json less more
0 {"foo":1}
+0
-1
json_schema_test_suite/additionalProperties/data_11.json less more
0 {"bar":2,"foo":1,"quux":true}
+0
-1
json_schema_test_suite/additionalProperties/data_12.json less more
0 {"bar":2,"foo":1,"quux":12}
+0
-1
json_schema_test_suite/additionalProperties/data_20.json less more
0 {"bar":2,"foo":1,"quux":true}
+0
-1
json_schema_test_suite/additionalProperties/schema_0.json less more
0 {"additionalProperties":false,"properties":{"bar":{},"foo":{}},"patternProperties": { "^v": {} }}
+0
-1
json_schema_test_suite/additionalProperties/schema_1.json less more
0 {"additionalProperties":{"type":"boolean"},"properties":{"bar":{},"foo":{}}}
+0
-1
json_schema_test_suite/additionalProperties/schema_2.json less more
0 {"properties":{"bar":{},"foo":{}}}
+0
-1
json_schema_test_suite/allOf/data_00.json less more
0 {"bar":2,"foo":"baz"}
+0
-1
json_schema_test_suite/allOf/data_01.json less more
0 {"foo":"baz"}
+0
-1
json_schema_test_suite/allOf/data_02.json less more
0 {"bar":2}
+0
-1
json_schema_test_suite/allOf/data_03.json less more
0 {"bar":"quux","foo":"baz"}
+0
-1
json_schema_test_suite/allOf/data_10.json less more
0 {"bar":2,"baz":null,"foo":"quux"}
+0
-1
json_schema_test_suite/allOf/data_11.json less more
0 {"baz":null,"foo":"quux"}
+0
-1
json_schema_test_suite/allOf/data_12.json less more
0 {"bar":2,"baz":null}
+0
-1
json_schema_test_suite/allOf/data_13.json less more
0 {"bar":2,"foo":"quux"}
+0
-1
json_schema_test_suite/allOf/data_14.json less more
0 {"bar":2}
+0
-1
json_schema_test_suite/allOf/data_20.json less more
0 25
+0
-1
json_schema_test_suite/allOf/data_21.json less more
0 35
+0
-1
json_schema_test_suite/allOf/schema_0.json less more
0 {"allOf":[{"properties":{"bar":{"type":"integer"}},"required":["bar"]},{"properties":{"foo":{"type":"string"}},"required":["foo"]}]}
+0
-1
json_schema_test_suite/allOf/schema_1.json less more
0 {"allOf":[{"properties":{"foo":{"type":"string"}},"required":["foo"]},{"properties":{"baz":{"type":"null"}},"required":["baz"]}],"properties":{"bar":{"type":"integer"}},"required":["bar"]}
+0
-1
json_schema_test_suite/allOf/schema_2.json less more
0 {"allOf":[{"maximum":30},{"minimum":20}]}
+0
-1
json_schema_test_suite/anyOf/data_00.json less more
0 1
+0
-1
json_schema_test_suite/anyOf/data_01.json less more
0 2.5
+0
-1
json_schema_test_suite/anyOf/data_02.json less more
0 3
+0
-1
json_schema_test_suite/anyOf/data_03.json less more
0 1.5
+0
-1
json_schema_test_suite/anyOf/data_10.json less more
0 3
+0
-1
json_schema_test_suite/anyOf/data_11.json less more
0 "foobar"
+0
-1
json_schema_test_suite/anyOf/data_12.json less more
0 "foo"
+0
-1
json_schema_test_suite/anyOf/schema_0.json less more
0 {"anyOf":[{"type":"integer"},{"minimum":2}]}
+0
-1
json_schema_test_suite/anyOf/schema_1.json less more
0 {"anyOf":[{"maxLength":2},{"minLength":4}],"type":"string"}
+0
-1
json_schema_test_suite/definitions/data_00.json less more
0 {"definitions":{"foo":{"type":"integer"}}}
+0
-1
json_schema_test_suite/definitions/data_10.json less more
0 {"definitions":{"foo":{"type":1}}}
+0
-1
json_schema_test_suite/definitions/schema_0.json less more
0 {"$ref":"http://json-schema.org/draft-04/schema#"}
+0
-1
json_schema_test_suite/definitions/schema_1.json less more
0 {"$ref":"http://json-schema.org/draft-04/schema#"}
+0
-1
json_schema_test_suite/dependencies/data_00.json less more
0 {}
+0
-1
json_schema_test_suite/dependencies/data_01.json less more
0 {"foo":1}
+0
-1
json_schema_test_suite/dependencies/data_02.json less more
0 {"bar":2,"foo":1}
+0
-1
json_schema_test_suite/dependencies/data_03.json less more
0 {"bar":2}
+0
-1
json_schema_test_suite/dependencies/data_04.json less more
0 "foo"
+0
-1
json_schema_test_suite/dependencies/data_10.json less more
0 {}
+0
-1
json_schema_test_suite/dependencies/data_11.json less more
0 {"bar":2,"foo":1}
+0
-1
json_schema_test_suite/dependencies/data_12.json less more
0 {"bar":2,"foo":1,"quux":3}
+0
-1
json_schema_test_suite/dependencies/data_13.json less more
0 {"foo":1,"quux":2}
+0
-1
json_schema_test_suite/dependencies/data_14.json less more
0 {"bar":1,"quux":2}
+0
-1
json_schema_test_suite/dependencies/data_15.json less more
0 {"quux":1}
+0
-1
json_schema_test_suite/dependencies/data_20.json less more
0 {"bar":2,"foo":1}
+0
-1
json_schema_test_suite/dependencies/data_21.json less more
0 {"foo":"quux"}
+0
-1
json_schema_test_suite/dependencies/data_22.json less more
0 {"bar":2,"foo":"quux"}
+0
-1
json_schema_test_suite/dependencies/data_23.json less more
0 {"bar":"quux","foo":2}
+0
-1
json_schema_test_suite/dependencies/data_24.json less more
0 {"bar":"quux","foo":"quux"}
+0
-1
json_schema_test_suite/dependencies/schema_0.json less more
0 {"dependencies":{"bar":["foo"]}}
+0
-1
json_schema_test_suite/dependencies/schema_1.json less more
0 {"dependencies":{"quux":["foo","bar"]}}
+0
-1
json_schema_test_suite/dependencies/schema_2.json less more
0 {"dependencies":{"bar":{"properties":{"bar":{"type":"integer"},"foo":{"type":"integer"}}}}}
+0
-1
json_schema_test_suite/enum/data_00.json less more
0 1
+0
-1
json_schema_test_suite/enum/data_01.json less more
0 4
+0
-1
json_schema_test_suite/enum/data_10.json less more
0 []
+0
-1
json_schema_test_suite/enum/data_11.json less more
0 null
+0
-1
json_schema_test_suite/enum/data_12.json less more
0 {"foo":false}
+0
-1
json_schema_test_suite/enum/schema_0.json less more
0 {"enum":[1,2,3]}
+0
-1
json_schema_test_suite/enum/schema_1.json less more
0 {"enum":[6,"foo",[],true,{"foo":12}]}
+0
-1
json_schema_test_suite/format/data_00.json less more
0 "test"
+0
-1
json_schema_test_suite/format/data_01.json less more
0 "test@"
+0
-1
json_schema_test_suite/format/data_02.json less more
0 "test@test.com"
+0
-1
json_schema_test_suite/format/data_03.json less more
0 "AB-10105"
+0
-1
json_schema_test_suite/format/data_04.json less more
0 "ABC10105"
+0
-1
json_schema_test_suite/format/data_05.json less more
0 "05:15:37"
+0
-1
json_schema_test_suite/format/data_06.json less more
0 "2015-05-13"
+0
-1
json_schema_test_suite/format/data_07.json less more
0 "2015-6-31"
+0
-1
json_schema_test_suite/format/data_08.json less more
0 "2015-01-30 19:08:06"
+0
-1
json_schema_test_suite/format/data_09.json less more
0 "18:31:24-05:00"
+0
-1
json_schema_test_suite/format/data_10.json less more
0 "2002-10-02T10:00:00-05:00"
+0
-1
json_schema_test_suite/format/data_11.json less more
0 "2002-10-02T15:00:00Z"
+0
-1
json_schema_test_suite/format/data_12.json less more
0 "2002-10-02T15:00:00.05Z"
+0
-1
json_schema_test_suite/format/data_13.json less more
0 "example.com"
+0
-1
json_schema_test_suite/format/data_14.json less more
0 "sub.example.com"
+0
-1
json_schema_test_suite/format/data_15.json less more
0 "hello.co.uk"
+0
-1
json_schema_test_suite/format/data_16.json less more
0 "http://example.com"
+0
-1
json_schema_test_suite/format/data_17.json less more
0 "example_com"
+0
-1
json_schema_test_suite/format/data_18.json less more
0 "4.2.2.4"
+0
-1
json_schema_test_suite/format/data_19.json less more
0 "4.1.1111.45"
+0
-1
json_schema_test_suite/format/data_20.json less more
0 "FE80:0000:0000:0000:0202:B3FF:FE1E:8329"
+0
-1
json_schema_test_suite/format/data_21.json less more
0 "FE80::0202:B3FF:FE1E:8329"
+0
-1
json_schema_test_suite/format/data_22.json less more
0 "1200::AB00:1234::2552:7777:1313"
+0
-1
json_schema_test_suite/format/data_23.json less more
0 "1200:0000:AB00:1234:O000:2552:7777:1313"
+0
-1
json_schema_test_suite/format/data_24.json less more
0 "ftp://ftp.is.co.za/rfc/rfc1808.txt"
+0
-1
json_schema_test_suite/format/data_25.json less more
0 "mailto:john.doe@example.com"
+0
-1
json_schema_test_suite/format/data_26.json less more
0 "tel:+1-816-555-1212"
+0
-1
json_schema_test_suite/format/data_27.json less more
0 "http://www.ietf.org/rfc/rfc2396.txt"
+0
-1
json_schema_test_suite/format/data_28.json less more
0 "example.com/path/to/file"
+0
-1
json_schema_test_suite/format/schema_0.json less more
0 {"type": "string", "format": "email"}
+0
-1
json_schema_test_suite/format/schema_1.json less more
0 {"type": "string", "format": "invoice"}
+0
-1
json_schema_test_suite/format/schema_2.json less more
0 {"type": "string", "format": "date-time"}
+0
-1
json_schema_test_suite/format/schema_3.json less more
0 {"type": "string", "format": "hostname"}
+0
-1
json_schema_test_suite/format/schema_4.json less more
0 {"type": "string", "format": "ipv4"}
+0
-1
json_schema_test_suite/format/schema_5.json less more
0 {"type": "string", "format": "ipv6"}
+0
-1
json_schema_test_suite/format/schema_6.json less more
0 {"type": "string", "format": "uri"}
+0
-1
json_schema_test_suite/items/data_00.json less more
0 [1,2,3]
+0
-1
json_schema_test_suite/items/data_01.json less more
0 [1,"x"]
+0
-1
json_schema_test_suite/items/data_02.json less more
0 {"foo":"bar"}
+0
-1
json_schema_test_suite/items/data_10.json less more
0 [1,"foo"]
+0
-1
json_schema_test_suite/items/data_11.json less more
0 ["foo",1]
+0
-1
json_schema_test_suite/items/schema_0.json less more
0 {"items":{"type":"integer"}}
+0
-1
json_schema_test_suite/items/schema_1.json less more
0 {"items":[{"type":"integer"},{"type":"string"}]}
+0
-1
json_schema_test_suite/maxItems/data_00.json less more
0 [1]
+0
-1
json_schema_test_suite/maxItems/data_01.json less more
0 [1,2]
+0
-1
json_schema_test_suite/maxItems/data_02.json less more
0 [1,2,3]
+0
-1
json_schema_test_suite/maxItems/data_03.json less more
0 "foobar"
+0
-1
json_schema_test_suite/maxItems/schema_0.json less more
0 {"maxItems":2}
+0
-1
json_schema_test_suite/maxLength/data_00.json less more
0 "f"
+0
-1
json_schema_test_suite/maxLength/data_01.json less more
0 "fo"
+0
-1
json_schema_test_suite/maxLength/data_02.json less more
0 "foo"
+0
-1
json_schema_test_suite/maxLength/data_03.json less more
0 10
+0
-1
json_schema_test_suite/maxLength/data_04.json less more
0 "世界"
+0
-1
json_schema_test_suite/maxLength/schema_0.json less more
0 {"maxLength":2}
+0
-1
json_schema_test_suite/maxProperties/data_00.json less more
0 {"foo":1}
+0
-1
json_schema_test_suite/maxProperties/data_01.json less more
0 {"bar":2,"foo":1}
+0
-1
json_schema_test_suite/maxProperties/data_02.json less more
0 {"bar":2,"baz":3,"foo":1}
+0
-1
json_schema_test_suite/maxProperties/data_03.json less more
0 "foobar"
+0
-1
json_schema_test_suite/maxProperties/schema_0.json less more
0 {"maxProperties":2}
+0
-1
json_schema_test_suite/maximum/data_00.json less more
0 2.6
+0
-1
json_schema_test_suite/maximum/data_01.json less more
0 3.5
+0
-1
json_schema_test_suite/maximum/data_02.json less more
0 "x"
+0
-1
json_schema_test_suite/maximum/data_10.json less more
0 2.2
+0
-1
json_schema_test_suite/maximum/data_11.json less more
0 3
+0
-1
json_schema_test_suite/maximum/schema_0.json less more
0 {"maximum":3}
+0
-1
json_schema_test_suite/maximum/schema_1.json less more
0 {"exclusiveMaximum":true,"maximum":3}
+0
-1
json_schema_test_suite/minItems/data_00.json less more
0 [1,2]
+0
-1
json_schema_test_suite/minItems/data_01.json less more
0 [1]
+0
-1
json_schema_test_suite/minItems/data_02.json less more
0 []
+0
-1
json_schema_test_suite/minItems/data_03.json less more
0 ""
+0
-1
json_schema_test_suite/minItems/schema_0.json less more
0 {"minItems":1}
+0
-1
json_schema_test_suite/minLength/data_00.json less more
0 "foo"
+0
-1
json_schema_test_suite/minLength/data_01.json less more
0 "fo"
+0
-1
json_schema_test_suite/minLength/data_02.json less more
0 "f"
+0
-1
json_schema_test_suite/minLength/data_03.json less more
0 1
+0
-1
json_schema_test_suite/minLength/data_04.json less more
0 "世"
+0
-1
json_schema_test_suite/minLength/schema_0.json less more
0 {"minLength":2}
+0
-1
json_schema_test_suite/minProperties/data_00.json less more
0 {"bar":2,"foo":1}
+0
-1
json_schema_test_suite/minProperties/data_01.json less more
0 {"foo":1}
+0
-1
json_schema_test_suite/minProperties/data_02.json less more
0 {}
+0
-1
json_schema_test_suite/minProperties/data_03.json less more
0 ""
+0
-1
json_schema_test_suite/minProperties/schema_0.json less more
0 {"minProperties":1}
+0
-1
json_schema_test_suite/minimum/data_00.json less more
0 2.6
+0
-1
json_schema_test_suite/minimum/data_01.json less more
0 0.6
+0
-1
json_schema_test_suite/minimum/data_02.json less more
0 "x"
+0
-1
json_schema_test_suite/minimum/data_10.json less more
0 1.2
+0
-1
json_schema_test_suite/minimum/data_11.json less more
0 1.1
+0
-1
json_schema_test_suite/minimum/schema_0.json less more
0 {"minimum":1.1}
+0
-1
json_schema_test_suite/minimum/schema_1.json less more
0 {"exclusiveMinimum":true,"minimum":1.1}
+0
-1
json_schema_test_suite/multipleOf/data_00.json less more
0 10
+0
-1
json_schema_test_suite/multipleOf/data_01.json less more
0 7
+0
-1
json_schema_test_suite/multipleOf/data_02.json less more
0 "foo"
+0
-1
json_schema_test_suite/multipleOf/data_10.json less more
0 0
+0
-1
json_schema_test_suite/multipleOf/data_11.json less more
0 4.5
+0
-1
json_schema_test_suite/multipleOf/data_12.json less more
0 35
+0
-1
json_schema_test_suite/multipleOf/data_20.json less more
0 0.0075
+0
-1
json_schema_test_suite/multipleOf/data_21.json less more
0 0.00751
+0
-1
json_schema_test_suite/multipleOf/schema_0.json less more
0 {"multipleOf":2}
+0
-1
json_schema_test_suite/multipleOf/schema_1.json less more
0 {"multipleOf":1.5}
+0
-1
json_schema_test_suite/multipleOf/schema_2.json less more
0 {"multipleOf":0.0001}
+0
-1
json_schema_test_suite/not/data_00.json less more
0 "foo"
+0
-1
json_schema_test_suite/not/data_01.json less more
0 1
+0
-1
json_schema_test_suite/not/data_10.json less more
0 "foo"
+0
-1
json_schema_test_suite/not/data_11.json less more
0 1
+0
-1
json_schema_test_suite/not/data_12.json less more
0 true
+0
-1
json_schema_test_suite/not/data_20.json less more
0 1
+0
-1
json_schema_test_suite/not/data_21.json less more
0 {"foo":1}
+0
-1
json_schema_test_suite/not/data_22.json less more
0 {"foo":"bar"}
+0
-1
json_schema_test_suite/not/schema_0.json less more
0 {"not":{"type":"integer"}}
+0
-1
json_schema_test_suite/not/schema_1.json less more
0 {"not":{"type":["integer","boolean"]}}
+0
-1
json_schema_test_suite/not/schema_2.json less more
0 {"not":{"properties":{"foo":{"type":"string"}},"type":"object"}}
+0
-1
json_schema_test_suite/oneOf/data_00.json less more
0 1
+0
-1
json_schema_test_suite/oneOf/data_01.json less more
0 2.5
+0
-1
json_schema_test_suite/oneOf/data_02.json less more
0 3
+0
-1
json_schema_test_suite/oneOf/data_03.json less more
0 1.5
+0
-1
json_schema_test_suite/oneOf/data_10.json less more
0 3
+0
-1
json_schema_test_suite/oneOf/data_11.json less more
0 "foobar"
+0
-1
json_schema_test_suite/oneOf/data_12.json less more
0 "foo"
+0
-1
json_schema_test_suite/oneOf/schema_0.json less more
0 {"oneOf":[{"type":"integer"},{"minimum":2}]}
+0
-1
json_schema_test_suite/oneOf/schema_1.json less more
0 {"oneOf":[{"minLength":2},{"maxLength":4}],"type":"string"}
+0
-1
json_schema_test_suite/pattern/data_00.json less more
0 "aaa"
+0
-1
json_schema_test_suite/pattern/data_01.json less more
0 "abc"
+0
-1
json_schema_test_suite/pattern/data_02.json less more
0 true
+0
-1
json_schema_test_suite/pattern/schema_0.json less more
0 {"pattern":"^a*$"}
+0
-1
json_schema_test_suite/patternProperties/data_00.json less more
0 {"foo":1}
+0
-1
json_schema_test_suite/patternProperties/data_01.json less more
0 {"foo":1,"foooooo":2}
+0
-1
json_schema_test_suite/patternProperties/data_02.json less more
0 {"foo":"bar","fooooo":2}
+0
-1
json_schema_test_suite/patternProperties/data_03.json less more
0 {"foo":"bar","foooooo":"baz"}
+0
-1
json_schema_test_suite/patternProperties/data_04.json less more
0 12
+0
-1
json_schema_test_suite/patternProperties/data_10.json less more
0 {"a":21}
+0
-1
json_schema_test_suite/patternProperties/data_11.json less more
0 {"aaaa":18}
+0
-1
json_schema_test_suite/patternProperties/data_12.json less more
0 {"a":21,"aaaa":18}
+0
-1
json_schema_test_suite/patternProperties/data_13.json less more
0 {"a":"bar"}
+0
-1
json_schema_test_suite/patternProperties/data_14.json less more
0 {"aaaa":31}
+0
-1
json_schema_test_suite/patternProperties/data_15.json less more
0 {"aaa":"foo","aaaa":31}
+0
-1
json_schema_test_suite/patternProperties/data_20.json less more
0 {"answer 1":"42"}
+0
-1
json_schema_test_suite/patternProperties/data_21.json less more
0 {"a31b":null}
+0
-1
json_schema_test_suite/patternProperties/data_22.json less more
0 {"a_x_3":3}
+0
-1
json_schema_test_suite/patternProperties/data_23.json less more
0 {"a_X_3":3}
+0
-4
json_schema_test_suite/patternProperties/data_24.json less more
0 {
1 "a": "hello",
2 "aaaaaaaaaaaaaaaaaaa": null
3 }
+0
-3
json_schema_test_suite/patternProperties/data_25.json less more
0 {
1 "aaaaaaaaaaaaaaaaaaa": null
2 }
+0
-6
json_schema_test_suite/patternProperties/data_26.json less more
0 {
1 "dictionary": {
2 "aaaaaaaaaaaaaaaaaaaa": null,
3 "b": "hello"
4 }
5 }
+0
-1
json_schema_test_suite/patternProperties/schema_0.json less more
0 {"patternProperties":{"f.*o":{"type":"integer"}}}
+0
-1
json_schema_test_suite/patternProperties/schema_1.json less more
0 {"patternProperties":{"a*":{"type":"integer"},"aaa*":{"maximum":20}}}
+0
-1
json_schema_test_suite/patternProperties/schema_2.json less more
0 {"patternProperties":{"X_":{"type":"string"},"[0-9]{2,}":{"type":"boolean"}}}
+0
-8
json_schema_test_suite/patternProperties/schema_3.json less more
0 {
1 "$schema": "http://json-schema.org/draft-04/schema#",
2 "type": "object",
3 "patternProperties": {
4 "^[a-zA-Z0-9]{1,10}$": { "type": "string" }
5 },
6 "additionalProperties": false
7 }
+0
-14
json_schema_test_suite/patternProperties/schema_4.json less more
0 {
1 "$schema": "http://json-schema.org/draft-04/schema#",
2 "type": "object",
3 "properties": {
4 "dictionary": {
5 "type": "object",
6 "patternProperties": {
7 "^[a-zA-Z0-9]{1,10}$": { "type": "string" }
8 },
9 "additionalProperties": false
10 }
11 },
12 "additionalProperties": false
13 }
+0
-1
json_schema_test_suite/properties/data_00.json less more
0 {"bar":"baz","foo":1}
+0
-1
json_schema_test_suite/properties/data_01.json less more
0 {"bar":{},"foo":1}
+0
-1
json_schema_test_suite/properties/data_02.json less more
0 {"bar":{},"foo":[]}
+0
-1
json_schema_test_suite/properties/data_03.json less more
0 {"quux":[]}
+0
-1
json_schema_test_suite/properties/data_04.json less more
0 []
+0
-1
json_schema_test_suite/properties/data_10.json less more
0 {"foo":[1,2]}
+0
-1
json_schema_test_suite/properties/data_11.json less more
0 {"foo":[1,2,3,4]}
+0
-1
json_schema_test_suite/properties/data_12.json less more
0 {"foo":[]}
+0
-1
json_schema_test_suite/properties/data_13.json less more
0 {"fxo":[1,2]}
+0
-1
json_schema_test_suite/properties/data_14.json less more
0 {"fxo":[]}
+0
-1
json_schema_test_suite/properties/data_15.json less more
0 {"bar":[]}
+0
-1
json_schema_test_suite/properties/data_16.json less more
0 {"quux":3}
+0
-1
json_schema_test_suite/properties/data_17.json less more
0 {"quux":"foo"}
+0
-1
json_schema_test_suite/properties/schema_0.json less more
0 {"properties":{"bar":{"type":"string"},"foo":{"type":"integer"}}}
+0
-1
json_schema_test_suite/properties/schema_1.json less more
0 {"additionalProperties":{"type":"integer"},"patternProperties":{"f.o":{"minItems":2}},"properties":{"bar":{"type":"array"},"foo":{"maxItems":3,"type":"array"}}}
+0
-1
json_schema_test_suite/ref/data_00.json less more
0 {"foo":false}
+0
-1
json_schema_test_suite/ref/data_01.json less more
0 {"foo":{"foo":false}}
+0
-1
json_schema_test_suite/ref/data_02.json less more
0 {"bar":false}
+0
-1
json_schema_test_suite/ref/data_03.json less more
0 {"foo":{"bar":false}}
+0
-1
json_schema_test_suite/ref/data_10.json less more
0 {"bar":3}
+0
-1
json_schema_test_suite/ref/data_11.json less more
0 {"bar":true}
+0
-1
json_schema_test_suite/ref/data_20.json less more
0 [1,2]
+0
-1
json_schema_test_suite/ref/data_21.json less more
0 [1,"foo"]
+0
-1
json_schema_test_suite/ref/data_30.json less more
0 {"slash":"aoeu"}
+0
-1
json_schema_test_suite/ref/data_31.json less more
0 {"tilda":"aoeu"}
+0
-1
json_schema_test_suite/ref/data_32.json less more
0 {"percent":"aoeu"}
+0
-1
json_schema_test_suite/ref/data_40.json less more
0 5
+0
-1
json_schema_test_suite/ref/data_41.json less more
0 "a"
+0
-1
json_schema_test_suite/ref/data_50.json less more
0 {"minLength":1}
+0
-1
json_schema_test_suite/ref/data_51.json less more
0 {"minLength":-1}
+0
-1
json_schema_test_suite/ref/schema_0.json less more
0 {"additionalProperties":false,"properties":{"foo":{"$ref":"#"}}}
+0
-1
json_schema_test_suite/ref/schema_1.json less more
0 {"properties":{"bar":{"$ref":"#/properties/foo"},"foo":{"type":"integer"}}}
+0
-1
json_schema_test_suite/ref/schema_2.json less more
0 {"items":[{"type":"integer"},{"$ref":"#/items/0"}]}
+0
-1
json_schema_test_suite/ref/schema_3.json less more
0 {"percent%field":{"type":"integer"},"properties":{"percent":{"$ref":"#/percent%25field"},"slash":{"$ref":"#/slash~1field"},"tilda":{"$ref":"#/tilda~0field"}},"slash/field":{"type":"integer"},"tilda~field":{"type":"integer"}}
+0
-1
json_schema_test_suite/ref/schema_4.json less more
0 {"$ref":"#/definitions/c","definitions":{"a":{"type":"integer"},"b":{"$ref":"#/definitions/a"},"c":{"$ref":"#/definitions/b"}}}
+0
-1
json_schema_test_suite/ref/schema_5.json less more
0 {"$ref":"http://json-schema.org/draft-04/schema#"}
+0
-1
json_schema_test_suite/refRemote/data_00.json less more
0 1
+0
-1
json_schema_test_suite/refRemote/data_01.json less more
0 "a"
+0
-1
json_schema_test_suite/refRemote/data_10.json less more
0 1
+0
-1
json_schema_test_suite/refRemote/data_11.json less more
0 "a"
+0
-1
json_schema_test_suite/refRemote/data_20.json less more
0 1
+0
-1
json_schema_test_suite/refRemote/data_21.json less more
0 "a"
+0
-1
json_schema_test_suite/refRemote/data_30.json less more
0 [[1]]
+0
-1
json_schema_test_suite/refRemote/data_31.json less more
0 [["a"]]
+0
-3
json_schema_test_suite/refRemote/remoteFiles/folder/folderInteger.json less more
0 {
1 "type": "integer"
2 }
+0
-3
json_schema_test_suite/refRemote/remoteFiles/integer.json less more
0 {
1 "type": "integer"
2 }
+0
-8
json_schema_test_suite/refRemote/remoteFiles/subSchemas.json less more
0 {
1 "integer": {
2 "type": "integer"
3 },
4 "refToInteger": {
5 "$ref": "#/integer"
6 }
7 }
+0
-1
json_schema_test_suite/refRemote/schema_0.json less more
0 {"$ref":"http://localhost:1234/integer.json"}
+0
-1
json_schema_test_suite/refRemote/schema_1.json less more
0 {"$ref":"http://localhost:1234/subSchemas.json#/integer"}
+0
-1
json_schema_test_suite/refRemote/schema_2.json less more
0 {"$ref":"http://localhost:1234/subSchemas.json#/refToInteger"}
+0
-1
json_schema_test_suite/refRemote/schema_3.json less more
0 {"id":"http://localhost:1234","items":{"id":"folder/","items":{"$ref":"folderInteger.json"}}}
+0
-1
json_schema_test_suite/required/data_00.json less more
0 {"foo":1}
+0
-1
json_schema_test_suite/required/data_01.json less more
0 {"bar":1}
+0
-1
json_schema_test_suite/required/data_10.json less more
0 {}
+0
-1
json_schema_test_suite/required/schema_0.json less more
0 {"properties":{"bar":{},"foo":{}},"required":["foo"]}
+0
-1
json_schema_test_suite/required/schema_1.json less more
0 {"properties":{"foo":{}}}
+0
-1
json_schema_test_suite/type/data_00.json less more
0 1
+0
-1
json_schema_test_suite/type/data_01.json less more
0 1.1
+0
-1
json_schema_test_suite/type/data_02.json less more
0 "foo"
+0
-1
json_schema_test_suite/type/data_03.json less more
0 {}
+0
-1
json_schema_test_suite/type/data_04.json less more
0 []
+0
-1
json_schema_test_suite/type/data_05.json less more
0 true
+0
-1
json_schema_test_suite/type/data_06.json less more
0 null
+0
-1
json_schema_test_suite/type/data_10.json less more
0 1
+0
-1
json_schema_test_suite/type/data_11.json less more
0 1.1
+0
-1
json_schema_test_suite/type/data_12.json less more
0 "foo"
+0
-1
json_schema_test_suite/type/data_13.json less more
0 {}
+0
-1
json_schema_test_suite/type/data_14.json less more
0 []
+0
-1
json_schema_test_suite/type/data_15.json less more
0 true
+0
-1
json_schema_test_suite/type/data_16.json less more
0 null
+0
-1
json_schema_test_suite/type/data_20.json less more
0 1
+0
-1
json_schema_test_suite/type/data_21.json less more
0 1.1
+0
-1
json_schema_test_suite/type/data_22.json less more
0 "foo"
+0
-1
json_schema_test_suite/type/data_23.json less more
0 {}
+0
-1
json_schema_test_suite/type/data_24.json less more
0 []
+0
-1
json_schema_test_suite/type/data_25.json less more
0 true
+0
-1
json_schema_test_suite/type/data_26.json less more
0 null
+0
-1
json_schema_test_suite/type/data_30.json less more
0 1
+0
-1
json_schema_test_suite/type/data_31.json less more
0 1.1
+0
-1
json_schema_test_suite/type/data_32.json less more
0 "foo"
+0
-1
json_schema_test_suite/type/data_33.json less more
0 {}
+0
-1
json_schema_test_suite/type/data_34.json less more
0 []
+0
-1
json_schema_test_suite/type/data_35.json less more
0 true
+0
-1
json_schema_test_suite/type/data_36.json less more
0 null
+0
-1
json_schema_test_suite/type/data_40.json less more
0 1
+0
-1
json_schema_test_suite/type/data_41.json less more
0 1.1
+0
-1
json_schema_test_suite/type/data_42.json less more
0 "foo"
+0
-1
json_schema_test_suite/type/data_43.json less more
0 {}
+0
-1
json_schema_test_suite/type/data_44.json less more
0 []
+0
-1
json_schema_test_suite/type/data_45.json less more
0 true
+0
-1
json_schema_test_suite/type/data_46.json less more
0 null
+0
-1
json_schema_test_suite/type/data_50.json less more
0 1
+0
-1
json_schema_test_suite/type/data_51.json less more
0 1.1
+0
-1
json_schema_test_suite/type/data_52.json less more
0 "foo"
+0
-1
json_schema_test_suite/type/data_53.json less more
0 {}
+0
-1
json_schema_test_suite/type/data_54.json less more
0 []
+0
-1
json_schema_test_suite/type/data_55.json less more
0 true
+0
-1
json_schema_test_suite/type/data_56.json less more
0 null
+0
-1
json_schema_test_suite/type/data_60.json less more
0 1
+0
-1
json_schema_test_suite/type/data_61.json less more
0 1.1
+0
-1
json_schema_test_suite/type/data_62.json less more
0 "foo"
+0
-1
json_schema_test_suite/type/data_63.json less more
0 {}
+0
-1
json_schema_test_suite/type/data_64.json less more
0 []
+0
-1
json_schema_test_suite/type/data_65.json less more
0 true
+0
-1
json_schema_test_suite/type/data_66.json less more
0 null
+0
-1
json_schema_test_suite/type/data_70.json less more
0 1
+0
-1
json_schema_test_suite/type/data_71.json less more
0 "foo"
+0
-1
json_schema_test_suite/type/data_72.json less more
0 1.1
+0
-1
json_schema_test_suite/type/data_73.json less more
0 {}
+0
-1
json_schema_test_suite/type/data_74.json less more
0 []
+0
-1
json_schema_test_suite/type/data_75.json less more
0 true
+0
-1
json_schema_test_suite/type/data_76.json less more
0 null
+0
-1
json_schema_test_suite/type/schema_0.json less more
0 {"type":"integer"}
+0
-1
json_schema_test_suite/type/schema_1.json less more
0 {"type":"number"}
+0
-1
json_schema_test_suite/type/schema_2.json less more
0 {"type":"string"}
+0
-1
json_schema_test_suite/type/schema_3.json less more
0 {"type":"object"}
+0
-1
json_schema_test_suite/type/schema_4.json less more
0 {"type":"array"}
+0
-1
json_schema_test_suite/type/schema_5.json less more
0 {"type":"boolean"}
+0
-1
json_schema_test_suite/type/schema_6.json less more
0 {"type":"null"}
+0
-1
json_schema_test_suite/type/schema_7.json less more
0 {"type":["integer","string"]}
+0
-1
json_schema_test_suite/uniqueItems/data_00.json less more
0 [1,2]
+0
-1
json_schema_test_suite/uniqueItems/data_01.json less more
0 [1,1]
+0
-1
json_schema_test_suite/uniqueItems/data_010.json less more
0 [0,false]
+0
-1
json_schema_test_suite/uniqueItems/data_011.json less more
0 [{},[1],true,null,1]
+0
-1
json_schema_test_suite/uniqueItems/data_012.json less more
0 [{},[1],true,null,{},1]
+0
-1
json_schema_test_suite/uniqueItems/data_02.json less more
0 [1,1,1]
+0
-1
json_schema_test_suite/uniqueItems/data_03.json less more
0 [{"foo":"bar"},{"foo":"baz"}]
+0
-1
json_schema_test_suite/uniqueItems/data_04.json less more
0 [{"foo":"bar"},{"foo":"bar"}]
+0
-1
json_schema_test_suite/uniqueItems/data_05.json less more
0 [{"foo":{"bar":{"baz":true}}},{"foo":{"bar":{"baz":false}}}]
+0
-1
json_schema_test_suite/uniqueItems/data_06.json less more
0 [{"foo":{"bar":{"baz":true}}},{"foo":{"bar":{"baz":true}}}]
+0
-1
json_schema_test_suite/uniqueItems/data_07.json less more
0 [["foo"],["bar"]]
+0
-1
json_schema_test_suite/uniqueItems/data_08.json less more
0 [["foo"],["foo"]]
+0
-1
json_schema_test_suite/uniqueItems/data_09.json less more
0 [1,true]
+0
-1
json_schema_test_suite/uniqueItems/schema_0.json less more
0 {"uniqueItems":true}
0 // Copyright 2017 johandorland ( https://github.com/johandorland )
1 //
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package gojsonschema
15
16 import (
17 "encoding/json"
18 "fmt"
19 "io/ioutil"
20 "net/http"
21 "os"
22 "path/filepath"
23 "regexp"
24 "strings"
25 "testing"
26 )
27
28 type jsonSchemaTest struct {
29 Description string `json:"description"`
30 // Some tests may not always pass, so some tests are manually edited to include
31 // an extra attribute whether that specific test should be disabled and skipped
32 Disabled bool `json:"disabled"`
33 Schema interface{} `json:"schema"`
34 Tests []jsonSchemaTestCase `json:"tests"`
35 }
36 type jsonSchemaTestCase struct {
37 Description string `json:"description"`
38 Data interface{} `json:"data"`
39 Valid bool `json:"valid"`
40 }
41
42 //Skip any directories not named appropiately
43 // filepath.Walk will also visit files in the root of the test directory
44 var testDirectories = regexp.MustCompile(`(draft\d+)`)
45 var draftMapping = map[string]Draft{
46 "draft4": Draft4,
47 "draft6": Draft6,
48 "draft7": Draft7,
49 }
50
51 func executeTests(t *testing.T, path string) error {
52 file, err := os.Open(path)
53 if err != nil {
54 t.Errorf("Error (%s)\n", err.Error())
55 }
56 fmt.Println(file.Name())
57
58 var tests []jsonSchemaTest
59 d := json.NewDecoder(file)
60 d.UseNumber()
61 err = d.Decode(&tests)
62
63 if err != nil {
64 t.Errorf("Error (%s)\n", err.Error())
65 }
66
67 draft := Hybrid
68 if m := testDirectories.FindString(path); m != "" {
69 draft = draftMapping[m]
70 }
71
72 for _, test := range tests {
73 fmt.Println(" " + test.Description)
74
75 if test.Disabled {
76 continue
77 }
78
79 testSchemaLoader := NewRawLoader(test.Schema)
80 sl := NewSchemaLoader()
81 sl.Draft = draft
82 sl.Validate = true
83 testSchema, err := sl.Compile(testSchemaLoader)
84
85 if err != nil {
86 t.Errorf("Error (%s)\n", err.Error())
87 }
88
89 for _, testCase := range test.Tests {
90 testDataLoader := NewRawLoader(testCase.Data)
91 result, err := testSchema.Validate(testDataLoader)
92
93 if err != nil {
94 t.Errorf("Error (%s)\n", err.Error())
95 }
96
97 if result.Valid() != testCase.Valid {
98 schemaString, _ := marshalToJSONString(test.Schema)
99 testCaseString, _ := marshalToJSONString(testCase.Data)
100
101 t.Errorf("Test failed : %s\n"+
102 "%s.\n"+
103 "%s.\n"+
104 "expects: %t, given %t\n"+
105 "Schema: %s\n"+
106 "Data: %s\n",
107 file.Name(),
108 test.Description,
109 testCase.Description,
110 testCase.Valid,
111 result.Valid(),
112 *schemaString,
113 *testCaseString)
114 }
115 }
116 }
117 return nil
118 }
119
120 func TestSuite(t *testing.T) {
121
122 wd, err := os.Getwd()
123 if err != nil {
124 panic(err.Error())
125 }
126 wd = filepath.Join(wd, "testdata")
127
128 go func() {
129 err := http.ListenAndServe(":1234", http.FileServer(http.Dir(filepath.Join(wd, "remotes"))))
130 if err != nil {
131
132 panic(err.Error())
133 }
134 }()
135
136 err = filepath.Walk(wd, func(path string, fileInfo os.FileInfo, err error) error {
137 if fileInfo.IsDir() && path != wd && !testDirectories.MatchString(fileInfo.Name()) {
138 return filepath.SkipDir
139 }
140 if !strings.HasSuffix(fileInfo.Name(), ".json") {
141 return nil
142 }
143 return executeTests(t, path)
144 })
145 if err != nil {
146 t.Errorf("Error (%s)\n", err.Error())
147 }
148 }
149
150 func TestFormats(t *testing.T) {
151 wd, err := os.Getwd()
152 if err != nil {
153 panic(err.Error())
154 }
155 wd = filepath.Join(wd, "testdata")
156
157 dirs, err := ioutil.ReadDir(wd)
158
159 if err != nil {
160 panic(err.Error())
161 }
162
163 for _, dir := range dirs {
164 if testDirectories.MatchString(dir.Name()) {
165 formatsDirectory := filepath.Join(wd, dir.Name(), "optional", "format")
166 err = filepath.Walk(formatsDirectory, func(path string, fileInfo os.FileInfo, err error) error {
167 if fileInfo == nil || !strings.HasSuffix(fileInfo.Name(), ".json") {
168 return nil
169 }
170 return executeTests(t, path)
171 })
172
173 if err != nil {
174 t.Errorf("Error (%s)\n", err.Error())
175 }
176 }
177 }
178 }
2525 package gojsonschema
2626
2727 type (
28 // locale is an interface for definining custom error strings
28 // locale is an interface for defining custom error strings
2929 locale interface {
30
31 // False returns a format-string for "false" schema validation errors
32 False() string
33
34 // Required returns a format-string for "required" schema validation errors
3035 Required() string
36
37 // InvalidType returns a format-string for "invalid type" schema validation errors
3138 InvalidType() string
39
40 // NumberAnyOf returns a format-string for "anyOf" schema validation errors
3241 NumberAnyOf() string
42
43 // NumberOneOf returns a format-string for "oneOf" schema validation errors
3344 NumberOneOf() string
45
46 // NumberAllOf returns a format-string for "allOf" schema validation errors
3447 NumberAllOf() string
48
49 // NumberNot returns a format-string to format a NumberNotError
3550 NumberNot() string
51
52 // MissingDependency returns a format-string for "missing dependency" schema validation errors
3653 MissingDependency() string
54
55 // Internal returns a format-string for internal errors
3756 Internal() string
57
58 // Const returns a format-string to format a ConstError
59 Const() string
60
61 // Enum returns a format-string to format an EnumError
3862 Enum() string
63
64 // ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema
3965 ArrayNotEnoughItems() string
66
67 // ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError
4068 ArrayNoAdditionalItems() string
69
70 // ArrayMinItems returns a format-string to format an ArrayMinItemsError
4171 ArrayMinItems() string
72
73 // ArrayMaxItems returns a format-string to format an ArrayMaxItemsError
4274 ArrayMaxItems() string
75
76 // Unique returns a format-string to format an ItemsMustBeUniqueError
4377 Unique() string
78
79 // ArrayContains returns a format-string to format an ArrayContainsError
80 ArrayContains() string
81
82 // ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError
4483 ArrayMinProperties() string
84
85 // ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError
4586 ArrayMaxProperties() string
87
88 // AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError
4689 AdditionalPropertyNotAllowed() string
90
91 // InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError
4792 InvalidPropertyPattern() string
93
94 // InvalidPropertyName returns a format-string to format an InvalidPropertyNameError
95 InvalidPropertyName() string
96
97 // StringGTE returns a format-string to format an StringLengthGTEError
4898 StringGTE() string
99
100 // StringLTE returns a format-string to format an StringLengthLTEError
49101 StringLTE() string
102
103 // DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError
50104 DoesNotMatchPattern() string
105
106 // DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError
51107 DoesNotMatchFormat() string
108
109 // MultipleOf returns a format-string to format an MultipleOfError
52110 MultipleOf() string
111
112 // NumberGTE returns a format-string to format an NumberGTEError
53113 NumberGTE() string
114
115 // NumberGT returns a format-string to format an NumberGTError
54116 NumberGT() string
117
118 // NumberLTE returns a format-string to format an NumberLTEError
55119 NumberLTE() string
120
121 // NumberLT returns a format-string to format an NumberLTError
56122 NumberLT() string
57123
58124 // Schema validations
125
126 // RegexPattern returns a format-string to format a regex-pattern error
59127 RegexPattern() string
128
129 // GreaterThanZero returns a format-string to format an error where a number must be greater than zero
60130 GreaterThanZero() string
131
132 // MustBeOfA returns a format-string to format an error where a value is of the wrong type
61133 MustBeOfA() string
134
135 // MustBeOfAn returns a format-string to format an error where a value is of the wrong type
62136 MustBeOfAn() string
137
138 // CannotBeUsedWithout returns a format-string to format a "cannot be used without" error
63139 CannotBeUsedWithout() string
140
141 // CannotBeGT returns a format-string to format an error where a value are greater than allowed
64142 CannotBeGT() string
143
144 // MustBeOfType returns a format-string to format an error where a value does not match the required type
65145 MustBeOfType() string
146
147 // MustBeValidRegex returns a format-string to format an error where a regex is invalid
66148 MustBeValidRegex() string
149
150 // MustBeValidFormat returns a format-string to format an error where a value does not match the expected format
67151 MustBeValidFormat() string
152
153 // MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0
68154 MustBeGTEZero() string
155
156 // KeyCannotBeGreaterThan returns a format-string to format an error where a key is greater than the maximum allowed
69157 KeyCannotBeGreaterThan() string
158
159 // KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type
70160 KeyItemsMustBeOfType() string
161
162 // KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique
71163 KeyItemsMustBeUnique() string
164
165 // ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error
72166 ReferenceMustBeCanonical() string
167
168 // NotAValidType returns a format-string to format an invalid type error
73169 NotAValidType() string
170
171 // Duplicated returns a format-string to format an error where types are duplicated
74172 Duplicated() string
75 httpBadStatus() string
76
77 // ErrorFormat
173
174 // HttpBadStatus returns a format-string for errors when loading a schema using HTTP
175 HttpBadStatus() string
176
177 // ParseError returns a format-string for JSON parsing errors
178 ParseError() string
179
180 // ConditionThen returns a format-string for ConditionThenError errors
181 ConditionThen() string
182
183 // ConditionElse returns a format-string for ConditionElseError errors
184 ConditionElse() string
185
186 // ErrorFormat returns a format string for errors
78187 ErrorFormat() string
79188 }
80189
82191 DefaultLocale struct{}
83192 )
84193
194 // False returns a format-string for "false" schema validation errors
195 func (l DefaultLocale) False() string {
196 return "False always fails validation"
197 }
198
199 // Required returns a format-string for "required" schema validation errors
85200 func (l DefaultLocale) Required() string {
86201 return `{{.property}} is required`
87202 }
88203
204 // InvalidType returns a format-string for "invalid type" schema validation errors
89205 func (l DefaultLocale) InvalidType() string {
90206 return `Invalid type. Expected: {{.expected}}, given: {{.given}}`
91207 }
92208
209 // NumberAnyOf returns a format-string for "anyOf" schema validation errors
93210 func (l DefaultLocale) NumberAnyOf() string {
94211 return `Must validate at least one schema (anyOf)`
95212 }
96213
214 // NumberOneOf returns a format-string for "oneOf" schema validation errors
97215 func (l DefaultLocale) NumberOneOf() string {
98216 return `Must validate one and only one schema (oneOf)`
99217 }
100218
219 // NumberAllOf returns a format-string for "allOf" schema validation errors
101220 func (l DefaultLocale) NumberAllOf() string {
102221 return `Must validate all the schemas (allOf)`
103222 }
104223
224 // NumberNot returns a format-string to format a NumberNotError
105225 func (l DefaultLocale) NumberNot() string {
106226 return `Must not validate the schema (not)`
107227 }
108228
229 // MissingDependency returns a format-string for "missing dependency" schema validation errors
109230 func (l DefaultLocale) MissingDependency() string {
110231 return `Has a dependency on {{.dependency}}`
111232 }
112233
234 // Internal returns a format-string for internal errors
113235 func (l DefaultLocale) Internal() string {
114236 return `Internal Error {{.error}}`
115237 }
116238
239 // Const returns a format-string to format a ConstError
240 func (l DefaultLocale) Const() string {
241 return `{{.field}} does not match: {{.allowed}}`
242 }
243
244 // Enum returns a format-string to format an EnumError
117245 func (l DefaultLocale) Enum() string {
118246 return `{{.field}} must be one of the following: {{.allowed}}`
119247 }
120248
249 // ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError
121250 func (l DefaultLocale) ArrayNoAdditionalItems() string {
122251 return `No additional items allowed on array`
123252 }
124253
254 // ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema
125255 func (l DefaultLocale) ArrayNotEnoughItems() string {
126256 return `Not enough items on array to match positional list of schema`
127257 }
128258
259 // ArrayMinItems returns a format-string to format an ArrayMinItemsError
129260 func (l DefaultLocale) ArrayMinItems() string {
130261 return `Array must have at least {{.min}} items`
131262 }
132263
264 // ArrayMaxItems returns a format-string to format an ArrayMaxItemsError
133265 func (l DefaultLocale) ArrayMaxItems() string {
134266 return `Array must have at most {{.max}} items`
135267 }
136268
269 // Unique returns a format-string to format an ItemsMustBeUniqueError
137270 func (l DefaultLocale) Unique() string {
138 return `{{.type}} items must be unique`
139 }
140
271 return `{{.type}} items[{{.i}},{{.j}}] must be unique`
272 }
273
274 // ArrayContains returns a format-string to format an ArrayContainsError
275 func (l DefaultLocale) ArrayContains() string {
276 return `At least one of the items must match`
277 }
278
279 // ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError
141280 func (l DefaultLocale) ArrayMinProperties() string {
142281 return `Must have at least {{.min}} properties`
143282 }
144283
284 // ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError
145285 func (l DefaultLocale) ArrayMaxProperties() string {
146286 return `Must have at most {{.max}} properties`
147287 }
148288
289 // AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError
149290 func (l DefaultLocale) AdditionalPropertyNotAllowed() string {
150291 return `Additional property {{.property}} is not allowed`
151292 }
152293
294 // InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError
153295 func (l DefaultLocale) InvalidPropertyPattern() string {
154296 return `Property "{{.property}}" does not match pattern {{.pattern}}`
155297 }
156298
299 // InvalidPropertyName returns a format-string to format an InvalidPropertyNameError
300 func (l DefaultLocale) InvalidPropertyName() string {
301 return `Property name of "{{.property}}" does not match`
302 }
303
304 // StringGTE returns a format-string to format an StringLengthGTEError
157305 func (l DefaultLocale) StringGTE() string {
158306 return `String length must be greater than or equal to {{.min}}`
159307 }
160308
309 // StringLTE returns a format-string to format an StringLengthLTEError
161310 func (l DefaultLocale) StringLTE() string {
162311 return `String length must be less than or equal to {{.max}}`
163312 }
164313
314 // DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError
165315 func (l DefaultLocale) DoesNotMatchPattern() string {
166316 return `Does not match pattern '{{.pattern}}'`
167317 }
168318
319 // DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError
169320 func (l DefaultLocale) DoesNotMatchFormat() string {
170321 return `Does not match format '{{.format}}'`
171322 }
172323
324 // MultipleOf returns a format-string to format an MultipleOfError
173325 func (l DefaultLocale) MultipleOf() string {
174326 return `Must be a multiple of {{.multiple}}`
175327 }
176328
329 // NumberGTE returns the format string to format a NumberGTEError
177330 func (l DefaultLocale) NumberGTE() string {
178331 return `Must be greater than or equal to {{.min}}`
179332 }
180333
334 // NumberGT returns the format string to format a NumberGTError
181335 func (l DefaultLocale) NumberGT() string {
182336 return `Must be greater than {{.min}}`
183337 }
184338
339 // NumberLTE returns the format string to format a NumberLTEError
185340 func (l DefaultLocale) NumberLTE() string {
186341 return `Must be less than or equal to {{.max}}`
187342 }
188343
344 // NumberLT returns the format string to format a NumberLTError
189345 func (l DefaultLocale) NumberLT() string {
190346 return `Must be less than {{.max}}`
191347 }
192348
193349 // Schema validators
350
351 // RegexPattern returns a format-string to format a regex-pattern error
194352 func (l DefaultLocale) RegexPattern() string {
195353 return `Invalid regex pattern '{{.pattern}}'`
196354 }
197355
356 // GreaterThanZero returns a format-string to format an error where a number must be greater than zero
198357 func (l DefaultLocale) GreaterThanZero() string {
199358 return `{{.number}} must be strictly greater than 0`
200359 }
201360
361 // MustBeOfA returns a format-string to format an error where a value is of the wrong type
202362 func (l DefaultLocale) MustBeOfA() string {
203363 return `{{.x}} must be of a {{.y}}`
204364 }
205365
366 // MustBeOfAn returns a format-string to format an error where a value is of the wrong type
206367 func (l DefaultLocale) MustBeOfAn() string {
207368 return `{{.x}} must be of an {{.y}}`
208369 }
209370
371 // CannotBeUsedWithout returns a format-string to format a "cannot be used without" error
210372 func (l DefaultLocale) CannotBeUsedWithout() string {
211373 return `{{.x}} cannot be used without {{.y}}`
212374 }
213375
376 // CannotBeGT returns a format-string to format an error where a value are greater than allowed
214377 func (l DefaultLocale) CannotBeGT() string {
215378 return `{{.x}} cannot be greater than {{.y}}`
216379 }
217380
381 // MustBeOfType returns a format-string to format an error where a value does not match the required type
218382 func (l DefaultLocale) MustBeOfType() string {
219383 return `{{.key}} must be of type {{.type}}`
220384 }
221385
386 // MustBeValidRegex returns a format-string to format an error where a regex is invalid
222387 func (l DefaultLocale) MustBeValidRegex() string {
223388 return `{{.key}} must be a valid regex`
224389 }
225390
391 // MustBeValidFormat returns a format-string to format an error where a value does not match the expected format
226392 func (l DefaultLocale) MustBeValidFormat() string {
227393 return `{{.key}} must be a valid format {{.given}}`
228394 }
229395
396 // MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0
230397 func (l DefaultLocale) MustBeGTEZero() string {
231398 return `{{.key}} must be greater than or equal to 0`
232399 }
233400
401 // KeyCannotBeGreaterThan returns a format-string to format an error where a value is greater than the maximum allowed
234402 func (l DefaultLocale) KeyCannotBeGreaterThan() string {
235403 return `{{.key}} cannot be greater than {{.y}}`
236404 }
237405
406 // KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type
238407 func (l DefaultLocale) KeyItemsMustBeOfType() string {
239408 return `{{.key}} items must be {{.type}}`
240409 }
241410
411 // KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique
242412 func (l DefaultLocale) KeyItemsMustBeUnique() string {
243413 return `{{.key}} items must be unique`
244414 }
245415
416 // ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error
246417 func (l DefaultLocale) ReferenceMustBeCanonical() string {
247418 return `Reference {{.reference}} must be canonical`
248419 }
249420
421 // NotAValidType returns a format-string to format an invalid type error
250422 func (l DefaultLocale) NotAValidType() string {
251 return `{{.type}} is not a valid type -- `
252 }
253
423 return `has a primitive type that is NOT VALID -- given: {{.given}} Expected valid values are:{{.expected}}`
424 }
425
426 // Duplicated returns a format-string to format an error where types are duplicated
254427 func (l DefaultLocale) Duplicated() string {
255428 return `{{.type}} type is duplicated`
256429 }
257430
258 func (l DefaultLocale) httpBadStatus() string {
431 // HttpBadStatus returns a format-string for errors when loading a schema using HTTP
432 func (l DefaultLocale) HttpBadStatus() string {
259433 return `Could not read schema from HTTP, response status is {{.status}}`
260434 }
261435
436 // ErrorFormat returns a format string for errors
262437 // Replacement options: field, description, context, value
263438 func (l DefaultLocale) ErrorFormat() string {
264439 return `{{.field}}: {{.description}}`
265440 }
266441
442 // ParseError returns a format-string for JSON parsing errors
443 func (l DefaultLocale) ParseError() string {
444 return `Expected: {{.expected}}, given: Invalid JSON`
445 }
446
447 // ConditionThen returns a format-string for ConditionThenError errors
448 // If/Else
449 func (l DefaultLocale) ConditionThen() string {
450 return `Must validate "then" as "if" was valid`
451 }
452
453 // ConditionElse returns a format-string for ConditionElseError errors
454 func (l DefaultLocale) ConditionElse() string {
455 return `Must validate "else" as "if" was not valid`
456 }
457
458 // constants
267459 const (
268460 STRING_NUMBER = "number"
269461 STRING_ARRAY_OF_STRINGS = "array of strings"
270462 STRING_ARRAY_OF_SCHEMAS = "array of schemas"
271 STRING_SCHEMA = "schema"
463 STRING_SCHEMA = "valid schema"
272464 STRING_SCHEMA_OR_ARRAY_OF_STRINGS = "schema or array of strings"
273465 STRING_PROPERTIES = "properties"
274466 STRING_DEPENDENCY = "dependency"
3636
3737 // ResultError is the interface that library errors must implement
3838 ResultError interface {
39 // Field returns the field name without the root context
40 // i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName
3941 Field() string
42 // SetType sets the error-type
4043 SetType(string)
44 // Type returns the error-type
4145 Type() string
42 SetContext(*jsonContext)
43 Context() *jsonContext
46 // SetContext sets the JSON-context for the error
47 SetContext(*JsonContext)
48 // Context returns the JSON-context of the error
49 Context() *JsonContext
50 // SetDescription sets a description for the error
4451 SetDescription(string)
52 // Description returns the description of the error
4553 Description() string
54 // SetDescriptionFormat sets the format for the description in the default text/template format
55 SetDescriptionFormat(string)
56 // DescriptionFormat returns the format for the description in the default text/template format
57 DescriptionFormat() string
58 // SetValue sets the value related to the error
4659 SetValue(interface{})
60 // Value returns the value related to the error
4761 Value() interface{}
62 // SetDetails sets the details specific to the error
4863 SetDetails(ErrorDetails)
64 // Details returns details about the error
4965 Details() ErrorDetails
66 // String returns a string representation of the error
5067 String() string
5168 }
5269
5471 // ResultErrorFields implements the ResultError interface, so custom errors
5572 // can be defined by just embedding this type
5673 ResultErrorFields struct {
57 errorType string // A string with the type of error (i.e. invalid_type)
58 context *jsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
59 description string // A human readable error message
60 value interface{} // Value given by the JSON file that is the source of the error
61 details ErrorDetails
62 }
63
74 errorType string // A string with the type of error (i.e. invalid_type)
75 context *JsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
76 description string // A human readable error message
77 descriptionFormat string // A format for human readable error message
78 value interface{} // Value given by the JSON file that is the source of the error
79 details ErrorDetails
80 }
81
82 // Result holds the result of a validation
6483 Result struct {
6584 errors []ResultError
6685 // Scores how well the validation matched. Useful in generating
6988 }
7089 )
7190
72 // Field outputs the field name without the root context
91 // Field returns the field name without the root context
7392 // i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName
7493 func (v *ResultErrorFields) Field() string {
75 if p, ok := v.Details()["property"]; ok {
76 if str, isString := p.(string); isString {
77 return str
78 }
79 }
80
8194 return strings.TrimPrefix(v.context.String(), STRING_ROOT_SCHEMA_PROPERTY+".")
8295 }
8396
97 // SetType sets the error-type
8498 func (v *ResultErrorFields) SetType(errorType string) {
8599 v.errorType = errorType
86100 }
87101
102 // Type returns the error-type
88103 func (v *ResultErrorFields) Type() string {
89104 return v.errorType
90105 }
91106
92 func (v *ResultErrorFields) SetContext(context *jsonContext) {
107 // SetContext sets the JSON-context for the error
108 func (v *ResultErrorFields) SetContext(context *JsonContext) {
93109 v.context = context
94110 }
95111
96 func (v *ResultErrorFields) Context() *jsonContext {
112 // Context returns the JSON-context of the error
113 func (v *ResultErrorFields) Context() *JsonContext {
97114 return v.context
98115 }
99116
117 // SetDescription sets a description for the error
100118 func (v *ResultErrorFields) SetDescription(description string) {
101119 v.description = description
102120 }
103121
122 // Description returns the description of the error
104123 func (v *ResultErrorFields) Description() string {
105124 return v.description
106125 }
107126
127 // SetDescriptionFormat sets the format for the description in the default text/template format
128 func (v *ResultErrorFields) SetDescriptionFormat(descriptionFormat string) {
129 v.descriptionFormat = descriptionFormat
130 }
131
132 // DescriptionFormat returns the format for the description in the default text/template format
133 func (v *ResultErrorFields) DescriptionFormat() string {
134 return v.descriptionFormat
135 }
136
137 // SetValue sets the value related to the error
108138 func (v *ResultErrorFields) SetValue(value interface{}) {
109139 v.value = value
110140 }
111141
142 // Value returns the value related to the error
112143 func (v *ResultErrorFields) Value() interface{} {
113144 return v.value
114145 }
115146
147 // SetDetails sets the details specific to the error
116148 func (v *ResultErrorFields) SetDetails(details ErrorDetails) {
117149 v.details = details
118150 }
119151
152 // Details returns details about the error
120153 func (v *ResultErrorFields) Details() ErrorDetails {
121154 return v.details
122155 }
123156
157 // String returns a string representation of the error
124158 func (v ResultErrorFields) String() string {
125159 // as a fallback, the value is displayed go style
126160 valueString := fmt.Sprintf("%v", v.value)
129163 if v.value == nil {
130164 valueString = TYPE_NULL
131165 } else {
132 if vs, err := marshalToJsonString(v.value); err == nil {
166 if vs, err := marshalToJSONString(v.value); err == nil {
133167 if vs == nil {
134168 valueString = TYPE_NULL
135169 } else {
146180 })
147181 }
148182
183 // Valid indicates if no errors were found
149184 func (v *Result) Valid() bool {
150185 return len(v.errors) == 0
151186 }
152187
188 // Errors returns the errors that were found
153189 func (v *Result) Errors() []ResultError {
154190 return v.errors
155191 }
156192
157 func (v *Result) addError(err ResultError, context *jsonContext, value interface{}, details ErrorDetails) {
193 // AddError appends a fully filled error to the error set
194 // SetDescription() will be called with the result of the parsed err.DescriptionFormat()
195 func (v *Result) AddError(err ResultError, details ErrorDetails) {
196 if _, exists := details["context"]; !exists && err.Context() != nil {
197 details["context"] = err.Context().String()
198 }
199
200 err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
201
202 v.errors = append(v.errors, err)
203 }
204
205 func (v *Result) addInternalError(err ResultError, context *JsonContext, value interface{}, details ErrorDetails) {
158206 newError(err, context, value, Locale, details)
159207 v.errors = append(v.errors, err)
160208 v.score -= 2 // results in a net -1 when added to the +1 we get at the end of the validation function
2626 package gojsonschema
2727
2828 import (
29 // "encoding/json"
3029 "errors"
30 "math/big"
3131 "reflect"
3232 "regexp"
33 "text/template"
3334
3435 "github.com/xeipuuv/gojsonreference"
3536 )
3839 // Locale is the default locale to use
3940 // Library users can overwrite with their own implementation
4041 Locale locale = DefaultLocale{}
42
43 // ErrorTemplateFuncs allows you to define custom template funcs for use in localization.
44 ErrorTemplateFuncs template.FuncMap
4145 )
4246
47 // NewSchema instances a schema using the given JSONLoader
4348 func NewSchema(l JSONLoader) (*Schema, error) {
44 ref, err := l.JsonReference()
45 if err != nil {
46 return nil, err
47 }
48
49 d := Schema{}
50 d.pool = newSchemaPool(l.LoaderFactory())
51 d.documentReference = ref
52 d.referencePool = newSchemaReferencePool()
53
54 var doc interface{}
55 if ref.String() != "" {
56 // Get document from schema pool
57 spd, err := d.pool.GetDocument(d.documentReference)
58 if err != nil {
59 return nil, err
60 }
61 doc = spd.Document
62 } else {
63 // Load JSON directly
64 doc, err = l.LoadJSON()
65 if err != nil {
66 return nil, err
67 }
68 d.pool.SetStandaloneDocument(doc)
69 }
70
71 err = d.parse(doc)
72 if err != nil {
73 return nil, err
74 }
75
76 return &d, nil
49 return NewSchemaLoader().Compile(l)
7750 }
7851
52 // Schema holds a schema
7953 type Schema struct {
8054 documentReference gojsonreference.JsonReference
8155 rootSchema *subSchema
8357 referencePool *schemaReferencePool
8458 }
8559
86 func (d *Schema) parse(document interface{}) error {
87 d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY}
60 func (d *Schema) parse(document interface{}, draft Draft) error {
61 d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY, draft: &draft}
8862 return d.parseSchema(document, d.rootSchema)
8963 }
9064
65 // SetRootSchemaName sets the root-schema name
9166 func (d *Schema) SetRootSchemaName(name string) {
9267 d.rootSchema.property = name
9368 }
10075 //
10176 func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error {
10277
78 if currentSchema.draft == nil {
79 if currentSchema.parent == nil {
80 return errors.New("Draft not set")
81 }
82 currentSchema.draft = currentSchema.parent.draft
83 }
84
85 // As of draft 6 "true" is equivalent to an empty schema "{}" and false equals "{"not":{}}"
86 if *currentSchema.draft >= Draft6 && isKind(documentNode, reflect.Bool) {
87 b := documentNode.(bool)
88 currentSchema.pass = &b
89 return nil
90 }
91
10392 if !isKind(documentNode, reflect.Map) {
93 return errors.New(formatErrorDescription(
94 Locale.ParseError(),
95 ErrorDetails{
96 "expected": STRING_SCHEMA,
97 },
98 ))
99 }
100
101 m := documentNode.(map[string]interface{})
102
103 if currentSchema.parent == nil {
104 currentSchema.ref = &d.documentReference
105 currentSchema.id = &d.documentReference
106 }
107
108 if currentSchema.id == nil && currentSchema.parent != nil {
109 currentSchema.id = currentSchema.parent.id
110 }
111
112 // In draft 6 the id keyword was renamed to $id
113 // Hybrid mode uses the old id by default
114 var keyID string
115
116 switch *currentSchema.draft {
117 case Draft4:
118 keyID = KEY_ID
119 case Hybrid:
120 keyID = KEY_ID_NEW
121 if existsMapKey(m, KEY_ID) {
122 keyID = KEY_ID
123 }
124 default:
125 keyID = KEY_ID_NEW
126 }
127 if existsMapKey(m, keyID) && !isKind(m[keyID], reflect.String) {
104128 return errors.New(formatErrorDescription(
105129 Locale.InvalidType(),
106130 ErrorDetails{
107 "expected": TYPE_OBJECT,
108 "given": STRING_SCHEMA,
131 "expected": TYPE_STRING,
132 "given": keyID,
109133 },
110134 ))
111135 }
112
113 m := documentNode.(map[string]interface{})
114
115 if currentSchema == d.rootSchema {
116 currentSchema.ref = &d.documentReference
117 }
118
119 // $subSchema
120 if existsMapKey(m, KEY_SCHEMA) {
121 if !isKind(m[KEY_SCHEMA], reflect.String) {
136 if k, ok := m[keyID].(string); ok {
137 jsonReference, err := gojsonreference.NewJsonReference(k)
138 if err != nil {
139 return err
140 }
141 if currentSchema == d.rootSchema {
142 currentSchema.id = &jsonReference
143 } else {
144 ref, err := currentSchema.parent.id.Inherits(jsonReference)
145 if err != nil {
146 return err
147 }
148 currentSchema.id = ref
149 }
150 }
151
152 // definitions
153 if existsMapKey(m, KEY_DEFINITIONS) {
154 if isKind(m[KEY_DEFINITIONS], reflect.Map, reflect.Bool) {
155 for _, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) {
156 if isKind(dv, reflect.Map, reflect.Bool) {
157
158 newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema}
159
160 err := d.parseSchema(dv, newSchema)
161
162 if err != nil {
163 return err
164 }
165 } else {
166 return errors.New(formatErrorDescription(
167 Locale.InvalidType(),
168 ErrorDetails{
169 "expected": STRING_ARRAY_OF_SCHEMAS,
170 "given": KEY_DEFINITIONS,
171 },
172 ))
173 }
174 }
175 } else {
122176 return errors.New(formatErrorDescription(
123177 Locale.InvalidType(),
124178 ErrorDetails{
125 "expected": TYPE_STRING,
126 "given": KEY_SCHEMA,
179 "expected": STRING_ARRAY_OF_SCHEMAS,
180 "given": KEY_DEFINITIONS,
127181 },
128182 ))
129183 }
130 schemaRef := m[KEY_SCHEMA].(string)
131 schemaReference, err := gojsonreference.NewJsonReference(schemaRef)
132 currentSchema.subSchema = &schemaReference
133 if err != nil {
134 return err
135 }
184
185 }
186
187 // title
188 if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) {
189 return errors.New(formatErrorDescription(
190 Locale.InvalidType(),
191 ErrorDetails{
192 "expected": TYPE_STRING,
193 "given": KEY_TITLE,
194 },
195 ))
196 }
197 if k, ok := m[KEY_TITLE].(string); ok {
198 currentSchema.title = &k
199 }
200
201 // description
202 if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) {
203 return errors.New(formatErrorDescription(
204 Locale.InvalidType(),
205 ErrorDetails{
206 "expected": TYPE_STRING,
207 "given": KEY_DESCRIPTION,
208 },
209 ))
210 }
211 if k, ok := m[KEY_DESCRIPTION].(string); ok {
212 currentSchema.description = &k
136213 }
137214
138215 // $ref
145222 },
146223 ))
147224 }
225
148226 if k, ok := m[KEY_REF].(string); ok {
149227
150228 jsonReference, err := gojsonreference.NewJsonReference(k)
152230 return err
153231 }
154232
155 if jsonReference.HasFullUrl {
156 currentSchema.ref = &jsonReference
157 } else {
158 inheritedReference, err := currentSchema.ref.Inherits(jsonReference)
233 currentSchema.ref = &jsonReference
234
235 if sch, ok := d.referencePool.Get(currentSchema.ref.String()); ok {
236 currentSchema.refSchema = sch
237 } else {
238 err := d.parseReference(documentNode, currentSchema)
239
159240 if err != nil {
160241 return err
161242 }
162243
163 currentSchema.ref = inheritedReference
164 }
165
166 if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok {
167 currentSchema.refSchema = sch
168
169 } else {
170 err := d.parseReference(documentNode, currentSchema, k)
171 if err != nil {
172 return err
173 }
174
175244 return nil
176245 }
177 }
178
179 // definitions
180 if existsMapKey(m, KEY_DEFINITIONS) {
181 if isKind(m[KEY_DEFINITIONS], reflect.Map) {
182 currentSchema.definitions = make(map[string]*subSchema)
183 for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) {
184 if isKind(dv, reflect.Map) {
185 newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref}
186 currentSchema.definitions[dk] = newSchema
187 err := d.parseSchema(dv, newSchema)
188 if err != nil {
189 return errors.New(err.Error())
190 }
191 } else {
192 return errors.New(formatErrorDescription(
193 Locale.InvalidType(),
194 ErrorDetails{
195 "expected": STRING_ARRAY_OF_SCHEMAS,
196 "given": KEY_DEFINITIONS,
197 },
198 ))
199 }
200 }
201 } else {
202 return errors.New(formatErrorDescription(
203 Locale.InvalidType(),
204 ErrorDetails{
205 "expected": STRING_ARRAY_OF_SCHEMAS,
206 "given": KEY_DEFINITIONS,
207 },
208 ))
209 }
210
211 }
212
213 // id
214 if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) {
215 return errors.New(formatErrorDescription(
216 Locale.InvalidType(),
217 ErrorDetails{
218 "expected": TYPE_STRING,
219 "given": KEY_ID,
220 },
221 ))
222 }
223 if k, ok := m[KEY_ID].(string); ok {
224 currentSchema.id = &k
225 }
226
227 // title
228 if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) {
229 return errors.New(formatErrorDescription(
230 Locale.InvalidType(),
231 ErrorDetails{
232 "expected": TYPE_STRING,
233 "given": KEY_TITLE,
234 },
235 ))
236 }
237 if k, ok := m[KEY_TITLE].(string); ok {
238 currentSchema.title = &k
239 }
240
241 // description
242 if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) {
243 return errors.New(formatErrorDescription(
244 Locale.InvalidType(),
245 ErrorDetails{
246 "expected": TYPE_STRING,
247 "given": KEY_DESCRIPTION,
248 },
249 ))
250 }
251 if k, ok := m[KEY_DESCRIPTION].(string); ok {
252 currentSchema.description = &k
253246 }
254247
255248 // type
273266 "given": KEY_TYPE,
274267 },
275268 ))
276 } else {
277 currentSchema.types.Add(typeInArray.(string))
269 }
270 if err := currentSchema.types.Add(typeInArray.(string)); err != nil {
271 return err
278272 }
279273 }
280274
353347 }
354348 }
355349
350 // propertyNames
351 if existsMapKey(m, KEY_PROPERTY_NAMES) && *currentSchema.draft >= Draft6 {
352 if isKind(m[KEY_PROPERTY_NAMES], reflect.Map, reflect.Bool) {
353 newSchema := &subSchema{property: KEY_PROPERTY_NAMES, parent: currentSchema, ref: currentSchema.ref}
354 currentSchema.propertyNames = newSchema
355 err := d.parseSchema(m[KEY_PROPERTY_NAMES], newSchema)
356 if err != nil {
357 return err
358 }
359 } else {
360 return errors.New(formatErrorDescription(
361 Locale.InvalidType(),
362 ErrorDetails{
363 "expected": STRING_SCHEMA,
364 "given": KEY_PATTERN_PROPERTIES,
365 },
366 ))
367 }
368 }
369
356370 // dependencies
357371 if existsMapKey(m, KEY_DEPENDENCIES) {
358372 err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema)
365379 if existsMapKey(m, KEY_ITEMS) {
366380 if isKind(m[KEY_ITEMS], reflect.Slice) {
367381 for _, itemElement := range m[KEY_ITEMS].([]interface{}) {
368 if isKind(itemElement, reflect.Map) {
382 if isKind(itemElement, reflect.Map, reflect.Bool) {
369383 newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
370384 newSchema.ref = currentSchema.ref
371 currentSchema.AddItemsChild(newSchema)
385 currentSchema.itemsChildren = append(currentSchema.itemsChildren, newSchema)
372386 err := d.parseSchema(itemElement, newSchema)
373387 if err != nil {
374388 return err
384398 }
385399 currentSchema.itemsChildrenIsSingleSchema = false
386400 }
387 } else if isKind(m[KEY_ITEMS], reflect.Map) {
401 } else if isKind(m[KEY_ITEMS], reflect.Map, reflect.Bool) {
388402 newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
389403 newSchema.ref = currentSchema.ref
390 currentSchema.AddItemsChild(newSchema)
404 currentSchema.itemsChildren = append(currentSchema.itemsChildren, newSchema)
391405 err := d.parseSchema(m[KEY_ITEMS], newSchema)
392406 if err != nil {
393407 return err
439453 },
440454 ))
441455 }
442 if *multipleOfValue <= 0 {
456 if multipleOfValue.Cmp(big.NewRat(0, 1)) <= 0 {
443457 return errors.New(formatErrorDescription(
444458 Locale.GreaterThanZero(),
445459 ErrorDetails{"number": KEY_MULTIPLE_OF},
460474 }
461475
462476 if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) {
463 if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
477 switch *currentSchema.draft {
478 case Draft4:
479 if !isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
480 return errors.New(formatErrorDescription(
481 Locale.InvalidType(),
482 ErrorDetails{
483 "expected": TYPE_BOOLEAN,
484 "given": KEY_EXCLUSIVE_MINIMUM,
485 },
486 ))
487 }
464488 if currentSchema.minimum == nil {
465489 return errors.New(formatErrorDescription(
466490 Locale.CannotBeUsedWithout(),
467491 ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM},
468492 ))
469493 }
470 exclusiveMinimumValue := m[KEY_EXCLUSIVE_MINIMUM].(bool)
471 currentSchema.exclusiveMinimum = exclusiveMinimumValue
472 } else {
473 return errors.New(formatErrorDescription(
474 Locale.MustBeOfA(),
475 ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": TYPE_BOOLEAN},
476 ))
494 if m[KEY_EXCLUSIVE_MINIMUM].(bool) {
495 currentSchema.exclusiveMinimum = currentSchema.minimum
496 currentSchema.minimum = nil
497 }
498 case Hybrid:
499 if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
500 if currentSchema.minimum == nil {
501 return errors.New(formatErrorDescription(
502 Locale.CannotBeUsedWithout(),
503 ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM},
504 ))
505 }
506 if m[KEY_EXCLUSIVE_MINIMUM].(bool) {
507 currentSchema.exclusiveMinimum = currentSchema.minimum
508 currentSchema.minimum = nil
509 }
510 } else if isJSONNumber(m[KEY_EXCLUSIVE_MINIMUM]) {
511 currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM])
512 } else {
513 return errors.New(formatErrorDescription(
514 Locale.InvalidType(),
515 ErrorDetails{
516 "expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER,
517 "given": KEY_EXCLUSIVE_MINIMUM,
518 },
519 ))
520 }
521 default:
522 if isJSONNumber(m[KEY_EXCLUSIVE_MINIMUM]) {
523 currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM])
524 } else {
525 return errors.New(formatErrorDescription(
526 Locale.InvalidType(),
527 ErrorDetails{
528 "expected": TYPE_NUMBER,
529 "given": KEY_EXCLUSIVE_MINIMUM,
530 },
531 ))
532 }
477533 }
478534 }
479535
489545 }
490546
491547 if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) {
492 if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
548 switch *currentSchema.draft {
549 case Draft4:
550 if !isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
551 return errors.New(formatErrorDescription(
552 Locale.InvalidType(),
553 ErrorDetails{
554 "expected": TYPE_BOOLEAN,
555 "given": KEY_EXCLUSIVE_MAXIMUM,
556 },
557 ))
558 }
493559 if currentSchema.maximum == nil {
494560 return errors.New(formatErrorDescription(
495561 Locale.CannotBeUsedWithout(),
496562 ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM},
497563 ))
498564 }
499 exclusiveMaximumValue := m[KEY_EXCLUSIVE_MAXIMUM].(bool)
500 currentSchema.exclusiveMaximum = exclusiveMaximumValue
501 } else {
502 return errors.New(formatErrorDescription(
503 Locale.MustBeOfA(),
504 ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": STRING_NUMBER},
505 ))
506 }
507 }
508
509 if currentSchema.minimum != nil && currentSchema.maximum != nil {
510 if *currentSchema.minimum > *currentSchema.maximum {
511 return errors.New(formatErrorDescription(
512 Locale.CannotBeGT(),
513 ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM},
514 ))
565 if m[KEY_EXCLUSIVE_MAXIMUM].(bool) {
566 currentSchema.exclusiveMaximum = currentSchema.maximum
567 currentSchema.maximum = nil
568 }
569 case Hybrid:
570 if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
571 if currentSchema.maximum == nil {
572 return errors.New(formatErrorDescription(
573 Locale.CannotBeUsedWithout(),
574 ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM},
575 ))
576 }
577 if m[KEY_EXCLUSIVE_MAXIMUM].(bool) {
578 currentSchema.exclusiveMaximum = currentSchema.maximum
579 currentSchema.maximum = nil
580 }
581 } else if isJSONNumber(m[KEY_EXCLUSIVE_MAXIMUM]) {
582 currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM])
583 } else {
584 return errors.New(formatErrorDescription(
585 Locale.InvalidType(),
586 ErrorDetails{
587 "expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER,
588 "given": KEY_EXCLUSIVE_MAXIMUM,
589 },
590 ))
591 }
592 default:
593 if isJSONNumber(m[KEY_EXCLUSIVE_MAXIMUM]) {
594 currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM])
595 } else {
596 return errors.New(formatErrorDescription(
597 Locale.InvalidType(),
598 ErrorDetails{
599 "expected": TYPE_NUMBER,
600 "given": KEY_EXCLUSIVE_MAXIMUM,
601 },
602 ))
603 }
515604 }
516605 }
517606
580669
581670 if existsMapKey(m, KEY_FORMAT) {
582671 formatString, ok := m[KEY_FORMAT].(string)
583 if ok && FormatCheckers.Has(formatString) {
584 currentSchema.format = formatString
585 } else {
586 return errors.New(formatErrorDescription(
587 Locale.MustBeValidFormat(),
588 ErrorDetails{"key": KEY_FORMAT, "given": m[KEY_FORMAT]},
589 ))
590 }
672 if !ok {
673 return errors.New(formatErrorDescription(
674 Locale.MustBeOfType(),
675 ErrorDetails{"key": KEY_FORMAT, "type": TYPE_STRING},
676 ))
677 }
678 currentSchema.format = formatString
591679 }
592680
593681 // validation : object
640728 requiredValues := m[KEY_REQUIRED].([]interface{})
641729 for _, requiredValue := range requiredValues {
642730 if isKind(requiredValue, reflect.String) {
643 err := currentSchema.AddRequired(requiredValue.(string))
644 if err != nil {
645 return err
731 if isStringInSlice(currentSchema.required, requiredValue.(string)) {
732 return errors.New(formatErrorDescription(
733 Locale.KeyItemsMustBeUnique(),
734 ErrorDetails{"key": KEY_REQUIRED},
735 ))
646736 }
737 currentSchema.required = append(currentSchema.required, requiredValue.(string))
647738 } else {
648739 return errors.New(formatErrorDescription(
649740 Locale.KeyItemsMustBeOfType(),
706797 }
707798 }
708799
800 if existsMapKey(m, KEY_CONTAINS) && *currentSchema.draft >= Draft6 {
801 newSchema := &subSchema{property: KEY_CONTAINS, parent: currentSchema, ref: currentSchema.ref}
802 currentSchema.contains = newSchema
803 err := d.parseSchema(m[KEY_CONTAINS], newSchema)
804 if err != nil {
805 return err
806 }
807 }
808
709809 // validation : all
810
811 if existsMapKey(m, KEY_CONST) && *currentSchema.draft >= Draft6 {
812 is, err := marshalWithoutNumber(m[KEY_CONST])
813 if err != nil {
814 return err
815 }
816 currentSchema._const = is
817 }
710818
711819 if existsMapKey(m, KEY_ENUM) {
712820 if isKind(m[KEY_ENUM], reflect.Slice) {
713821 for _, v := range m[KEY_ENUM].([]interface{}) {
714 err := currentSchema.AddEnum(v)
822 is, err := marshalWithoutNumber(v)
715823 if err != nil {
716824 return err
717825 }
826 if isStringInSlice(currentSchema.enum, *is) {
827 return errors.New(formatErrorDescription(
828 Locale.KeyItemsMustBeUnique(),
829 ErrorDetails{"key": KEY_ENUM},
830 ))
831 }
832 currentSchema.enum = append(currentSchema.enum, *is)
718833 }
719834 } else {
720835 return errors.New(formatErrorDescription(
730845 if isKind(m[KEY_ONE_OF], reflect.Slice) {
731846 for _, v := range m[KEY_ONE_OF].([]interface{}) {
732847 newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref}
733 currentSchema.AddOneOf(newSchema)
848 currentSchema.oneOf = append(currentSchema.oneOf, newSchema)
734849 err := d.parseSchema(v, newSchema)
735850 if err != nil {
736851 return err
748863 if isKind(m[KEY_ANY_OF], reflect.Slice) {
749864 for _, v := range m[KEY_ANY_OF].([]interface{}) {
750865 newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref}
751 currentSchema.AddAnyOf(newSchema)
866 currentSchema.anyOf = append(currentSchema.anyOf, newSchema)
752867 err := d.parseSchema(v, newSchema)
753868 if err != nil {
754869 return err
766881 if isKind(m[KEY_ALL_OF], reflect.Slice) {
767882 for _, v := range m[KEY_ALL_OF].([]interface{}) {
768883 newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref}
769 currentSchema.AddAllOf(newSchema)
884 currentSchema.allOf = append(currentSchema.allOf, newSchema)
770885 err := d.parseSchema(v, newSchema)
771886 if err != nil {
772887 return err
781896 }
782897
783898 if existsMapKey(m, KEY_NOT) {
784 if isKind(m[KEY_NOT], reflect.Map) {
899 if isKind(m[KEY_NOT], reflect.Map, reflect.Bool) {
785900 newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref}
786 currentSchema.SetNot(newSchema)
901 currentSchema.not = newSchema
787902 err := d.parseSchema(m[KEY_NOT], newSchema)
788903 if err != nil {
789904 return err
796911 }
797912 }
798913
914 if *currentSchema.draft >= Draft7 {
915 if existsMapKey(m, KEY_IF) {
916 if isKind(m[KEY_IF], reflect.Map, reflect.Bool) {
917 newSchema := &subSchema{property: KEY_IF, parent: currentSchema, ref: currentSchema.ref}
918 currentSchema._if = newSchema
919 err := d.parseSchema(m[KEY_IF], newSchema)
920 if err != nil {
921 return err
922 }
923 } else {
924 return errors.New(formatErrorDescription(
925 Locale.MustBeOfAn(),
926 ErrorDetails{"x": KEY_IF, "y": TYPE_OBJECT},
927 ))
928 }
929 }
930
931 if existsMapKey(m, KEY_THEN) {
932 if isKind(m[KEY_THEN], reflect.Map, reflect.Bool) {
933 newSchema := &subSchema{property: KEY_THEN, parent: currentSchema, ref: currentSchema.ref}
934 currentSchema._then = newSchema
935 err := d.parseSchema(m[KEY_THEN], newSchema)
936 if err != nil {
937 return err
938 }
939 } else {
940 return errors.New(formatErrorDescription(
941 Locale.MustBeOfAn(),
942 ErrorDetails{"x": KEY_THEN, "y": TYPE_OBJECT},
943 ))
944 }
945 }
946
947 if existsMapKey(m, KEY_ELSE) {
948 if isKind(m[KEY_ELSE], reflect.Map, reflect.Bool) {
949 newSchema := &subSchema{property: KEY_ELSE, parent: currentSchema, ref: currentSchema.ref}
950 currentSchema._else = newSchema
951 err := d.parseSchema(m[KEY_ELSE], newSchema)
952 if err != nil {
953 return err
954 }
955 } else {
956 return errors.New(formatErrorDescription(
957 Locale.MustBeOfAn(),
958 ErrorDetails{"x": KEY_ELSE, "y": TYPE_OBJECT},
959 ))
960 }
961 }
962 }
963
799964 return nil
800965 }
801966
802 func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) error {
803 var refdDocumentNode interface{}
804 jsonPointer := currentSchema.ref.GetPointer()
805 standaloneDocument := d.pool.GetStandaloneDocument()
806
807 if standaloneDocument != nil {
808
809 var err error
810 refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument)
811 if err != nil {
812 return err
813 }
814
815 } else {
816 dsp, err := d.pool.GetDocument(*currentSchema.ref)
817 if err != nil {
818 return err
819 }
820
821 refdDocumentNode, _, err = jsonPointer.Get(dsp.Document)
822 if err != nil {
823 return err
824 }
825
826 }
827
828 if !isKind(refdDocumentNode, reflect.Map) {
967 func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema) error {
968 var (
969 refdDocumentNode interface{}
970 dsp *schemaPoolDocument
971 err error
972 )
973
974 newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
975
976 d.referencePool.Add(currentSchema.ref.String(), newSchema)
977
978 dsp, err = d.pool.GetDocument(*currentSchema.ref)
979 if err != nil {
980 return err
981 }
982 newSchema.id = currentSchema.ref
983
984 refdDocumentNode = dsp.Document
985 newSchema.draft = dsp.Draft
986
987 if err != nil {
988 return err
989 }
990
991 if !isKind(refdDocumentNode, reflect.Map, reflect.Bool) {
829992 return errors.New(formatErrorDescription(
830993 Locale.MustBeOfType(),
831994 ErrorDetails{"key": STRING_SCHEMA, "type": TYPE_OBJECT},
832995 ))
833996 }
834997
835 // returns the loaded referenced subSchema for the caller to update its current subSchema
836 newSchemaDocument := refdDocumentNode.(map[string]interface{})
837 newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
838 d.referencePool.Add(currentSchema.ref.String()+reference, newSchema)
839
840 err := d.parseSchema(newSchemaDocument, newSchema)
998 err = d.parseSchema(refdDocumentNode, newSchema)
841999 if err != nil {
8421000 return err
8431001 }
8611019 for k := range m {
8621020 schemaProperty := k
8631021 newSchema := &subSchema{property: schemaProperty, parent: currentSchema, ref: currentSchema.ref}
864 currentSchema.AddPropertiesChild(newSchema)
1022 currentSchema.propertiesChildren = append(currentSchema.propertiesChildren, newSchema)
8651023 err := d.parseSchema(m[k], newSchema)
8661024 if err != nil {
8671025 return err
8991057 "type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS,
9001058 },
9011059 ))
902 } else {
903 valuesToRegister = append(valuesToRegister, value.(string))
904 }
1060 }
1061 valuesToRegister = append(valuesToRegister, value.(string))
9051062 currentSchema.dependencies[k] = valuesToRegister
9061063 }
9071064
908 case reflect.Map:
1065 case reflect.Map, reflect.Bool:
9091066 depSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref}
9101067 err := d.parseSchema(m[k], depSchema)
9111068 if err != nil {
0 // Copyright 2018 johandorland ( https://github.com/johandorland )
1 //
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package gojsonschema
15
16 import (
17 "bytes"
18 "errors"
19
20 "github.com/xeipuuv/gojsonreference"
21 )
22
23 // SchemaLoader is used to load schemas
24 type SchemaLoader struct {
25 pool *schemaPool
26 AutoDetect bool
27 Validate bool
28 Draft Draft
29 }
30
31 // NewSchemaLoader creates a new NewSchemaLoader
32 func NewSchemaLoader() *SchemaLoader {
33
34 ps := &SchemaLoader{
35 pool: &schemaPool{
36 schemaPoolDocuments: make(map[string]*schemaPoolDocument),
37 },
38 AutoDetect: true,
39 Validate: false,
40 Draft: Hybrid,
41 }
42 ps.pool.autoDetect = &ps.AutoDetect
43
44 return ps
45 }
46
47 func (sl *SchemaLoader) validateMetaschema(documentNode interface{}) error {
48
49 var (
50 schema string
51 err error
52 )
53 if sl.AutoDetect {
54 schema, _, err = parseSchemaURL(documentNode)
55 if err != nil {
56 return err
57 }
58 }
59
60 // If no explicit "$schema" is used, use the default metaschema associated with the draft used
61 if schema == "" {
62 if sl.Draft == Hybrid {
63 return nil
64 }
65 schema = drafts.GetSchemaURL(sl.Draft)
66 }
67
68 //Disable validation when loading the metaschema to prevent an infinite recursive loop
69 sl.Validate = false
70
71 metaSchema, err := sl.Compile(NewReferenceLoader(schema))
72
73 if err != nil {
74 return err
75 }
76
77 sl.Validate = true
78
79 result := metaSchema.validateDocument(documentNode)
80
81 if !result.Valid() {
82 var res bytes.Buffer
83 for _, err := range result.Errors() {
84 res.WriteString(err.String())
85 res.WriteString("\n")
86 }
87 return errors.New(res.String())
88 }
89
90 return nil
91 }
92
93 // AddSchemas adds an arbritrary amount of schemas to the schema cache. As this function does not require
94 // an explicit URL, every schema should contain an $id, so that it can be referenced by the main schema
95 func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error {
96 emptyRef, _ := gojsonreference.NewJsonReference("")
97
98 for _, loader := range loaders {
99 doc, err := loader.LoadJSON()
100
101 if err != nil {
102 return err
103 }
104
105 if sl.Validate {
106 if err := sl.validateMetaschema(doc); err != nil {
107 return err
108 }
109 }
110
111 // Directly use the Recursive function, so that it get only added to the schema pool by $id
112 // and not by the ref of the document as it's empty
113 if err = sl.pool.parseReferences(doc, emptyRef, false); err != nil {
114 return err
115 }
116 }
117
118 return nil
119 }
120
121 //AddSchema adds a schema under the provided URL to the schema cache
122 func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error {
123
124 ref, err := gojsonreference.NewJsonReference(url)
125
126 if err != nil {
127 return err
128 }
129
130 doc, err := loader.LoadJSON()
131
132 if err != nil {
133 return err
134 }
135
136 if sl.Validate {
137 if err := sl.validateMetaschema(doc); err != nil {
138 return err
139 }
140 }
141
142 return sl.pool.parseReferences(doc, ref, true)
143 }
144
145 // Compile loads and compiles a schema
146 func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) {
147
148 ref, err := rootSchema.JsonReference()
149
150 if err != nil {
151 return nil, err
152 }
153
154 d := Schema{}
155 d.pool = sl.pool
156 d.pool.jsonLoaderFactory = rootSchema.LoaderFactory()
157 d.documentReference = ref
158 d.referencePool = newSchemaReferencePool()
159
160 var doc interface{}
161 if ref.String() != "" {
162 // Get document from schema pool
163 spd, err := d.pool.GetDocument(d.documentReference)
164 if err != nil {
165 return nil, err
166 }
167 doc = spd.Document
168 } else {
169 // Load JSON directly
170 doc, err = rootSchema.LoadJSON()
171 if err != nil {
172 return nil, err
173 }
174 // References need only be parsed if loading JSON directly
175 // as pool.GetDocument already does this for us if loading by reference
176 err = sl.pool.parseReferences(doc, ref, true)
177 if err != nil {
178 return nil, err
179 }
180 }
181
182 if sl.Validate {
183 if err := sl.validateMetaschema(doc); err != nil {
184 return nil, err
185 }
186 }
187
188 draft := sl.Draft
189 if sl.AutoDetect {
190 _, detectedDraft, err := parseSchemaURL(doc)
191 if err != nil {
192 return nil, err
193 }
194 if detectedDraft != nil {
195 draft = *detectedDraft
196 }
197 }
198
199 err = d.parse(doc, draft)
200 if err != nil {
201 return nil, err
202 }
203
204 return &d, nil
205 }
0 // Copyright 2018 johandorland ( https://github.com/johandorland )
1 //
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package gojsonschema
15
16 import (
17 "github.com/stretchr/testify/require"
18 "testing"
19
20 "github.com/stretchr/testify/assert"
21 )
22
23 func TestSchemaLoaderWithReferenceToAddedSchema(t *testing.T) {
24 sl := NewSchemaLoader()
25 err := sl.AddSchemas(NewStringLoader(`{
26 "$id" : "http://localhost:1234/test1.json",
27 "type" : "integer"
28 }`))
29
30 assert.Nil(t, err)
31 schema, err := sl.Compile(NewReferenceLoader("http://localhost:1234/test1.json"))
32 assert.Nil(t, err)
33 result, err := schema.Validate(NewStringLoader(`"hello"`))
34 assert.Nil(t, err)
35 if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" {
36 t.Errorf("Expected invalid type erorr, instead got %v", result.Errors())
37 }
38 }
39
40 func TestCrossReference(t *testing.T) {
41 schema1 := NewStringLoader(`{
42 "$ref" : "http://localhost:1234/test3.json",
43 "definitions" : {
44 "foo" : {
45 "type" : "integer"
46 }
47 }
48 }`)
49 schema2 := NewStringLoader(`{
50 "$ref" : "http://localhost:1234/test2.json#/definitions/foo"
51 }`)
52
53 sl := NewSchemaLoader()
54 err := sl.AddSchema("http://localhost:1234/test2.json", schema1)
55 assert.Nil(t, err)
56 err = sl.AddSchema("http://localhost:1234/test3.json", schema2)
57 assert.Nil(t, err)
58 schema, err := sl.Compile(NewStringLoader(`{"$ref" : "http://localhost:1234/test2.json"}`))
59 assert.Nil(t, err)
60 result, err := schema.Validate(NewStringLoader(`"hello"`))
61 assert.Nil(t, err)
62 if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" {
63 t.Errorf("Expected invalid type erorr, instead got %v", result.Errors())
64 }
65 }
66
67 // Multiple schemas identifying under the same $id should throw an error
68 func TestDoubleIDReference(t *testing.T) {
69 sl := NewSchemaLoader()
70 err := sl.AddSchema("http://localhost:1234/test4.json", NewStringLoader("{}"))
71 assert.Nil(t, err)
72 err = sl.AddSchemas(NewStringLoader(`{ "$id" : "http://localhost:1234/test4.json"}`))
73 assert.NotNil(t, err)
74 }
75
76 func TestCustomMetaSchema(t *testing.T) {
77
78 loader := NewStringLoader(`{
79 "$id" : "http://localhost:1234/test5.json",
80 "properties" : {
81 "multipleOf" : false
82 }
83 }`)
84
85 // Test a custom metaschema in which we disallow the use of the keyword "multipleOf"
86 sl := NewSchemaLoader()
87 sl.Validate = true
88
89 err := sl.AddSchemas(loader)
90 assert.Nil(t, err)
91 _, err = sl.Compile(NewStringLoader(`{
92 "$id" : "http://localhost:1234/test6.json",
93 "$schema" : "http://localhost:1234/test5.json",
94 "type" : "string"
95 }`))
96 assert.Nil(t, err)
97
98 sl = NewSchemaLoader()
99 sl.Validate = true
100 err = sl.AddSchemas(loader)
101 assert.Nil(t, err)
102 _, err = sl.Compile(NewStringLoader(`{
103 "$id" : "http://localhost:1234/test7.json",
104 "$schema" : "http://localhost:1234/test5.json",
105 "multipleOf" : 5
106 }`))
107 assert.NotNil(t, err)
108 }
109
110 func TestSchemaDetection(t *testing.T) {
111 loader := NewStringLoader(`{
112 "$schema" : "http://json-schema.org/draft-04/schema#",
113 "exclusiveMinimum" : 5
114 }`)
115
116 // The schema should produce an error in draft-04 mode
117 _, err := NewSchema(loader)
118 assert.NotNil(t, err)
119
120 // With schema detection disabled the schema should not produce an error in hybrid mode
121 sl := NewSchemaLoader()
122 sl.AutoDetect = false
123
124 _, err = sl.Compile(loader)
125 assert.Nil(t, err)
126 }
127
128 func TestDraftCrossReferencing(t *testing.T) {
129
130 // Tests the following cross referencing with any combination
131 // of autodetection and preset draft version.
132
133 loader1 := NewStringLoader(`{
134 "$schema" : "http://json-schema.org/draft-04/schema#",
135 "id" : "http://localhost:1234/file.json",
136 "$id" : "http://localhost:1234/file.json",
137 "exclusiveMinimum" : 5
138 }`)
139 loader2 := NewStringLoader(`{
140 "$schema" : "http://json-schema.org/draft-07/schema#",
141 "id" : "http://localhost:1234/main.json",
142 "$id" : "http://localhost:1234/main.json",
143 "$ref" : "file.json"
144 }`)
145
146 for _, b := range []bool{true, false} {
147 for _, draft := range []Draft{Draft4, Draft6, Draft7} {
148 sl := NewSchemaLoader()
149 sl.Draft = draft
150 sl.AutoDetect = b
151
152 err := sl.AddSchemas(loader1)
153 assert.Nil(t, err)
154 _, err = sl.Compile(loader2)
155
156 // It will always fail with autodetection on as "exclusiveMinimum" : 5
157 // is only valid since draft-06. With autodetection off it will pass if
158 // draft-06 or newer is used
159
160 assert.Equal(t, err == nil, !b && draft >= Draft6)
161 }
162 }
163 }
164
165 const not_map_interface = "not map interface"
166
167 func TestParseSchemaURL_NotMap(t *testing.T) {
168 //GIVEN
169 sl := NewGoLoader(not_map_interface)
170 //WHEN
171 _, err := NewSchema(sl)
172 //THEN
173 require.Error(t, err)
174 assert.EqualError(t, err, "schema is invalid")
175 }
2727
2828 import (
2929 "errors"
30 "fmt"
31 "reflect"
3032
3133 "github.com/xeipuuv/gojsonreference"
3234 )
3335
3436 type schemaPoolDocument struct {
3537 Document interface{}
38 Draft *Draft
3639 }
3740
3841 type schemaPool struct {
3942 schemaPoolDocuments map[string]*schemaPoolDocument
40 standaloneDocument interface{}
4143 jsonLoaderFactory JSONLoaderFactory
42 }
43
44 func newSchemaPool(f JSONLoaderFactory) *schemaPool {
45
46 p := &schemaPool{}
47 p.schemaPoolDocuments = make(map[string]*schemaPoolDocument)
48 p.standaloneDocument = nil
49 p.jsonLoaderFactory = f
50
51 return p
52 }
53
54 func (p *schemaPool) SetStandaloneDocument(document interface{}) {
55 p.standaloneDocument = document
56 }
57
58 func (p *schemaPool) GetStandaloneDocument() (document interface{}) {
59 return p.standaloneDocument
44 autoDetect *bool
45 }
46
47 func (p *schemaPool) parseReferences(document interface{}, ref gojsonreference.JsonReference, pooled bool) error {
48
49 var (
50 draft *Draft
51 err error
52 reference = ref.String()
53 )
54 // Only the root document should be added to the schema pool if pooled is true
55 if _, ok := p.schemaPoolDocuments[reference]; pooled && ok {
56 return fmt.Errorf("Reference already exists: \"%s\"", reference)
57 }
58
59 if *p.autoDetect {
60 _, draft, err = parseSchemaURL(document)
61 if err != nil {
62 return err
63 }
64 }
65
66 err = p.parseReferencesRecursive(document, ref, draft)
67
68 if pooled {
69 p.schemaPoolDocuments[reference] = &schemaPoolDocument{Document: document, Draft: draft}
70 }
71
72 return err
73 }
74
75 func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonreference.JsonReference, draft *Draft) error {
76 // parseReferencesRecursive parses a JSON document and resolves all $id and $ref references.
77 // For $ref references it takes into account the $id scope it is in and replaces
78 // the reference by the absolute resolved reference
79
80 // When encountering errors it fails silently. Error handling is done when the schema
81 // is syntactically parsed and any error encountered here should also come up there.
82 switch m := document.(type) {
83 case []interface{}:
84 for _, v := range m {
85 p.parseReferencesRecursive(v, ref, draft)
86 }
87 case map[string]interface{}:
88 localRef := &ref
89
90 keyID := KEY_ID_NEW
91 if existsMapKey(m, KEY_ID) {
92 keyID = KEY_ID
93 }
94 if existsMapKey(m, keyID) && isKind(m[keyID], reflect.String) {
95 jsonReference, err := gojsonreference.NewJsonReference(m[keyID].(string))
96 if err == nil {
97 localRef, err = ref.Inherits(jsonReference)
98 if err == nil {
99 if _, ok := p.schemaPoolDocuments[localRef.String()]; ok {
100 return fmt.Errorf("Reference already exists: \"%s\"", localRef.String())
101 }
102 p.schemaPoolDocuments[localRef.String()] = &schemaPoolDocument{Document: document, Draft: draft}
103 }
104 }
105 }
106
107 if existsMapKey(m, KEY_REF) && isKind(m[KEY_REF], reflect.String) {
108 jsonReference, err := gojsonreference.NewJsonReference(m[KEY_REF].(string))
109 if err == nil {
110 absoluteRef, err := localRef.Inherits(jsonReference)
111 if err == nil {
112 m[KEY_REF] = absoluteRef.String()
113 }
114 }
115 }
116
117 for k, v := range m {
118 // const and enums should be interpreted literally, so ignore them
119 if k == KEY_CONST || k == KEY_ENUM {
120 continue
121 }
122 // Something like a property or a dependency is not a valid schema, as it might describe properties named "$ref", "$id" or "const", etc
123 // Therefore don't treat it like a schema.
124 if k == KEY_PROPERTIES || k == KEY_DEPENDENCIES || k == KEY_PATTERN_PROPERTIES {
125 if child, ok := v.(map[string]interface{}); ok {
126 for _, v := range child {
127 p.parseReferencesRecursive(v, *localRef, draft)
128 }
129 }
130 } else {
131 p.parseReferencesRecursive(v, *localRef, draft)
132 }
133 }
134 }
135 return nil
60136 }
61137
62138 func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*schemaPoolDocument, error) {
139
140 var (
141 spd *schemaPoolDocument
142 draft *Draft
143 ok bool
144 err error
145 )
63146
64147 if internalLogEnabled {
65148 internalLog("Get Document ( %s )", reference.String())
66149 }
67150
68 var err error
69
70 // It is not possible to load anything that is not canonical...
151 // Create a deep copy, so we can remove the fragment part later on without altering the original
152 refToURL, _ := gojsonreference.NewJsonReference(reference.String())
153
154 // First check if the given fragment is a location independent identifier
155 // http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3
156
157 if spd, ok = p.schemaPoolDocuments[refToURL.String()]; ok {
158 if internalLogEnabled {
159 internalLog(" From pool")
160 }
161 return spd, nil
162 }
163
164 // If the given reference is not a location independent identifier,
165 // strip the fragment and look for a document with it's base URI
166
167 refToURL.GetUrl().Fragment = ""
168
169 if cachedSpd, ok := p.schemaPoolDocuments[refToURL.String()]; ok {
170 document, _, err := reference.GetPointer().Get(cachedSpd.Document)
171
172 if err != nil {
173 return nil, err
174 }
175
176 if internalLogEnabled {
177 internalLog(" From pool")
178 }
179
180 spd = &schemaPoolDocument{Document: document, Draft: cachedSpd.Draft}
181 p.schemaPoolDocuments[reference.String()] = spd
182
183 return spd, nil
184 }
185
186 // It is not possible to load anything remotely that is not canonical...
71187 if !reference.IsCanonical() {
72188 return nil, errors.New(formatErrorDescription(
73189 Locale.ReferenceMustBeCanonical(),
74 ErrorDetails{"reference": reference},
190 ErrorDetails{"reference": reference.String()},
75191 ))
76 }
77
78 refToUrl := reference
79 refToUrl.GetUrl().Fragment = ""
80
81 var spd *schemaPoolDocument
82
83 // Try to find the requested document in the pool
84 for k := range p.schemaPoolDocuments {
85 if k == refToUrl.String() {
86 spd = p.schemaPoolDocuments[k]
87 }
88 }
89
90 if spd != nil {
91 if internalLogEnabled {
92 internalLog(" From pool")
93 }
94 return spd, nil
95192 }
96193
97194 jsonReferenceLoader := p.jsonLoaderFactory.New(reference.String())
98195 document, err := jsonReferenceLoader.LoadJSON()
196
99197 if err != nil {
100198 return nil, err
101199 }
102200
103 spd = &schemaPoolDocument{Document: document}
104 // add the document to the pool for potential later use
105 p.schemaPoolDocuments[refToUrl.String()] = spd
106
107 return spd, nil
108 }
201 // add the whole document to the pool for potential re-use
202 p.parseReferences(document, refToURL, true)
203
204 _, draft, _ = parseSchemaURL(document)
205
206 // resolve the potential fragment and also cache it
207 document, _, err = reference.GetPointer().Get(document)
208
209 if err != nil {
210 return nil, err
211 }
212
213 return &schemaPoolDocument{Document: document, Draft: draft}, nil
214 }
6161 if internalLogEnabled {
6262 internalLog(fmt.Sprintf("Add Schema Reference %s to pool", ref))
6363 }
64
65 p.documents[ref] = sch
64 if _, ok := p.documents[ref]; !ok {
65 p.documents[ref] = sch
66 }
6667 }
4343 func (t *jsonSchemaType) Add(etype string) error {
4444
4545 if !isStringInSlice(JSON_TYPES, etype) {
46 return errors.New(formatErrorDescription(Locale.NotAValidType(), ErrorDetails{"type": etype}))
46 return errors.New(formatErrorDescription(Locale.NotAValidType(), ErrorDetails{"given": "/" + etype + "/", "expected": JSON_TYPES}))
4747 }
4848
4949 if t.Contains(etype) {
2929 "fmt"
3030 "io"
3131 "io/ioutil"
32 "net/http"
3332 "os"
34 "reflect"
35 "regexp"
36 "sort"
37 "strconv"
38 "strings"
33 "path/filepath"
3934 "testing"
4035
4136 "github.com/stretchr/testify/assert"
4237 )
4338
4439 const displayErrorMessages = false
45
46 var rxInvoice = regexp.MustCompile("^[A-Z]{2}-[0-9]{5}")
47 var rxSplitErrors = regexp.MustCompile(", ?")
48
49 // Used for remote schema in ref/schema_5.json that defines "uri" and "regex" types
50 type alwaysTrueFormatChecker struct{}
51 type invoiceFormatChecker struct{}
52
53 func (a alwaysTrueFormatChecker) IsFormat(input string) bool {
54 return true
55 }
56
57 func (a invoiceFormatChecker) IsFormat(input string) bool {
58 return rxInvoice.MatchString(input)
59 }
60
61 func TestJsonSchemaTestSuite(t *testing.T) {
62
63 JsonSchemaTestSuiteMap := []map[string]string{
64
65 {"phase": "integer type matches integers", "test": "an integer is an integer", "schema": "type/schema_0.json", "data": "type/data_00.json", "valid": "true"},
66 {"phase": "integer type matches integers", "test": "a float is not an integer", "schema": "type/schema_0.json", "data": "type/data_01.json", "valid": "false", "errors": "invalid_type"},
67 {"phase": "integer type matches integers", "test": "a string is not an integer", "schema": "type/schema_0.json", "data": "type/data_02.json", "valid": "false", "errors": "invalid_type"},
68 {"phase": "integer type matches integers", "test": "an object is not an integer", "schema": "type/schema_0.json", "data": "type/data_03.json", "valid": "false", "errors": "invalid_type"},
69 {"phase": "integer type matches integers", "test": "an array is not an integer", "schema": "type/schema_0.json", "data": "type/data_04.json", "valid": "false", "errors": "invalid_type"},
70 {"phase": "integer type matches integers", "test": "a boolean is not an integer", "schema": "type/schema_0.json", "data": "type/data_05.json", "valid": "false", "errors": "invalid_type"},
71 {"phase": "integer type matches integers", "test": "null is not an integer", "schema": "type/schema_0.json", "data": "type/data_06.json", "valid": "false", "errors": "invalid_type"},
72 {"phase": "number type matches numbers", "test": "an integer is a number", "schema": "type/schema_1.json", "data": "type/data_10.json", "valid": "true"},
73 {"phase": "number type matches numbers", "test": "a float is a number", "schema": "type/schema_1.json", "data": "type/data_11.json", "valid": "true"},
74 {"phase": "number type matches numbers", "test": "a string is not a number", "schema": "type/schema_1.json", "data": "type/data_12.json", "valid": "false", "errors": "invalid_type"},
75 {"phase": "number type matches numbers", "test": "an object is not a number", "schema": "type/schema_1.json", "data": "type/data_13.json", "valid": "false", "errors": "invalid_type"},
76 {"phase": "number type matches numbers", "test": "an array is not a number", "schema": "type/schema_1.json", "data": "type/data_14.json", "valid": "false", "errors": "invalid_type"},
77 {"phase": "number type matches numbers", "test": "a boolean is not a number", "schema": "type/schema_1.json", "data": "type/data_15.json", "valid": "false", "errors": "invalid_type"},
78 {"phase": "number type matches numbers", "test": "null is not a number", "schema": "type/schema_1.json", "data": "type/data_16.json", "valid": "false", "errors": "invalid_type"},
79 {"phase": "string type matches strings", "test": "1 is not a string", "schema": "type/schema_2.json", "data": "type/data_20.json", "valid": "false", "errors": "invalid_type"},
80 {"phase": "string type matches strings", "test": "a float is not a string", "schema": "type/schema_2.json", "data": "type/data_21.json", "valid": "false", "errors": "invalid_type"},
81 {"phase": "string type matches strings", "test": "a string is a string", "schema": "type/schema_2.json", "data": "type/data_22.json", "valid": "true"},
82 {"phase": "string type matches strings", "test": "an object is not a string", "schema": "type/schema_2.json", "data": "type/data_23.json", "valid": "false", "errors": "invalid_type"},
83 {"phase": "string type matches strings", "test": "an array is not a string", "schema": "type/schema_2.json", "data": "type/data_24.json", "valid": "false", "errors": "invalid_type"},
84 {"phase": "string type matches strings", "test": "a boolean is not a string", "schema": "type/schema_2.json", "data": "type/data_25.json", "valid": "false", "errors": "invalid_type"},
85 {"phase": "string type matches strings", "test": "null is not a string", "schema": "type/schema_2.json", "data": "type/data_26.json", "valid": "false", "errors": "invalid_type"},
86 {"phase": "object type matches objects", "test": "an integer is not an object", "schema": "type/schema_3.json", "data": "type/data_30.json", "valid": "false", "errors": "invalid_type"},
87 {"phase": "object type matches objects", "test": "a float is not an object", "schema": "type/schema_3.json", "data": "type/data_31.json", "valid": "false", "errors": "invalid_type"},
88 {"phase": "object type matches objects", "test": "a string is not an object", "schema": "type/schema_3.json", "data": "type/data_32.json", "valid": "false", "errors": "invalid_type"},
89 {"phase": "object type matches objects", "test": "an object is an object", "schema": "type/schema_3.json", "data": "type/data_33.json", "valid": "true"},
90 {"phase": "object type matches objects", "test": "an array is not an object", "schema": "type/schema_3.json", "data": "type/data_34.json", "valid": "false", "errors": "invalid_type"},
91 {"phase": "object type matches objects", "test": "a boolean is not an object", "schema": "type/schema_3.json", "data": "type/data_35.json", "valid": "false", "errors": "invalid_type"},
92 {"phase": "object type matches objects", "test": "null is not an object", "schema": "type/schema_3.json", "data": "type/data_36.json", "valid": "false", "errors": "invalid_type"},
93 {"phase": "array type matches arrays", "test": "an integer is not an array", "schema": "type/schema_4.json", "data": "type/data_40.json", "valid": "false", "errors": "invalid_type"},
94 {"phase": "array type matches arrays", "test": "a float is not an array", "schema": "type/schema_4.json", "data": "type/data_41.json", "valid": "false", "errors": "invalid_type"},
95 {"phase": "array type matches arrays", "test": "a string is not an array", "schema": "type/schema_4.json", "data": "type/data_42.json", "valid": "false", "errors": "invalid_type"},
96 {"phase": "array type matches arrays", "test": "an object is not an array", "schema": "type/schema_4.json", "data": "type/data_43.json", "valid": "false", "errors": "invalid_type"},
97 {"phase": "array type matches arrays", "test": "an array is not an array", "schema": "type/schema_4.json", "data": "type/data_44.json", "valid": "true"},
98 {"phase": "array type matches arrays", "test": "a boolean is not an array", "schema": "type/schema_4.json", "data": "type/data_45.json", "valid": "false", "errors": "invalid_type"},
99 {"phase": "array type matches arrays", "test": "null is not an array", "schema": "type/schema_4.json", "data": "type/data_46.json", "valid": "false", "errors": "invalid_type"},
100 {"phase": "boolean type matches booleans", "test": "an integer is not a boolean", "schema": "type/schema_5.json", "data": "type/data_50.json", "valid": "false", "errors": "invalid_type"},
101 {"phase": "boolean type matches booleans", "test": "a float is not a boolean", "schema": "type/schema_5.json", "data": "type/data_51.json", "valid": "false", "errors": "invalid_type"},
102 {"phase": "boolean type matches booleans", "test": "a string is not a boolean", "schema": "type/schema_5.json", "data": "type/data_52.json", "valid": "false", "errors": "invalid_type"},
103 {"phase": "boolean type matches booleans", "test": "an object is not a boolean", "schema": "type/schema_5.json", "data": "type/data_53.json", "valid": "false", "errors": "invalid_type"},
104 {"phase": "boolean type matches booleans", "test": "an array is not a boolean", "schema": "type/schema_5.json", "data": "type/data_54.json", "valid": "false", "errors": "invalid_type"},
105 {"phase": "boolean type matches booleans", "test": "a boolean is not a boolean", "schema": "type/schema_5.json", "data": "type/data_55.json", "valid": "true", "errors": "invalid_type"},
106 {"phase": "boolean type matches booleans", "test": "null is not a boolean", "schema": "type/schema_5.json", "data": "type/data_56.json", "valid": "false", "errors": "invalid_type"},
107 {"phase": "null type matches only the null object", "test": "an integer is not null", "schema": "type/schema_6.json", "data": "type/data_60.json", "valid": "false", "errors": "invalid_type"},
108 {"phase": "null type matches only the null object", "test": "a float is not null", "schema": "type/schema_6.json", "data": "type/data_61.json", "valid": "false", "errors": "invalid_type"},
109 {"phase": "null type matches only the null object", "test": "a string is not null", "schema": "type/schema_6.json", "data": "type/data_62.json", "valid": "false", "errors": "invalid_type"},
110 {"phase": "null type matches only the null object", "test": "an object is not null", "schema": "type/schema_6.json", "data": "type/data_63.json", "valid": "false", "errors": "invalid_type"},
111 {"phase": "null type matches only the null object", "test": "an array is not null", "schema": "type/schema_6.json", "data": "type/data_64.json", "valid": "false", "errors": "invalid_type"},
112 {"phase": "null type matches only the null object", "test": "a boolean is not null", "schema": "type/schema_6.json", "data": "type/data_65.json", "valid": "false", "errors": "invalid_type"},
113 {"phase": "null type matches only the null object", "test": "null is null", "schema": "type/schema_6.json", "data": "type/data_66.json", "valid": "true"},
114 {"phase": "multiple types can be specified in an array", "test": "an integer is valid", "schema": "type/schema_7.json", "data": "type/data_70.json", "valid": "true"},
115 {"phase": "multiple types can be specified in an array", "test": "a string is valid", "schema": "type/schema_7.json", "data": "type/data_71.json", "valid": "true"},
116 {"phase": "multiple types can be specified in an array", "test": "a float is invalid", "schema": "type/schema_7.json", "data": "type/data_72.json", "valid": "false", "errors": "invalid_type"},
117 {"phase": "multiple types can be specified in an array", "test": "an object is invalid", "schema": "type/schema_7.json", "data": "type/data_73.json", "valid": "false", "errors": "invalid_type"},
118 {"phase": "multiple types can be specified in an array", "test": "an array is invalid", "schema": "type/schema_7.json", "data": "type/data_74.json", "valid": "false", "errors": "invalid_type"},
119 {"phase": "multiple types can be specified in an array", "test": "a boolean is invalid", "schema": "type/schema_7.json", "data": "type/data_75.json", "valid": "false", "errors": "invalid_type"},
120 {"phase": "multiple types can be specified in an array", "test": "null is invalid", "schema": "type/schema_7.json", "data": "type/data_76.json", "valid": "false", "errors": "invalid_type"},
121 {"phase": "required validation", "test": "present required property is valid", "schema": "required/schema_0.json", "data": "required/data_00.json", "valid": "true"},
122 {"phase": "required validation", "test": "non-present required property is invalid", "schema": "required/schema_0.json", "data": "required/data_01.json", "valid": "false", "errors": "required"},
123 {"phase": "required default validation", "test": "not required by default", "schema": "required/schema_1.json", "data": "required/data_10.json", "valid": "true"},
124 {"phase": "uniqueItems validation", "test": "unique array of integers is valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_00.json", "valid": "true"},
125 {"phase": "uniqueItems validation", "test": "non-unique array of integers is invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_01.json", "valid": "false", "errors": "unique"},
126 {"phase": "uniqueItems validation", "test": "numbers are unique if mathematically unequal", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_02.json", "valid": "false", "errors": "unique, unique"},
127 {"phase": "uniqueItems validation", "test": "unique array of objects is valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_03.json", "valid": "true"},
128 {"phase": "uniqueItems validation", "test": "non-unique array of objects is invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_04.json", "valid": "false", "errors": "unique"},
129 {"phase": "uniqueItems validation", "test": "unique array of nested objects is valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_05.json", "valid": "true"},
130 {"phase": "uniqueItems validation", "test": "non-unique array of nested objects is invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_06.json", "valid": "false", "errors": "unique"},
131 {"phase": "uniqueItems validation", "test": "unique array of arrays is valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_07.json", "valid": "true"},
132 {"phase": "uniqueItems validation", "test": "non-unique array of arrays is invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_08.json", "valid": "false", "errors": "unique"},
133 {"phase": "uniqueItems validation", "test": "1 and true are unique", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_09.json", "valid": "true"},
134 {"phase": "uniqueItems validation", "test": "0 and false are unique", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_010.json", "valid": "true"},
135 {"phase": "uniqueItems validation", "test": "unique heterogeneous types are valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_011.json", "valid": "true"},
136 {"phase": "uniqueItems validation", "test": "non-unique heterogeneous types are invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_012.json", "valid": "false", "errors": "unique"},
137 {"phase": "pattern validation", "test": "a matching pattern is valid", "schema": "pattern/schema_0.json", "data": "pattern/data_00.json", "valid": "true"},
138 {"phase": "pattern validation", "test": "a non-matching pattern is invalid", "schema": "pattern/schema_0.json", "data": "pattern/data_01.json", "valid": "false", "errors": "pattern"},
139 {"phase": "pattern validation", "test": "ignores non-strings", "schema": "pattern/schema_0.json", "data": "pattern/data_02.json", "valid": "true"},
140 {"phase": "simple enum validation", "test": "one of the enum is valid", "schema": "enum/schema_0.json", "data": "enum/data_00.json", "valid": "true"},
141 {"phase": "simple enum validation", "test": "something else is invalid", "schema": "enum/schema_0.json", "data": "enum/data_01.json", "valid": "false", "errors": "enum"},
142 {"phase": "heterogeneous enum validation", "test": "one of the enum is valid", "schema": "enum/schema_1.json", "data": "enum/data_10.json", "valid": "true"},
143 {"phase": "heterogeneous enum validation", "test": "something else is invalid", "schema": "enum/schema_1.json", "data": "enum/data_11.json", "valid": "false", "errors": "enum"},
144 {"phase": "heterogeneous enum validation", "test": "objects are deep compared", "schema": "enum/schema_1.json", "data": "enum/data_12.json", "valid": "false", "errors": "enum"},
145 {"phase": "minLength validation", "test": "longer is valid", "schema": "minLength/schema_0.json", "data": "minLength/data_00.json", "valid": "true"},
146 {"phase": "minLength validation", "test": "exact length is valid", "schema": "minLength/schema_0.json", "data": "minLength/data_01.json", "valid": "true"},
147 {"phase": "minLength validation", "test": "too short is invalid", "schema": "minLength/schema_0.json", "data": "minLength/data_02.json", "valid": "false", "errors": "string_gte"},
148 {"phase": "minLength validation", "test": "ignores non-strings", "schema": "minLength/schema_0.json", "data": "minLength/data_03.json", "valid": "true"},
149 {"phase": "minLength validation", "test": "counts utf8 length correctly", "schema": "minLength/schema_0.json", "data": "minLength/data_04.json", "valid": "false", "errors": "string_gte"},
150 {"phase": "maxLength validation", "test": "shorter is valid", "schema": "maxLength/schema_0.json", "data": "maxLength/data_00.json", "valid": "true"},
151 {"phase": "maxLength validation", "test": "exact length is valid", "schema": "maxLength/schema_0.json", "data": "maxLength/data_01.json", "valid": "true"},
152 {"phase": "maxLength validation", "test": "too long is invalid", "schema": "maxLength/schema_0.json", "data": "maxLength/data_02.json", "valid": "false", "errors": "string_lte"},
153 {"phase": "maxLength validation", "test": "ignores non-strings", "schema": "maxLength/schema_0.json", "data": "maxLength/data_03.json", "valid": "true"},
154 {"phase": "maxLength validation", "test": "counts utf8 length correctly", "schema": "maxLength/schema_0.json", "data": "maxLength/data_04.json", "valid": "true"},
155 {"phase": "minimum validation", "test": "above the minimum is valid", "schema": "minimum/schema_0.json", "data": "minimum/data_00.json", "valid": "true"},
156 {"phase": "minimum validation", "test": "below the minimum is invalid", "schema": "minimum/schema_0.json", "data": "minimum/data_01.json", "valid": "false", "errors": "number_gte"},
157 {"phase": "minimum validation", "test": "ignores non-numbers", "schema": "minimum/schema_0.json", "data": "minimum/data_02.json", "valid": "true"},
158 {"phase": "exclusiveMinimum validation", "test": "above the minimum is still valid", "schema": "minimum/schema_1.json", "data": "minimum/data_10.json", "valid": "true"},
159 {"phase": "exclusiveMinimum validation", "test": "boundary point is invalid", "schema": "minimum/schema_1.json", "data": "minimum/data_11.json", "valid": "false", "errors": "number_gt"},
160 {"phase": "maximum validation", "test": "below the maximum is valid", "schema": "maximum/schema_0.json", "data": "maximum/data_00.json", "valid": "true"},
161 {"phase": "maximum validation", "test": "above the maximum is invalid", "schema": "maximum/schema_0.json", "data": "maximum/data_01.json", "valid": "false", "errors": "number_lte"},
162 {"phase": "maximum validation", "test": "ignores non-numbers", "schema": "maximum/schema_0.json", "data": "maximum/data_02.json", "valid": "true"},
163 {"phase": "exclusiveMaximum validation", "test": "below the maximum is still valid", "schema": "maximum/schema_1.json", "data": "maximum/data_10.json", "valid": "true"},
164 {"phase": "exclusiveMaximum validation", "test": "boundary point is invalid", "schema": "maximum/schema_1.json", "data": "maximum/data_11.json", "valid": "false", "errors": "number_lt"},
165 {"phase": "allOf", "test": "allOf", "schema": "allOf/schema_0.json", "data": "allOf/data_00.json", "valid": "true"},
166 {"phase": "allOf", "test": "mismatch second", "schema": "allOf/schema_0.json", "data": "allOf/data_01.json", "valid": "false", "errors": "number_all_of, required"},
167 {"phase": "allOf", "test": "mismatch first", "schema": "allOf/schema_0.json", "data": "allOf/data_02.json", "valid": "false", "errors": "number_all_of, required"},
168 {"phase": "allOf", "test": "wrong type", "schema": "allOf/schema_0.json", "data": "allOf/data_03.json", "valid": "false", "errors": "number_all_of, invalid_type"},
169 {"phase": "allOf with base schema", "test": "valid", "schema": "allOf/schema_1.json", "data": "allOf/data_10.json", "valid": "true"},
170 {"phase": "allOf with base schema", "test": "mismatch base schema", "schema": "allOf/schema_1.json", "data": "allOf/data_11.json", "valid": "false", "errors": "required"},
171 {"phase": "allOf with base schema", "test": "mismatch first allOf", "schema": "allOf/schema_1.json", "data": "allOf/data_12.json", "valid": "false", "errors": "number_all_of, required"},
172 {"phase": "allOf with base schema", "test": "mismatch second allOf", "schema": "allOf/schema_1.json", "data": "allOf/data_13.json", "valid": "false", "errors": "number_all_of, required"},
173 {"phase": "allOf with base schema", "test": "mismatch both", "schema": "allOf/schema_1.json", "data": "allOf/data_14.json", "valid": "false", "errors": "number_all_of, required, required"},
174 {"phase": "allOf simple types", "test": "valid", "schema": "allOf/schema_2.json", "data": "allOf/data_20.json", "valid": "true"},
175 {"phase": "allOf simple types", "test": "mismatch one", "schema": "allOf/schema_2.json", "data": "allOf/data_21.json", "valid": "false", "errors": "number_all_of, number_lte"},
176 {"phase": "oneOf", "test": "first oneOf valid", "schema": "oneOf/schema_0.json", "data": "oneOf/data_00.json", "valid": "true"},
177 {"phase": "oneOf", "test": "second oneOf valid", "schema": "oneOf/schema_0.json", "data": "oneOf/data_01.json", "valid": "true"},
178 {"phase": "oneOf", "test": "both oneOf valid", "schema": "oneOf/schema_0.json", "data": "oneOf/data_02.json", "valid": "false", "errors": "number_one_of"},
179 {"phase": "oneOf", "test": "neither oneOf valid", "schema": "oneOf/schema_0.json", "data": "oneOf/data_03.json", "valid": "false"},
180 {"phase": "oneOf with base schema", "test": "mismatch base schema", "schema": "oneOf/schema_1.json", "data": "oneOf/data_10.json", "valid": "false", "errors": "invalid_type"},
181 {"phase": "oneOf with base schema", "test": "one oneOf valid", "schema": "oneOf/schema_1.json", "data": "oneOf/data_11.json", "valid": "true"},
182 {"phase": "oneOf with base schema", "test": "both oneOf valid", "schema": "oneOf/schema_1.json", "data": "oneOf/data_12.json", "valid": "false", "errors": "number_one_of"},
183 {"phase": "anyOf", "test": "first anyOf valid", "schema": "anyOf/schema_0.json", "data": "anyOf/data_00.json", "valid": "true"},
184 {"phase": "anyOf", "test": "second anyOf valid", "schema": "anyOf/schema_0.json", "data": "anyOf/data_01.json", "valid": "true"},
185 {"phase": "anyOf", "test": "both anyOf valid", "schema": "anyOf/schema_0.json", "data": "anyOf/data_02.json", "valid": "true"},
186 {"phase": "anyOf", "test": "neither anyOf valid", "schema": "anyOf/schema_0.json", "data": "anyOf/data_03.json", "valid": "false", "errors": "number_any_of, number_gte"},
187 {"phase": "anyOf with base schema", "test": "mismatch base schema", "schema": "anyOf/schema_1.json", "data": "anyOf/data_10.json", "valid": "false", "errors": "invalid_type"},
188 {"phase": "anyOf with base schema", "test": "one anyOf valid", "schema": "anyOf/schema_1.json", "data": "anyOf/data_11.json", "valid": "true"},
189 {"phase": "anyOf with base schema", "test": "both anyOf invalid", "schema": "anyOf/schema_1.json", "data": "anyOf/data_12.json", "valid": "false", "errors": "number_any_of, string_lte"},
190 {"phase": "not", "test": "allowed", "schema": "not/schema_0.json", "data": "not/data_00.json", "valid": "true"},
191 {"phase": "not", "test": "disallowed", "schema": "not/schema_0.json", "data": "not/data_01.json", "valid": "false", "errors": "number_not"},
192 {"phase": "not multiple types", "test": "valid", "schema": "not/schema_1.json", "data": "not/data_10.json", "valid": "true"},
193 {"phase": "not multiple types", "test": "mismatch", "schema": "not/schema_1.json", "data": "not/data_11.json", "valid": "false", "errors": "number_not"},
194 {"phase": "not multiple types", "test": "other mismatch", "schema": "not/schema_1.json", "data": "not/data_12.json", "valid": "false", "errors": "number_not"},
195 {"phase": "not more complex schema", "test": "match", "schema": "not/schema_2.json", "data": "not/data_20.json", "valid": "true"},
196 {"phase": "not more complex schema", "test": "other match", "schema": "not/schema_2.json", "data": "not/data_21.json", "valid": "true"},
197 {"phase": "not more complex schema", "test": "mismatch", "schema": "not/schema_2.json", "data": "not/data_22.json", "valid": "false", "errors": "number_not"},
198 {"phase": "minProperties validation", "test": "longer is valid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_00.json", "valid": "true"},
199 {"phase": "minProperties validation", "test": "exact length is valid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_01.json", "valid": "true"},
200 {"phase": "minProperties validation", "test": "too short is invalid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_02.json", "valid": "false", "errors": "array_min_properties"},
201 {"phase": "minProperties validation", "test": "ignores non-objects", "schema": "minProperties/schema_0.json", "data": "minProperties/data_03.json", "valid": "true"},
202 {"phase": "maxProperties validation", "test": "shorter is valid", "schema": "maxProperties/schema_0.json", "data": "maxProperties/data_00.json", "valid": "true"},
203 {"phase": "maxProperties validation", "test": "exact length is valid", "schema": "maxProperties/schema_0.json", "data": "maxProperties/data_01.json", "valid": "true"},
204 {"phase": "maxProperties validation", "test": "too long is invalid", "schema": "maxProperties/schema_0.json", "data": "maxProperties/data_02.json", "valid": "false", "errors": "array_max_properties"},
205 {"phase": "maxProperties validation", "test": "ignores non-objects", "schema": "maxProperties/schema_0.json", "data": "maxProperties/data_03.json", "valid": "true"},
206 {"phase": "by int", "test": "int by int", "schema": "multipleOf/schema_0.json", "data": "multipleOf/data_00.json", "valid": "true"},
207 {"phase": "by int", "test": "int by int fail", "schema": "multipleOf/schema_0.json", "data": "multipleOf/data_01.json", "valid": "false", "errors": "multiple_of"},
208 {"phase": "by int", "test": "ignores non-numbers", "schema": "multipleOf/schema_0.json", "data": "multipleOf/data_02.json", "valid": "true"},
209 {"phase": "by number", "test": "zero is multiple of anything", "schema": "multipleOf/schema_1.json", "data": "multipleOf/data_10.json", "valid": "true"},
210 {"phase": "by number", "test": "4.5 is multiple of 1.5", "schema": "multipleOf/schema_1.json", "data": "multipleOf/data_11.json", "valid": "true"},
211 {"phase": "by number", "test": "35 is not multiple of 1.5", "schema": "multipleOf/schema_1.json", "data": "multipleOf/data_12.json", "valid": "false", "errors": "multiple_of"},
212 {"phase": "by small number", "test": "0.0075 is multiple of 0.0001", "schema": "multipleOf/schema_2.json", "data": "multipleOf/data_20.json", "valid": "true"},
213 {"phase": "by small number", "test": "0.00751 is not multiple of 0.0001", "schema": "multipleOf/schema_2.json", "data": "multipleOf/data_21.json", "valid": "false", "errors": "multiple_of"},
214 {"phase": "minItems validation", "test": "longer is valid", "schema": "minItems/schema_0.json", "data": "minItems/data_00.json", "valid": "true"},
215 {"phase": "minItems validation", "test": "exact length is valid", "schema": "minItems/schema_0.json", "data": "minItems/data_01.json", "valid": "true"},
216 {"phase": "minItems validation", "test": "too short is invalid", "schema": "minItems/schema_0.json", "data": "minItems/data_02.json", "valid": "false", "errors": "array_min_items"},
217 {"phase": "minItems validation", "test": "ignores non-arrays", "schema": "minItems/schema_0.json", "data": "minItems/data_03.json", "valid": "true"},
218 {"phase": "maxItems validation", "test": "shorter is valid", "schema": "maxItems/schema_0.json", "data": "maxItems/data_00.json", "valid": "true"},
219 {"phase": "maxItems validation", "test": "exact length is valid", "schema": "maxItems/schema_0.json", "data": "maxItems/data_01.json", "valid": "true"},
220 {"phase": "maxItems validation", "test": "too long is invalid", "schema": "maxItems/schema_0.json", "data": "maxItems/data_02.json", "valid": "false", "errors": "array_max_items"},
221 {"phase": "maxItems validation", "test": "ignores non-arrays", "schema": "maxItems/schema_0.json", "data": "maxItems/data_03.json", "valid": "true"},
222 {"phase": "object properties validation", "test": "both properties present and valid is valid", "schema": "properties/schema_0.json", "data": "properties/data_00.json", "valid": "true"},
223 {"phase": "object properties validation", "test": "one property invalid is invalid", "schema": "properties/schema_0.json", "data": "properties/data_01.json", "valid": "false", "errors": "invalid_type"},
224 {"phase": "object properties validation", "test": "both properties invalid is invalid", "schema": "properties/schema_0.json", "data": "properties/data_02.json", "valid": "false", "errors": "invalid_type, invalid_type"},
225 {"phase": "object properties validation", "test": "doesn't invalidate other properties", "schema": "properties/schema_0.json", "data": "properties/data_03.json", "valid": "true"},
226 {"phase": "object properties validation", "test": "ignores non-objects", "schema": "properties/schema_0.json", "data": "properties/data_04.json", "valid": "true"},
227 {"phase": "properties, patternProperties, additionalProperties interaction", "test": "property validates property", "schema": "properties/schema_1.json", "data": "properties/data_10.json", "valid": "true"},
228 {"phase": "properties, patternProperties, additionalProperties interaction", "test": "property invalidates property", "schema": "properties/schema_1.json", "data": "properties/data_11.json", "valid": "false", "errors": "array_max_items"},
229 {"phase": "properties, patternProperties, additionalProperties interaction", "test": "patternProperty invalidates property", "schema": "properties/schema_1.json", "data": "properties/data_12.json", "valid": "false", "errors": "array_min_items, invalid_type"},
230 {"phase": "properties, patternProperties, additionalProperties interaction", "test": "patternProperty validates nonproperty", "schema": "properties/schema_1.json", "data": "properties/data_13.json", "valid": "true"},
231 {"phase": "properties, patternProperties, additionalProperties interaction", "test": "patternProperty invalidates nonproperty", "schema": "properties/schema_1.json", "data": "properties/data_14.json", "valid": "false", "errors": "array_min_items, invalid_type"},
232 {"phase": "properties, patternProperties, additionalProperties interaction", "test": "additionalProperty ignores property", "schema": "properties/schema_1.json", "data": "properties/data_15.json", "valid": "true"},
233 {"phase": "properties, patternProperties, additionalProperties interaction", "test": "additionalProperty validates others", "schema": "properties/schema_1.json", "data": "properties/data_16.json", "valid": "true"},
234 {"phase": "properties, patternProperties, additionalProperties interaction", "test": "additionalProperty invalidates others", "schema": "properties/schema_1.json", "data": "properties/data_17.json", "valid": "false", "errors": "invalid_type"},
235 {"phase": "root pointer ref", "test": "match", "schema": "ref/schema_0.json", "data": "ref/data_00.json", "valid": "true"},
236 {"phase": "root pointer ref", "test": "recursive match", "schema": "ref/schema_0.json", "data": "ref/data_01.json", "valid": "true"},
237 {"phase": "root pointer ref", "test": "mismatch", "schema": "ref/schema_0.json", "data": "ref/data_02.json", "valid": "false", "errors": "additional_property_not_allowed"},
238 {"phase": "root pointer ref", "test": "recursive mismatch", "schema": "ref/schema_0.json", "data": "ref/data_03.json", "valid": "false", "errors": "additional_property_not_allowed"},
239 {"phase": "relative pointer ref to object", "test": "match", "schema": "ref/schema_1.json", "data": "ref/data_10.json", "valid": "true"},
240 {"phase": "relative pointer ref to object", "test": "mismatch", "schema": "ref/schema_1.json", "data": "ref/data_11.json", "valid": "false", "errors": "invalid_type"},
241 {"phase": "relative pointer ref to array", "test": "match array", "schema": "ref/schema_2.json", "data": "ref/data_20.json", "valid": "true"},
242 {"phase": "relative pointer ref to array", "test": "mismatch array", "schema": "ref/schema_2.json", "data": "ref/data_21.json", "valid": "false", "errors": "invalid_type"},
243 {"phase": "escaped pointer ref", "test": "slash", "schema": "ref/schema_3.json", "data": "ref/data_30.json", "valid": "false", "errors": "invalid_type"},
244 {"phase": "escaped pointer ref", "test": "tilda", "schema": "ref/schema_3.json", "data": "ref/data_31.json", "valid": "false", "errors": "invalid_type"},
245 {"phase": "escaped pointer ref", "test": "percent", "schema": "ref/schema_3.json", "data": "ref/data_32.json", "valid": "false", "errors": "invalid_type"},
246 {"phase": "nested refs", "test": "nested ref valid", "schema": "ref/schema_4.json", "data": "ref/data_40.json", "valid": "true"},
247 {"phase": "nested refs", "test": "nested ref invalid", "schema": "ref/schema_4.json", "data": "ref/data_41.json", "valid": "false", "errors": "invalid_type"},
248 {"phase": "remote ref, containing refs itself", "test": "remote ref valid", "schema": "ref/schema_5.json", "data": "ref/data_50.json", "valid": "true"},
249 {"phase": "remote ref, containing refs itself", "test": "remote ref invalid", "schema": "ref/schema_5.json", "data": "ref/data_51.json", "valid": "false", "errors": "number_all_of, number_gte"},
250 {"phase": "a schema given for items", "test": "valid items", "schema": "items/schema_0.json", "data": "items/data_00.json", "valid": "true"},
251 {"phase": "a schema given for items", "test": "wrong type of items", "schema": "items/schema_0.json", "data": "items/data_01.json", "valid": "false", "errors": "invalid_type"},
252 {"phase": "a schema given for items", "test": "ignores non-arrays", "schema": "items/schema_0.json", "data": "items/data_02.json", "valid": "true"},
253 {"phase": "an array of schemas for items", "test": "correct types", "schema": "items/schema_1.json", "data": "items/data_10.json", "valid": "true"},
254 {"phase": "an array of schemas for items", "test": "wrong types", "schema": "items/schema_1.json", "data": "items/data_11.json", "valid": "false", "errors": "invalid_type, invalid_type"},
255 {"phase": "valid definition", "test": "valid definition schema", "schema": "definitions/schema_0.json", "data": "definitions/data_00.json", "valid": "true"},
256 {"phase": "invalid definition", "test": "invalid definition schema", "schema": "definitions/schema_1.json", "data": "definitions/data_10.json", "valid": "false", "errors": "number_any_of, enum"},
257 {"phase": "additionalItems as schema", "test": "additional items match schema", "schema": "additionalItems/schema_0.json", "data": "additionalItems/data_00.json", "valid": "true"},
258 {"phase": "additionalItems as schema", "test": "additional items do not match schema", "schema": "additionalItems/schema_0.json", "data": "additionalItems/data_01.json", "valid": "false", "errors": "invalid_type"},
259 {"phase": "items is schema, no additionalItems", "test": "all items match schema", "schema": "additionalItems/schema_1.json", "data": "additionalItems/data_10.json", "valid": "true"},
260 {"phase": "array of items with no additionalItems", "test": "no additional items present", "schema": "additionalItems/schema_2.json", "data": "additionalItems/data_20.json", "valid": "true"},
261 {"phase": "array of items with no additionalItems", "test": "additional items are not permitted", "schema": "additionalItems/schema_2.json", "data": "additionalItems/data_21.json", "valid": "false", "errors": "array_no_additional_items"},
262 {"phase": "additionalItems as false without items", "test": "items defaults to empty schema so everything is valid", "schema": "additionalItems/schema_3.json", "data": "additionalItems/data_30.json", "valid": "true"},
263 {"phase": "additionalItems as false without items", "test": "ignores non-arrays", "schema": "additionalItems/schema_3.json", "data": "additionalItems/data_31.json", "valid": "true"},
264 {"phase": "additionalItems are allowed by default", "test": "only the first item is validated", "schema": "additionalItems/schema_4.json", "data": "additionalItems/data_40.json", "valid": "true"},
265 {"phase": "additionalProperties being false does not allow other properties", "test": "no additional properties is valid", "schema": "additionalProperties/schema_0.json", "data": "additionalProperties/data_00.json", "valid": "true"},
266 {"phase": "additionalProperties being false does not allow other properties", "test": "an additional property is invalid", "schema": "additionalProperties/schema_0.json", "data": "additionalProperties/data_01.json", "valid": "false", "errors": "additional_property_not_allowed"},
267 {"phase": "additionalProperties being false does not allow other properties", "test": "ignores non-objects", "schema": "additionalProperties/schema_0.json", "data": "additionalProperties/data_02.json", "valid": "true"},
268 {"phase": "additionalProperties being false does not allow other properties", "test": "patternProperties are not additional properties", "schema": "additionalProperties/schema_0.json", "data": "additionalProperties/data_03.json", "valid": "true"},
269 {"phase": "additionalProperties allows a schema which should validate", "test": "no additional properties is valid", "schema": "additionalProperties/schema_1.json", "data": "additionalProperties/data_10.json", "valid": "true"},
270 {"phase": "additionalProperties allows a schema which should validate", "test": "an additional valid property is valid", "schema": "additionalProperties/schema_1.json", "data": "additionalProperties/data_11.json", "valid": "true"},
271 {"phase": "additionalProperties allows a schema which should validate", "test": "an additional invalid property is invalid", "schema": "additionalProperties/schema_1.json", "data": "additionalProperties/data_12.json", "valid": "false", "errors": "invalid_type"},
272 {"phase": "additionalProperties are allowed by default", "test": "additional properties are allowed", "schema": "additionalProperties/schema_2.json", "data": "additionalProperties/data_20.json", "valid": "true"},
273 {"phase": "dependencies", "test": "neither", "schema": "dependencies/schema_0.json", "data": "dependencies/data_00.json", "valid": "true"},
274 {"phase": "dependencies", "test": "nondependant", "schema": "dependencies/schema_0.json", "data": "dependencies/data_01.json", "valid": "true"},
275 {"phase": "dependencies", "test": "with dependency", "schema": "dependencies/schema_0.json", "data": "dependencies/data_02.json", "valid": "true"},
276 {"phase": "dependencies", "test": "missing dependency", "schema": "dependencies/schema_0.json", "data": "dependencies/data_03.json", "valid": "false", "errors": "missing_dependency"},
277 {"phase": "dependencies", "test": "ignores non-objects", "schema": "dependencies/schema_0.json", "data": "dependencies/data_04.json", "valid": "true"},
278 {"phase": "multiple dependencies", "test": "neither", "schema": "dependencies/schema_1.json", "data": "dependencies/data_10.json", "valid": "true"},
279 {"phase": "multiple dependencies", "test": "nondependants", "schema": "dependencies/schema_1.json", "data": "dependencies/data_11.json", "valid": "true"},
280 {"phase": "multiple dependencies", "test": "with dependencies", "schema": "dependencies/schema_1.json", "data": "dependencies/data_12.json", "valid": "true"},
281 {"phase": "multiple dependencies", "test": "missing dependency", "schema": "dependencies/schema_1.json", "data": "dependencies/data_13.json", "valid": "false", "errors": "missing_dependency"},
282 {"phase": "multiple dependencies", "test": "missing other dependency", "schema": "dependencies/schema_1.json", "data": "dependencies/data_14.json", "valid": "false", "errors": "missing_dependency"},
283 {"phase": "multiple dependencies", "test": "missing both dependencies", "schema": "dependencies/schema_1.json", "data": "dependencies/data_15.json", "valid": "false", "errors": "missing_dependency, missing_dependency"},
284 {"phase": "multiple dependencies subschema", "test": "valid", "schema": "dependencies/schema_2.json", "data": "dependencies/data_20.json", "valid": "true"},
285 {"phase": "multiple dependencies subschema", "test": "no dependency", "schema": "dependencies/schema_2.json", "data": "dependencies/data_21.json", "valid": "true"},
286 {"phase": "multiple dependencies subschema", "test": "wrong type", "schema": "dependencies/schema_2.json", "data": "dependencies/data_22.json", "valid": "false"},
287 {"phase": "multiple dependencies subschema", "test": "wrong type other", "schema": "dependencies/schema_2.json", "data": "dependencies/data_23.json", "valid": "false", "errors": "invalid_type"},
288 {"phase": "multiple dependencies subschema", "test": "wrong type both", "schema": "dependencies/schema_2.json", "data": "dependencies/data_24.json", "valid": "false", "errors": "invalid_type, invalid_type"},
289 {"phase": "patternProperties validates properties matching a regex", "test": "a single valid match is valid", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_00.json", "valid": "true"},
290 {"phase": "patternProperties validates properties matching a regex", "test": "multiple valid matches is valid", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_01.json", "valid": "true"},
291 {"phase": "patternProperties validates properties matching a regex", "test": "a single invalid match is invalid", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_02.json", "valid": "false", "errors": "invalid_property_pattern, invalid_type"},
292 {"phase": "patternProperties validates properties matching a regex", "test": "multiple invalid matches is invalid", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_03.json", "valid": "false", "errors": "invalid_property_pattern, invalid_property_pattern, invalid_type, invalid_type"},
293 {"phase": "patternProperties validates properties matching a regex", "test": "ignores non-objects", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_04.json", "valid": "true"},
294 {"phase": "patternProperties validates properties matching a regex", "test": "with additionalProperties combination", "schema": "patternProperties/schema_3.json", "data": "patternProperties/data_24.json", "valid": "false", "errors": "additional_property_not_allowed"},
295 {"phase": "patternProperties validates properties matching a regex", "test": "with additionalProperties combination", "schema": "patternProperties/schema_3.json", "data": "patternProperties/data_25.json", "valid": "false", "errors": "additional_property_not_allowed"},
296 {"phase": "patternProperties validates properties matching a regex", "test": "with additionalProperties combination", "schema": "patternProperties/schema_4.json", "data": "patternProperties/data_26.json", "valid": "false", "errors": "additional_property_not_allowed"},
297 {"phase": "multiple simultaneous patternProperties are validated", "test": "a single valid match is valid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_10.json", "valid": "true"},
298 {"phase": "multiple simultaneous patternProperties are validated", "test": "a simultaneous match is valid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_11.json", "valid": "true"},
299 {"phase": "multiple simultaneous patternProperties are validated", "test": "multiple matches is valid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_12.json", "valid": "true"},
300 {"phase": "multiple simultaneous patternProperties are validated", "test": "an invalid due to one is invalid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_13.json", "valid": "false", "errors": "invalid_property_pattern, invalid_type"},
301 {"phase": "multiple simultaneous patternProperties are validated", "test": "an invalid due to the other is invalid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_14.json", "valid": "false", "errors": "number_lte"},
302 {"phase": "multiple simultaneous patternProperties are validated", "test": "an invalid due to both is invalid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_15.json", "valid": "false", "errors": "invalid_type, number_lte"},
303 {"phase": "regexes are not anchored by default and are case sensitive", "test": "non recognized members are ignored", "schema": "patternProperties/schema_2.json", "data": "patternProperties/data_20.json", "valid": "true"},
304 {"phase": "regexes are not anchored by default and are case sensitive", "test": "recognized members are accounted for", "schema": "patternProperties/schema_2.json", "data": "patternProperties/data_21.json", "valid": "false", "errors": "invalid_property_pattern, invalid_type"},
305 {"phase": "regexes are not anchored by default and are case sensitive", "test": "regexes are case sensitive", "schema": "patternProperties/schema_2.json", "data": "patternProperties/data_22.json", "valid": "true"},
306 {"phase": "regexes are not anchored by default and are case sensitive", "test": "regexes are case sensitive, 2", "schema": "patternProperties/schema_2.json", "data": "patternProperties/data_23.json", "valid": "false", "errors": "invalid_property_pattern, invalid_type"},
307 {"phase": "remote ref", "test": "remote ref valid", "schema": "refRemote/schema_0.json", "data": "refRemote/data_00.json", "valid": "true"},
308 {"phase": "remote ref", "test": "remote ref invalid", "schema": "refRemote/schema_0.json", "data": "refRemote/data_01.json", "valid": "false", "errors": "invalid_type"},
309 {"phase": "fragment within remote ref", "test": "remote fragment valid", "schema": "refRemote/schema_1.json", "data": "refRemote/data_10.json", "valid": "true"},
310 {"phase": "fragment within remote ref", "test": "remote fragment invalid", "schema": "refRemote/schema_1.json", "data": "refRemote/data_11.json", "valid": "false", "errors": "invalid_type"},
311 {"phase": "ref within remote ref", "test": "ref within ref valid", "schema": "refRemote/schema_2.json", "data": "refRemote/data_20.json", "valid": "true"},
312 {"phase": "ref within remote ref", "test": "ref within ref invalid", "schema": "refRemote/schema_2.json", "data": "refRemote/data_21.json", "valid": "false", "errors": "invalid_type"},
313 {"phase": "format validation", "test": "email format is invalid", "schema": "format/schema_0.json", "data": "format/data_00.json", "valid": "false", "errors": "format"},
314 {"phase": "format validation", "test": "email format is invalid", "schema": "format/schema_0.json", "data": "format/data_01.json", "valid": "false", "errors": "format"},
315 {"phase": "format validation", "test": "email format valid", "schema": "format/schema_0.json", "data": "format/data_02.json", "valid": "true"},
316 {"phase": "format validation", "test": "invoice format valid", "schema": "format/schema_1.json", "data": "format/data_03.json", "valid": "true"},
317 {"phase": "format validation", "test": "invoice format is invalid", "schema": "format/schema_1.json", "data": "format/data_04.json", "valid": "false", "errors": "format"},
318 {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_05.json", "valid": "true"},
319 {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_06.json", "valid": "true"},
320 {"phase": "format validation", "test": "date-time format is invalid", "schema": "format/schema_2.json", "data": "format/data_07.json", "valid": "false", "errors": "format"},
321 {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_08.json", "valid": "false", "errors": "format"},
322 {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_09.json", "valid": "true"},
323 {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_10.json", "valid": "true"},
324 {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_11.json", "valid": "true"},
325 {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_12.json", "valid": "true"},
326 {"phase": "format validation", "test": "hostname format is valid", "schema": "format/schema_3.json", "data": "format/data_13.json", "valid": "true"},
327 {"phase": "format validation", "test": "hostname format is valid", "schema": "format/schema_3.json", "data": "format/data_14.json", "valid": "true"},
328 {"phase": "format validation", "test": "hostname format is valid", "schema": "format/schema_3.json", "data": "format/data_15.json", "valid": "true"},
329 {"phase": "format validation", "test": "hostname format is invalid", "schema": "format/schema_3.json", "data": "format/data_16.json", "valid": "false", "errors": "format"},
330 {"phase": "format validation", "test": "hostname format is invalid", "schema": "format/schema_3.json", "data": "format/data_17.json", "valid": "false", "errors": "format"},
331 {"phase": "format validation", "test": "ipv4 format is valid", "schema": "format/schema_4.json", "data": "format/data_18.json", "valid": "true"},
332 {"phase": "format validation", "test": "ipv4 format is invalid", "schema": "format/schema_4.json", "data": "format/data_19.json", "valid": "false", "errors": "format"},
333 {"phase": "format validation", "test": "ipv6 format is valid", "schema": "format/schema_5.json", "data": "format/data_20.json", "valid": "true"},
334 {"phase": "format validation", "test": "ipv6 format is valid", "schema": "format/schema_5.json", "data": "format/data_21.json", "valid": "true"},
335 {"phase": "format validation", "test": "ipv6 format is invalid", "schema": "format/schema_5.json", "data": "format/data_22.json", "valid": "false", "errors": "format"},
336 {"phase": "format validation", "test": "ipv6 format is invalid", "schema": "format/schema_5.json", "data": "format/data_23.json", "valid": "false", "errors": "format"},
337 {"phase": "format validation", "test": "uri format is valid", "schema": "format/schema_6.json", "data": "format/data_24.json", "valid": "true"},
338 {"phase": "format validation", "test": "uri format is valid", "schema": "format/schema_6.json", "data": "format/data_25.json", "valid": "true"},
339 {"phase": "format validation", "test": "uri format is valid", "schema": "format/schema_6.json", "data": "format/data_26.json", "valid": "true"},
340 {"phase": "format validation", "test": "uri format is valid", "schema": "format/schema_6.json", "data": "format/data_27.json", "valid": "true"},
341 {"phase": "format validation", "test": "uri format is invalid", "schema": "format/schema_6.json", "data": "format/data_28.json", "valid": "false", "errors": "format"},
342 {"phase": "format validation", "test": "uri format is invalid", "schema": "format/schema_6.json", "data": "format/data_13.json", "valid": "false", "errors": "format"},
343 }
344
345 //TODO Pass failed tests : id(s) as scope for references is not implemented yet
346 //map[string]string{"phase": "change resolution scope", "test": "changed scope ref valid", "schema": "refRemote/schema_3.json", "data": "refRemote/data_30.json", "valid": "true"},
347 //map[string]string{"phase": "change resolution scope", "test": "changed scope ref invalid", "schema": "refRemote/schema_3.json", "data": "refRemote/data_31.json", "valid": "false"}}
348
349 // Setup a small http server on localhost:1234 for testing purposes
350
351 wd, err := os.Getwd()
352 if err != nil {
353 panic(err.Error())
354 }
355
356 testwd := wd + "/json_schema_test_suite"
357
358 go func() {
359 err := http.ListenAndServe(":1234", http.FileServer(http.Dir(testwd+"/refRemote/remoteFiles")))
360 if err != nil {
361 panic(err.Error())
362 }
363 }()
364
365 // Used for remote schema in ref/schema_5.json that defines "regex" type
366 FormatCheckers.Add("regex", alwaysTrueFormatChecker{})
367
368 // Custom Formatter
369 FormatCheckers.Add("invoice", invoiceFormatChecker{})
370
371 // Launch tests
372
373 for testJsonIndex, testJson := range JsonSchemaTestSuiteMap {
374
375 fmt.Printf("Test (%d) | %s :: %s\n", testJsonIndex, testJson["phase"], testJson["test"])
376
377 schemaLoader := NewReferenceLoader("file://" + testwd + "/" + testJson["schema"])
378 documentLoader := NewReferenceLoader("file://" + testwd + "/" + testJson["data"])
379
380 // validate
381 result, err := Validate(schemaLoader, documentLoader)
382 if err != nil {
383 t.Errorf("Error (%s)\n", err.Error())
384 }
385 givenValid := result.Valid()
386
387 if displayErrorMessages {
388 for vErrI, vErr := range result.Errors() {
389 fmt.Printf(" Error (%d) | %s\n", vErrI, vErr)
390 }
391 }
392
393 expectedValid, _ := strconv.ParseBool(testJson["valid"])
394 if givenValid != expectedValid {
395 t.Errorf("Test failed : %s :: %s, expects %t, given %t\n", testJson["phase"], testJson["test"], expectedValid, givenValid)
396 }
397
398 if !givenValid && testJson["errors"] != "" {
399 expectedErrors := rxSplitErrors.Split(testJson["errors"], -1)
400 errors := result.Errors()
401 if len(errors) != len(expectedErrors) {
402 t.Errorf("Test failed : %s :: %s, expects %d errors, given %d errors\n", testJson["phase"], testJson["test"], len(expectedErrors), len(errors))
403 }
404
405 actualErrors := make([]string, 0)
406 for _, e := range result.Errors() {
407 actualErrors = append(actualErrors, e.Type())
408 }
409
410 sort.Strings(actualErrors)
411 sort.Strings(expectedErrors)
412 if !reflect.DeepEqual(actualErrors, expectedErrors) {
413 t.Errorf("Test failed : %s :: %s, expected '%s' errors, given '%s' errors\n", testJson["phase"], testJson["test"], strings.Join(expectedErrors, ", "), strings.Join(actualErrors, ", "))
414 }
415 }
416
417 }
418
419 fmt.Printf("\n%d tests performed / %d total tests to perform ( %.2f %% )\n", len(JsonSchemaTestSuiteMap), 248, float32(len(JsonSchemaTestSuiteMap))/248.0*100.0)
420 }
42140
42241 const circularReference = `{
42342 "type": "object",
564183 assert.NotNil(t, err, "expected error loading invalid pattern: %T", l)
565184 }
566185 }
186
187 const refPropertySchema = `{
188 "$id" : "http://localhost/schema.json",
189 "properties" : {
190 "$id" : {
191 "$id": "http://localhost/foo.json"
192 },
193 "$ref" : {
194 "const": {
195 "$ref" : "hello.world"
196 }
197 },
198 "const" : {
199 "$ref" : "#/definitions/$ref"
200 }
201 },
202 "definitions" : {
203 "$ref" : {
204 "const": {
205 "$ref" : "hello.world"
206 }
207 }
208 },
209 "dependencies" : {
210 "$ref" : [ "const" ],
211 "const" : [ "$ref" ]
212 }
213 }`
214
215 func TestRefProperty(t *testing.T) {
216 schemaLoader := NewStringLoader(refPropertySchema)
217 documentLoader := NewStringLoader(`{
218 "$ref" : { "$ref" : "hello.world" },
219 "const" : { "$ref" : "hello.world" }
220 }`)
221 // call the target function
222 s, err := NewSchema(schemaLoader)
223 if err != nil {
224 t.Errorf("Got error: %s", err.Error())
225 }
226 result, err := s.Validate(documentLoader)
227 if err != nil {
228 t.Errorf("Got error: %s", err.Error())
229 }
230 if !result.Valid() {
231 for _, err := range result.Errors() {
232 fmt.Println(err.String())
233 }
234 t.Errorf("Got invalid validation result.")
235 }
236 }
237
238 func TestFragmentLoader(t *testing.T) {
239 wd, err := os.Getwd()
240
241 if err != nil {
242 panic(err.Error())
243 }
244
245 fileName := filepath.Join(wd, "testdata", "extra", "fragment_schema.json")
246
247 schemaLoader := NewReferenceLoader("file://" + filepath.ToSlash(fileName) + "#/definitions/x")
248 schema, err := NewSchema(schemaLoader)
249
250 if err != nil {
251 t.Errorf("Encountered error while loading schema: %s", err.Error())
252 }
253
254 validDocument := NewStringLoader(`5`)
255 invalidDocument := NewStringLoader(`"a"`)
256
257 result, err := schema.Validate(validDocument)
258
259 if assert.Nil(t, err, "Unexpected error while validating document: %T", err) {
260 if !result.Valid() {
261 t.Errorf("Got invalid validation result.")
262 }
263 }
264
265 result, err = schema.Validate(invalidDocument)
266
267 if assert.Nil(t, err, "Unexpected error while validating document: %T", err) {
268 if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" {
269 t.Errorf("Got invalid validation result.")
270 }
271 }
272 }
273
274 func TestFileWithSpace(t *testing.T) {
275 wd, err := os.Getwd()
276
277 if err != nil {
278 panic(err.Error())
279 }
280
281 fileName := filepath.Join(wd, "testdata", "extra", "file with space.json")
282 loader := NewReferenceLoader("file://" + filepath.ToSlash(fileName))
283
284 json, err := loader.LoadJSON()
285
286 assert.Nil(t, err, "Unexpected error when trying to load a filepath containing a space")
287 assert.Equal(t, map[string]interface{}{"foo": true}, json, "Contents of the file do not match")
288 }
289
290 func TestAdditionalPropertiesErrorMessage(t *testing.T) {
291 schema := `{
292 "$schema": "http://json-schema.org/draft-07/schema#",
293 "type": "object",
294 "properties": {
295 "Device": {
296 "type": "object",
297 "additionalProperties": {
298 "type": "string"
299 }
300 }
301 }
302 }`
303 text := `{
304 "Device":{
305 "Color" : true
306 }
307 }`
308 loader := NewBytesLoader([]byte(schema))
309 result, err := Validate(loader, NewBytesLoader([]byte(text)))
310 if err != nil {
311 t.Fatal(err)
312 }
313
314 if len(result.Errors()) != 1 {
315 t.Fatal("Expected 1 error but got", len(result.Errors()))
316 }
317
318 expected := "Device.Color: Invalid type. Expected: string, given: boolean"
319 actual := result.Errors()[0].String()
320 if actual != expected {
321 t.Fatalf("Expected '%s' but got '%s'", expected, actual)
322 }
323 }
324
325 // Inspired by http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3
326 const locationIndependentSchema = `{
327 "definitions": {
328 "A": {
329 "$id": "#foo"
330 },
331 "B": {
332 "$id": "http://example.com/other.json",
333 "definitions": {
334 "X": {
335 "$id": "#bar",
336 "allOf": [false]
337 },
338 "Y": {
339 "$id": "t/inner.json"
340 }
341 }
342 },
343 "C": {
344 "$id" : "#frag",
345 "$ref": "http://example.com/other.json#bar"
346 }
347 },
348 "$ref": "#frag"
349 }`
350
351 func TestLocationIndependentIdentifier(t *testing.T) {
352 schemaLoader := NewStringLoader(locationIndependentSchema)
353 documentLoader := NewStringLoader(`{}`)
354
355 s, err := NewSchema(schemaLoader)
356 if err != nil {
357 t.Errorf("Got error: %s", err.Error())
358 }
359
360 result, err := s.Validate(documentLoader)
361 if err != nil {
362 t.Errorf("Got error: %s", err.Error())
363 }
364
365 if len(result.Errors()) != 2 || result.Errors()[0].Type() != "false" || result.Errors()[1].Type() != "number_all_of" {
366 t.Errorf("Got invalid validation result.")
367 }
368 }
369
370 const incorrectRefSchema = `{
371 "$ref" : "#/fail"
372 }`
373
374 func TestIncorrectRef(t *testing.T) {
375
376 schemaLoader := NewStringLoader(incorrectRefSchema)
377 s, err := NewSchema(schemaLoader)
378
379 assert.Nil(t, s)
380 assert.Equal(t, "Object has no key 'fail'", err.Error())
381 }
2626 package gojsonschema
2727
2828 import (
29 "errors"
29 "github.com/xeipuuv/gojsonreference"
30 "math/big"
3031 "regexp"
31 "strings"
32
33 "github.com/xeipuuv/gojsonreference"
3432 )
3533
34 // Constants
3635 const (
37 KEY_SCHEMA = "$subSchema"
38 KEY_ID = "$id"
36 KEY_SCHEMA = "$schema"
37 KEY_ID = "id"
38 KEY_ID_NEW = "$id"
3939 KEY_REF = "$ref"
4040 KEY_TITLE = "title"
4141 KEY_DESCRIPTION = "description"
4545 KEY_PROPERTIES = "properties"
4646 KEY_PATTERN_PROPERTIES = "patternProperties"
4747 KEY_ADDITIONAL_PROPERTIES = "additionalProperties"
48 KEY_PROPERTY_NAMES = "propertyNames"
4849 KEY_DEFINITIONS = "definitions"
4950 KEY_MULTIPLE_OF = "multipleOf"
5051 KEY_MINIMUM = "minimum"
6263 KEY_MIN_ITEMS = "minItems"
6364 KEY_MAX_ITEMS = "maxItems"
6465 KEY_UNIQUE_ITEMS = "uniqueItems"
66 KEY_CONTAINS = "contains"
67 KEY_CONST = "const"
6568 KEY_ENUM = "enum"
6669 KEY_ONE_OF = "oneOf"
6770 KEY_ANY_OF = "anyOf"
6871 KEY_ALL_OF = "allOf"
6972 KEY_NOT = "not"
73 KEY_IF = "if"
74 KEY_THEN = "then"
75 KEY_ELSE = "else"
7076 )
7177
7278 type subSchema struct {
79 draft *Draft
7380
7481 // basic subSchema meta properties
75 id *string
82 id *gojsonreference.JsonReference
7683 title *string
7784 description *string
7885
7986 property string
87
88 // Quick pass/fail for boolean schemas
89 pass *bool
8090
8191 // Types associated with the subSchema
8292 types jsonSchemaType
8595 ref *gojsonreference.JsonReference
8696 // Schema referenced
8797 refSchema *subSchema
88 // Json reference
89 subSchema *gojsonreference.JsonReference
9098
9199 // hierarchy
92100 parent *subSchema
93 definitions map[string]*subSchema
94 definitionsChildren []*subSchema
95101 itemsChildren []*subSchema
96102 itemsChildrenIsSingleSchema bool
97103 propertiesChildren []*subSchema
98104
99105 // validation : number / integer
100 multipleOf *float64
101 maximum *float64
102 exclusiveMaximum bool
103 minimum *float64
104 exclusiveMinimum bool
106 multipleOf *big.Rat
107 maximum *big.Rat
108 exclusiveMaximum *big.Rat
109 minimum *big.Rat
110 exclusiveMinimum *big.Rat
105111
106112 // validation : string
107113 minLength *int
117123 dependencies map[string]interface{}
118124 additionalProperties interface{}
119125 patternProperties map[string]*subSchema
126 propertyNames *subSchema
120127
121128 // validation : array
122129 minItems *int
123130 maxItems *int
124131 uniqueItems bool
132 contains *subSchema
125133
126134 additionalItems interface{}
127135
128136 // validation : all
129 enum []string
137 _const *string //const is a golang keyword
138 enum []string
130139
131140 // validation : subSchema
132141 oneOf []*subSchema
133142 anyOf []*subSchema
134143 allOf []*subSchema
135144 not *subSchema
145 _if *subSchema // if/else are golang keywords
146 _then *subSchema
147 _else *subSchema
136148 }
137
138 func (s *subSchema) AddEnum(i interface{}) error {
139
140 is, err := marshalToJsonString(i)
141 if err != nil {
142 return err
143 }
144
145 if isStringInSlice(s.enum, *is) {
146 return errors.New(formatErrorDescription(
147 Locale.KeyItemsMustBeUnique(),
148 ErrorDetails{"key": KEY_ENUM},
149 ))
150 }
151
152 s.enum = append(s.enum, *is)
153
154 return nil
155 }
156
157 func (s *subSchema) ContainsEnum(i interface{}) (bool, error) {
158
159 is, err := marshalToJsonString(i)
160 if err != nil {
161 return false, err
162 }
163
164 return isStringInSlice(s.enum, *is), nil
165 }
166
167 func (s *subSchema) AddOneOf(subSchema *subSchema) {
168 s.oneOf = append(s.oneOf, subSchema)
169 }
170
171 func (s *subSchema) AddAllOf(subSchema *subSchema) {
172 s.allOf = append(s.allOf, subSchema)
173 }
174
175 func (s *subSchema) AddAnyOf(subSchema *subSchema) {
176 s.anyOf = append(s.anyOf, subSchema)
177 }
178
179 func (s *subSchema) SetNot(subSchema *subSchema) {
180 s.not = subSchema
181 }
182
183 func (s *subSchema) AddRequired(value string) error {
184
185 if isStringInSlice(s.required, value) {
186 return errors.New(formatErrorDescription(
187 Locale.KeyItemsMustBeUnique(),
188 ErrorDetails{"key": KEY_REQUIRED},
189 ))
190 }
191
192 s.required = append(s.required, value)
193
194 return nil
195 }
196
197 func (s *subSchema) AddDefinitionChild(child *subSchema) {
198 s.definitionsChildren = append(s.definitionsChildren, child)
199 }
200
201 func (s *subSchema) AddItemsChild(child *subSchema) {
202 s.itemsChildren = append(s.itemsChildren, child)
203 }
204
205 func (s *subSchema) AddPropertiesChild(child *subSchema) {
206 s.propertiesChildren = append(s.propertiesChildren, child)
207 }
208
209 func (s *subSchema) PatternPropertiesString() string {
210
211 if s.patternProperties == nil || len(s.patternProperties) == 0 {
212 return STRING_UNDEFINED // should never happen
213 }
214
215 patternPropertiesKeySlice := []string{}
216 for pk := range s.patternProperties {
217 patternPropertiesKeySlice = append(patternPropertiesKeySlice, `"`+pk+`"`)
218 }
219
220 if len(patternPropertiesKeySlice) == 1 {
221 return patternPropertiesKeySlice[0]
222 }
223
224 return "[" + strings.Join(patternPropertiesKeySlice, ",") + "]"
225
226 }
0 [
1 {
2 "description": "additionalItems as schema",
3 "schema": {
4 "items": [{}],
5 "additionalItems": {"type": "integer"}
6 },
7 "tests": [
8 {
9 "description": "additional items match schema",
10 "data": [ null, 2, 3, 4 ],
11 "valid": true
12 },
13 {
14 "description": "additional items do not match schema",
15 "data": [ null, 2, 3, "foo" ],
16 "valid": false
17 }
18 ]
19 },
20 {
21 "description": "items is schema, no additionalItems",
22 "schema": {
23 "items": {},
24 "additionalItems": false
25 },
26 "tests": [
27 {
28 "description": "all items match schema",
29 "data": [ 1, 2, 3, 4, 5 ],
30 "valid": true
31 }
32 ]
33 },
34 {
35 "description": "array of items with no additionalItems",
36 "schema": {
37 "items": [{}, {}, {}],
38 "additionalItems": false
39 },
40 "tests": [
41 {
42 "description": "fewer number of items present",
43 "data": [ 1, 2 ],
44 "valid": true
45 },
46 {
47 "description": "equal number of items present",
48 "data": [ 1, 2, 3 ],
49 "valid": true
50 },
51 {
52 "description": "additional items are not permitted",
53 "data": [ 1, 2, 3, 4 ],
54 "valid": false
55 }
56 ]
57 },
58 {
59 "description": "additionalItems as false without items",
60 "schema": {"additionalItems": false},
61 "tests": [
62 {
63 "description":
64 "items defaults to empty schema so everything is valid",
65 "data": [ 1, 2, 3, 4, 5 ],
66 "valid": true
67 },
68 {
69 "description": "ignores non-arrays",
70 "data": {"foo" : "bar"},
71 "valid": true
72 }
73 ]
74 },
75 {
76 "description": "additionalItems are allowed by default",
77 "schema": {"items": [{"type": "integer"}]},
78 "tests": [
79 {
80 "description": "only the first item is validated",
81 "data": [1, "foo", false],
82 "valid": true
83 }
84 ]
85 }
86 ]
0 [
1 {
2 "description":
3 "additionalProperties being false does not allow other properties",
4 "schema": {
5 "properties": {"foo": {}, "bar": {}},
6 "patternProperties": { "^v": {} },
7 "additionalProperties": false
8 },
9 "tests": [
10 {
11 "description": "no additional properties is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "an additional property is invalid",
17 "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [1, 2, 3],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "foobarbaz",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 },
35 {
36 "description": "patternProperties are not additional properties",
37 "data": {"foo":1, "vroom": 2},
38 "valid": true
39 }
40 ]
41 },
42 {
43 "description":
44 "additionalProperties allows a schema which should validate",
45 "schema": {
46 "properties": {"foo": {}, "bar": {}},
47 "additionalProperties": {"type": "boolean"}
48 },
49 "tests": [
50 {
51 "description": "no additional properties is valid",
52 "data": {"foo": 1},
53 "valid": true
54 },
55 {
56 "description": "an additional valid property is valid",
57 "data": {"foo" : 1, "bar" : 2, "quux" : true},
58 "valid": true
59 },
60 {
61 "description": "an additional invalid property is invalid",
62 "data": {"foo" : 1, "bar" : 2, "quux" : 12},
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description":
69 "additionalProperties can exist by itself",
70 "schema": {
71 "additionalProperties": {"type": "boolean"}
72 },
73 "tests": [
74 {
75 "description": "an additional valid property is valid",
76 "data": {"foo" : true},
77 "valid": true
78 },
79 {
80 "description": "an additional invalid property is invalid",
81 "data": {"foo" : 1},
82 "valid": false
83 }
84 ]
85 },
86 {
87 "description": "additionalProperties are allowed by default",
88 "schema": {"properties": {"foo": {}, "bar": {}}},
89 "tests": [
90 {
91 "description": "additional properties are allowed",
92 "data": {"foo": 1, "bar": 2, "quux": true},
93 "valid": true
94 }
95 ]
96 }
97 ]
0 [
1 {
2 "description": "allOf",
3 "schema": {
4 "allOf": [
5 {
6 "properties": {
7 "bar": {"type": "integer"}
8 },
9 "required": ["bar"]
10 },
11 {
12 "properties": {
13 "foo": {"type": "string"}
14 },
15 "required": ["foo"]
16 }
17 ]
18 },
19 "tests": [
20 {
21 "description": "allOf",
22 "data": {"foo": "baz", "bar": 2},
23 "valid": true
24 },
25 {
26 "description": "mismatch second",
27 "data": {"foo": "baz"},
28 "valid": false
29 },
30 {
31 "description": "mismatch first",
32 "data": {"bar": 2},
33 "valid": false
34 },
35 {
36 "description": "wrong type",
37 "data": {"foo": "baz", "bar": "quux"},
38 "valid": false
39 }
40 ]
41 },
42 {
43 "description": "allOf with base schema",
44 "schema": {
45 "properties": {"bar": {"type": "integer"}},
46 "required": ["bar"],
47 "allOf" : [
48 {
49 "properties": {
50 "foo": {"type": "string"}
51 },
52 "required": ["foo"]
53 },
54 {
55 "properties": {
56 "baz": {"type": "null"}
57 },
58 "required": ["baz"]
59 }
60 ]
61 },
62 "tests": [
63 {
64 "description": "valid",
65 "data": {"foo": "quux", "bar": 2, "baz": null},
66 "valid": true
67 },
68 {
69 "description": "mismatch base schema",
70 "data": {"foo": "quux", "baz": null},
71 "valid": false
72 },
73 {
74 "description": "mismatch first allOf",
75 "data": {"bar": 2, "baz": null},
76 "valid": false
77 },
78 {
79 "description": "mismatch second allOf",
80 "data": {"foo": "quux", "bar": 2},
81 "valid": false
82 },
83 {
84 "description": "mismatch both",
85 "data": {"bar": 2},
86 "valid": false
87 }
88 ]
89 },
90 {
91 "description": "allOf simple types",
92 "schema": {
93 "allOf": [
94 {"maximum": 30},
95 {"minimum": 20}
96 ]
97 },
98 "tests": [
99 {
100 "description": "valid",
101 "data": 25,
102 "valid": true
103 },
104 {
105 "description": "mismatch one",
106 "data": 35,
107 "valid": false
108 }
109 ]
110 }
111 ]
0 [
1 {
2 "description": "anyOf",
3 "schema": {
4 "anyOf": [
5 {
6 "type": "integer"
7 },
8 {
9 "minimum": 2
10 }
11 ]
12 },
13 "tests": [
14 {
15 "description": "first anyOf valid",
16 "data": 1,
17 "valid": true
18 },
19 {
20 "description": "second anyOf valid",
21 "data": 2.5,
22 "valid": true
23 },
24 {
25 "description": "both anyOf valid",
26 "data": 3,
27 "valid": true
28 },
29 {
30 "description": "neither anyOf valid",
31 "data": 1.5,
32 "valid": false
33 }
34 ]
35 },
36 {
37 "description": "anyOf with base schema",
38 "schema": {
39 "type": "string",
40 "anyOf" : [
41 {
42 "maxLength": 2
43 },
44 {
45 "minLength": 4
46 }
47 ]
48 },
49 "tests": [
50 {
51 "description": "mismatch base schema",
52 "data": 3,
53 "valid": false
54 },
55 {
56 "description": "one anyOf valid",
57 "data": "foobar",
58 "valid": true
59 },
60 {
61 "description": "both anyOf invalid",
62 "data": "foo",
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description": "anyOf complex types",
69 "schema": {
70 "anyOf": [
71 {
72 "properties": {
73 "bar": {"type": "integer"}
74 },
75 "required": ["bar"]
76 },
77 {
78 "properties": {
79 "foo": {"type": "string"}
80 },
81 "required": ["foo"]
82 }
83 ]
84 },
85 "tests": [
86 {
87 "description": "first anyOf valid (complex)",
88 "data": {"bar": 2},
89 "valid": true
90 },
91 {
92 "description": "second anyOf valid (complex)",
93 "data": {"foo": "baz"},
94 "valid": true
95 },
96 {
97 "description": "both anyOf valid (complex)",
98 "data": {"foo": "baz", "bar": 2},
99 "valid": true
100 },
101 {
102 "description": "neither anyOf valid (complex)",
103 "data": {"foo": 2, "bar": "quux"},
104 "valid": false
105 }
106 ]
107 }
108 ]
0 [
1 {
2 "description": "invalid type for default",
3 "schema": {
4 "properties": {
5 "foo": {
6 "type": "integer",
7 "default": []
8 }
9 }
10 },
11 "tests": [
12 {
13 "description": "valid when property is specified",
14 "data": {"foo": 13},
15 "valid": true
16 },
17 {
18 "description": "still valid when the invalid default is used",
19 "data": {},
20 "valid": true
21 }
22 ]
23 },
24 {
25 "description": "invalid string value for default",
26 "schema": {
27 "properties": {
28 "bar": {
29 "type": "string",
30 "minLength": 4,
31 "default": "bad"
32 }
33 }
34 },
35 "tests": [
36 {
37 "description": "valid when property is specified",
38 "data": {"bar": "good"},
39 "valid": true
40 },
41 {
42 "description": "still valid when the invalid default is used",
43 "data": {},
44 "valid": true
45 }
46 ]
47 }
48 ]
0 [
1 {
2 "description": "valid definition",
3 "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
4 "tests": [
5 {
6 "description": "valid definition schema",
7 "data": {
8 "definitions": {
9 "foo": {"type": "integer"}
10 }
11 },
12 "valid": true
13 }
14 ]
15 },
16 {
17 "description": "invalid definition",
18 "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
19 "tests": [
20 {
21 "description": "invalid definition schema",
22 "data": {
23 "definitions": {
24 "foo": {"type": 1}
25 }
26 },
27 "valid": false
28 }
29 ]
30 }
31 ]
0 [
1 {
2 "description": "dependencies",
3 "schema": {
4 "dependencies": {"bar": ["foo"]}
5 },
6 "tests": [
7 {
8 "description": "neither",
9 "data": {},
10 "valid": true
11 },
12 {
13 "description": "nondependant",
14 "data": {"foo": 1},
15 "valid": true
16 },
17 {
18 "description": "with dependency",
19 "data": {"foo": 1, "bar": 2},
20 "valid": true
21 },
22 {
23 "description": "missing dependency",
24 "data": {"bar": 2},
25 "valid": false
26 },
27 {
28 "description": "ignores arrays",
29 "data": ["bar"],
30 "valid": true
31 },
32 {
33 "description": "ignores strings",
34 "data": "foobar",
35 "valid": true
36 },
37 {
38 "description": "ignores other non-objects",
39 "data": 12,
40 "valid": true
41 }
42 ]
43 },
44 {
45 "description": "multiple dependencies",
46 "schema": {
47 "dependencies": {"quux": ["foo", "bar"]}
48 },
49 "tests": [
50 {
51 "description": "neither",
52 "data": {},
53 "valid": true
54 },
55 {
56 "description": "nondependants",
57 "data": {"foo": 1, "bar": 2},
58 "valid": true
59 },
60 {
61 "description": "with dependencies",
62 "data": {"foo": 1, "bar": 2, "quux": 3},
63 "valid": true
64 },
65 {
66 "description": "missing dependency",
67 "data": {"foo": 1, "quux": 2},
68 "valid": false
69 },
70 {
71 "description": "missing other dependency",
72 "data": {"bar": 1, "quux": 2},
73 "valid": false
74 },
75 {
76 "description": "missing both dependencies",
77 "data": {"quux": 1},
78 "valid": false
79 }
80 ]
81 },
82 {
83 "description": "multiple dependencies subschema",
84 "schema": {
85 "dependencies": {
86 "bar": {
87 "properties": {
88 "foo": {"type": "integer"},
89 "bar": {"type": "integer"}
90 }
91 }
92 }
93 },
94 "tests": [
95 {
96 "description": "valid",
97 "data": {"foo": 1, "bar": 2},
98 "valid": true
99 },
100 {
101 "description": "no dependency",
102 "data": {"foo": "quux"},
103 "valid": true
104 },
105 {
106 "description": "wrong type",
107 "data": {"foo": "quux", "bar": 2},
108 "valid": false
109 },
110 {
111 "description": "wrong type other",
112 "data": {"foo": 2, "bar": "quux"},
113 "valid": false
114 },
115 {
116 "description": "wrong type both",
117 "data": {"foo": "quux", "bar": "quux"},
118 "valid": false
119 }
120 ]
121 }
122 ]
0 [
1 {
2 "description": "simple enum validation",
3 "schema": {"enum": [1, 2, 3]},
4 "tests": [
5 {
6 "description": "one of the enum is valid",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "something else is invalid",
12 "data": 4,
13 "valid": false
14 }
15 ]
16 },
17 {
18 "description": "heterogeneous enum validation",
19 "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
20 "tests": [
21 {
22 "description": "one of the enum is valid",
23 "data": [],
24 "valid": true
25 },
26 {
27 "description": "something else is invalid",
28 "data": null,
29 "valid": false
30 },
31 {
32 "description": "objects are deep compared",
33 "data": {"foo": false},
34 "valid": false
35 }
36 ]
37 },
38 {
39 "description": "enums in properties",
40 "schema": {
41 "type":"object",
42 "properties": {
43 "foo": {"enum":["foo"]},
44 "bar": {"enum":["bar"]}
45 },
46 "required": ["bar"]
47 },
48 "tests": [
49 {
50 "description": "both properties are valid",
51 "data": {"foo":"foo", "bar":"bar"},
52 "valid": true
53 },
54 {
55 "description": "missing optional property is valid",
56 "data": {"bar":"bar"},
57 "valid": true
58 },
59 {
60 "description": "missing required property is invalid",
61 "data": {"foo":"foo"},
62 "valid": false
63 },
64 {
65 "description": "missing all properties is invalid",
66 "data": {},
67 "valid": false
68 }
69 ]
70 }
71 ]
0 [
1 {
2 "description": "a schema given for items",
3 "schema": {
4 "items": {"type": "integer"}
5 },
6 "tests": [
7 {
8 "description": "valid items",
9 "data": [ 1, 2, 3 ],
10 "valid": true
11 },
12 {
13 "description": "wrong type of items",
14 "data": [1, "x"],
15 "valid": false
16 },
17 {
18 "description": "ignores non-arrays",
19 "data": {"foo" : "bar"},
20 "valid": true
21 },
22 {
23 "description": "JavaScript pseudo-array is valid",
24 "data": {
25 "0": "invalid",
26 "length": 1
27 },
28 "valid": true
29 }
30 ]
31 },
32 {
33 "description": "an array of schemas for items",
34 "schema": {
35 "items": [
36 {"type": "integer"},
37 {"type": "string"}
38 ]
39 },
40 "tests": [
41 {
42 "description": "correct types",
43 "data": [ 1, "foo" ],
44 "valid": true
45 },
46 {
47 "description": "wrong types",
48 "data": [ "foo", 1 ],
49 "valid": false
50 },
51 {
52 "description": "incomplete array of items",
53 "data": [ 1 ],
54 "valid": true
55 },
56 {
57 "description": "array with additional items",
58 "data": [ 1, "foo", true ],
59 "valid": true
60 },
61 {
62 "description": "empty array",
63 "data": [ ],
64 "valid": true
65 },
66 {
67 "description": "JavaScript pseudo-array is valid",
68 "data": {
69 "0": "invalid",
70 "1": "valid",
71 "length": 2
72 },
73 "valid": true
74 }
75 ]
76 }
77 ]
0 [
1 {
2 "description": "maxItems validation",
3 "schema": {"maxItems": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": [1],
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": [1, 2],
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": [1, 2, 3],
18 "valid": false
19 },
20 {
21 "description": "ignores non-arrays",
22 "data": "foobar",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "maxLength validation",
3 "schema": {"maxLength": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": "f",
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": "fo",
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": "foo",
18 "valid": false
19 },
20 {
21 "description": "ignores non-strings",
22 "data": 100,
23 "valid": true
24 },
25 {
26 "description": "two supplementary Unicode code points is long enough",
27 "data": "\uD83D\uDCA9\uD83D\uDCA9",
28 "valid": true
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "maxProperties validation",
3 "schema": {"maxProperties": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": {"foo": 1},
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": {"foo": 1, "bar": 2},
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": {"foo": 1, "bar": 2, "baz": 3},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [1, 2, 3],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "foobar",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 }
35 ]
36 }
37 ]
0 [
1 {
2 "description": "maximum validation",
3 "schema": {"maximum": 3.0},
4 "tests": [
5 {
6 "description": "below the maximum is valid",
7 "data": 2.6,
8 "valid": true
9 },
10 {
11 "description": "boundary point is valid",
12 "data": 3.0,
13 "valid": true
14 },
15 {
16 "description": "above the maximum is invalid",
17 "data": 3.5,
18 "valid": false
19 },
20 {
21 "description": "ignores non-numbers",
22 "data": "x",
23 "valid": true
24 }
25 ]
26 },
27 {
28 "description": "exclusiveMaximum validation",
29 "schema": {
30 "maximum": 3.0,
31 "exclusiveMaximum": true
32 },
33 "tests": [
34 {
35 "description": "below the maximum is still valid",
36 "data": 2.2,
37 "valid": true
38 },
39 {
40 "description": "boundary point is invalid",
41 "data": 3.0,
42 "valid": false
43 }
44 ]
45 }
46 ]
0 [
1 {
2 "description": "minItems validation",
3 "schema": {"minItems": 1},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": [1, 2],
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": [1],
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": [],
18 "valid": false
19 },
20 {
21 "description": "ignores non-arrays",
22 "data": "",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "minLength validation",
3 "schema": {"minLength": 2},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": "foo",
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": "fo",
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": "f",
18 "valid": false
19 },
20 {
21 "description": "ignores non-strings",
22 "data": 1,
23 "valid": true
24 },
25 {
26 "description": "one supplementary Unicode code point is not long enough",
27 "data": "\uD83D\uDCA9",
28 "valid": false
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "minProperties validation",
3 "schema": {"minProperties": 1},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": {"foo": 1, "bar": 2},
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": {},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 }
35 ]
36 }
37 ]
0 [
1 {
2 "description": "minimum validation",
3 "schema": {"minimum": 1.1},
4 "tests": [
5 {
6 "description": "above the minimum is valid",
7 "data": 2.6,
8 "valid": true
9 },
10 {
11 "description": "boundary point is valid",
12 "data": 1.1,
13 "valid": true
14 },
15 {
16 "description": "below the minimum is invalid",
17 "data": 0.6,
18 "valid": false
19 },
20 {
21 "description": "ignores non-numbers",
22 "data": "x",
23 "valid": true
24 }
25 ]
26 },
27 {
28 "description": "exclusiveMinimum validation",
29 "schema": {
30 "minimum": 1.1,
31 "exclusiveMinimum": true
32 },
33 "tests": [
34 {
35 "description": "above the minimum is still valid",
36 "data": 1.2,
37 "valid": true
38 },
39 {
40 "description": "boundary point is invalid",
41 "data": 1.1,
42 "valid": false
43 }
44 ]
45 }
46 ]
0 [
1 {
2 "description": "by int",
3 "schema": {"multipleOf": 2},
4 "tests": [
5 {
6 "description": "int by int",
7 "data": 10,
8 "valid": true
9 },
10 {
11 "description": "int by int fail",
12 "data": 7,
13 "valid": false
14 },
15 {
16 "description": "ignores non-numbers",
17 "data": "foo",
18 "valid": true
19 }
20 ]
21 },
22 {
23 "description": "by number",
24 "schema": {"multipleOf": 1.5},
25 "tests": [
26 {
27 "description": "zero is multiple of anything",
28 "data": 0,
29 "valid": true
30 },
31 {
32 "description": "4.5 is multiple of 1.5",
33 "data": 4.5,
34 "valid": true
35 },
36 {
37 "description": "35 is not multiple of 1.5",
38 "data": 35,
39 "valid": false
40 }
41 ]
42 },
43 {
44 "description": "by small number",
45 "schema": {"multipleOf": 0.0001},
46 "tests": [
47 {
48 "description": "0.0075 is multiple of 0.0001",
49 "data": 0.0075,
50 "valid": true
51 },
52 {
53 "description": "0.00751 is not multiple of 0.0001",
54 "data": 0.00751,
55 "valid": false
56 }
57 ]
58 }
59 ]
0 [
1 {
2 "description": "not",
3 "schema": {
4 "not": {"type": "integer"}
5 },
6 "tests": [
7 {
8 "description": "allowed",
9 "data": "foo",
10 "valid": true
11 },
12 {
13 "description": "disallowed",
14 "data": 1,
15 "valid": false
16 }
17 ]
18 },
19 {
20 "description": "not multiple types",
21 "schema": {
22 "not": {"type": ["integer", "boolean"]}
23 },
24 "tests": [
25 {
26 "description": "valid",
27 "data": "foo",
28 "valid": true
29 },
30 {
31 "description": "mismatch",
32 "data": 1,
33 "valid": false
34 },
35 {
36 "description": "other mismatch",
37 "data": true,
38 "valid": false
39 }
40 ]
41 },
42 {
43 "description": "not more complex schema",
44 "schema": {
45 "not": {
46 "type": "object",
47 "properties": {
48 "foo": {
49 "type": "string"
50 }
51 }
52 }
53 },
54 "tests": [
55 {
56 "description": "match",
57 "data": 1,
58 "valid": true
59 },
60 {
61 "description": "other match",
62 "data": {"foo": 1},
63 "valid": true
64 },
65 {
66 "description": "mismatch",
67 "data": {"foo": "bar"},
68 "valid": false
69 }
70 ]
71 },
72 {
73 "description": "forbidden property",
74 "schema": {
75 "properties": {
76 "foo": {
77 "not": {}
78 }
79 }
80 },
81 "tests": [
82 {
83 "description": "property present",
84 "data": {"foo": 1, "bar": 2},
85 "valid": false
86 },
87 {
88 "description": "property absent",
89 "data": {"bar": 1, "baz": 2},
90 "valid": true
91 }
92 ]
93 }
94
95 ]
0 [
1 {
2 "description": "oneOf",
3 "schema": {
4 "oneOf": [
5 {
6 "type": "integer"
7 },
8 {
9 "minimum": 2
10 }
11 ]
12 },
13 "tests": [
14 {
15 "description": "first oneOf valid",
16 "data": 1,
17 "valid": true
18 },
19 {
20 "description": "second oneOf valid",
21 "data": 2.5,
22 "valid": true
23 },
24 {
25 "description": "both oneOf valid",
26 "data": 3,
27 "valid": false
28 },
29 {
30 "description": "neither oneOf valid",
31 "data": 1.5,
32 "valid": false
33 }
34 ]
35 },
36 {
37 "description": "oneOf with base schema",
38 "schema": {
39 "type": "string",
40 "oneOf" : [
41 {
42 "minLength": 2
43 },
44 {
45 "maxLength": 4
46 }
47 ]
48 },
49 "tests": [
50 {
51 "description": "mismatch base schema",
52 "data": 3,
53 "valid": false
54 },
55 {
56 "description": "one oneOf valid",
57 "data": "foobar",
58 "valid": true
59 },
60 {
61 "description": "both oneOf valid",
62 "data": "foo",
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description": "oneOf complex types",
69 "schema": {
70 "oneOf": [
71 {
72 "properties": {
73 "bar": {"type": "integer"}
74 },
75 "required": ["bar"]
76 },
77 {
78 "properties": {
79 "foo": {"type": "string"}
80 },
81 "required": ["foo"]
82 }
83 ]
84 },
85 "tests": [
86 {
87 "description": "first oneOf valid (complex)",
88 "data": {"bar": 2},
89 "valid": true
90 },
91 {
92 "description": "second oneOf valid (complex)",
93 "data": {"foo": "baz"},
94 "valid": true
95 },
96 {
97 "description": "both oneOf valid (complex)",
98 "data": {"foo": "baz", "bar": 2},
99 "valid": false
100 },
101 {
102 "description": "neither oneOf valid (complex)",
103 "data": {"foo": 2, "bar": "quux"},
104 "valid": false
105 }
106 ]
107 }
108 ]
0 [
1 {
2 "description": "integer",
3 "schema": {"type": "integer"},
4 "tests": [
5 {
6 "description": "a bignum is an integer",
7 "data": 12345678910111213141516171819202122232425262728293031,
8 "valid": true
9 }
10 ]
11 },
12 {
13 "description": "number",
14 "schema": {"type": "number"},
15 "tests": [
16 {
17 "description": "a bignum is a number",
18 "data": 98249283749234923498293171823948729348710298301928331,
19 "valid": true
20 }
21 ]
22 },
23 {
24 "description": "integer",
25 "schema": {"type": "integer"},
26 "tests": [
27 {
28 "description": "a negative bignum is an integer",
29 "data": -12345678910111213141516171819202122232425262728293031,
30 "valid": true
31 }
32 ]
33 },
34 {
35 "description": "number",
36 "schema": {"type": "number"},
37 "tests": [
38 {
39 "description": "a negative bignum is a number",
40 "data": -98249283749234923498293171823948729348710298301928331,
41 "valid": true
42 }
43 ]
44 },
45 {
46 "description": "string",
47 "schema": {"type": "string"},
48 "tests": [
49 {
50 "description": "a bignum is not a string",
51 "data": 98249283749234923498293171823948729348710298301928331,
52 "valid": false
53 }
54 ]
55 },
56 {
57 "description": "integer comparison",
58 "schema": {"maximum": 18446744073709551615},
59 "tests": [
60 {
61 "description": "comparison works for high numbers",
62 "data": 18446744073709551600,
63 "valid": true
64 }
65 ]
66 },
67 {
68 "description": "float comparison with high precision",
69 "schema": {
70 "maximum": 972783798187987123879878123.18878137,
71 "exclusiveMaximum": true
72 },
73 "tests": [
74 {
75 "description": "comparison works for high numbers",
76 "data": 972783798187987123879878123.188781371,
77 "valid": false
78 }
79 ]
80 },
81 {
82 "description": "integer comparison",
83 "schema": {"minimum": -18446744073709551615},
84 "tests": [
85 {
86 "description": "comparison works for very negative numbers",
87 "data": -18446744073709551600,
88 "valid": true
89 }
90 ]
91 },
92 {
93 "description": "float comparison with high precision on negative numbers",
94 "schema": {
95 "minimum": -972783798187987123879878123.18878137,
96 "exclusiveMinimum": true
97 },
98 "tests": [
99 {
100 "description": "comparison works for very negative numbers",
101 "data": -972783798187987123879878123.188781371,
102 "valid": false
103 }
104 ]
105 }
106 ]
0 [
1 {
2 "description": "ECMA 262 regex non-compliance",
3 "schema": { "format": "regex" },
4 "tests": [
5 {
6 "description": "ECMA 262 has no support for \\Z anchor from .NET",
7 "data": "^\\S(|(.|\\n)*\\S)\\Z",
8 "valid": false
9 }
10 ]
11 }
12 ]
0 [
1 {
2 "description": "validation of date-time strings",
3 "schema": {"format": "date-time"},
4 "tests": [
5 {
6 "description": "a valid date-time string",
7 "data": "1963-06-19T08:30:06.283185Z",
8 "valid": true
9 },
10 {
11 "description": "an invalid date-time string",
12 "data": "06/19/1963 08:30:06 PST",
13 "valid": false
14 },
15 {
16 "description": "only RFC3339 not all of ISO 8601 are valid",
17 "data": "2013-350T01:01:01",
18 "valid": false
19 }
20 ]
21 },
22 {
23 "description": "validation of URIs",
24 "schema": {"format": "uri"},
25 "tests": [
26 {
27 "description": "a valid URL with anchor tag",
28 "data": "http://foo.bar/?baz=qux#quux",
29 "valid": true
30 },
31 {
32 "description": "a valid URL with anchor tag and parantheses",
33 "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
34 "valid": true
35 },
36 {
37 "description": "a valid URL with URL-encoded stuff",
38 "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
39 "valid": true
40 },
41 {
42 "description": "a valid puny-coded URL ",
43 "data": "http://xn--nw2a.xn--j6w193g/",
44 "valid": true
45 },
46 {
47 "description": "a valid URL with many special characters",
48 "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
49 "valid": true
50 },
51 {
52 "description": "a valid URL based on IPv4",
53 "data": "http://223.255.255.254",
54 "valid": true
55 },
56 {
57 "description": "a valid URL with ftp scheme",
58 "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
59 "valid": true
60 },
61 {
62 "description": "a valid URL for a simple text file",
63 "data": "http://www.ietf.org/rfc/rfc2396.txt",
64 "valid": true
65 },
66 {
67 "description": "a valid URL ",
68 "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
69 "valid": true
70 },
71 {
72 "description": "a valid mailto URI",
73 "data": "mailto:John.Doe@example.com",
74 "valid": true
75 },
76 {
77 "description": "a valid newsgroup URI",
78 "data": "news:comp.infosystems.www.servers.unix",
79 "valid": true
80 },
81 {
82 "description": "a valid tel URI",
83 "data": "tel:+1-816-555-1212",
84 "valid": true
85 },
86 {
87 "description": "a valid URN",
88 "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
89 "valid": true
90 },
91 {
92 "description": "an invalid protocol-relative URI Reference",
93 "data": "//foo.bar/?baz=qux#quux",
94 "valid": false
95 },
96 {
97 "description": "an invalid relative URI Reference",
98 "data": "/abc",
99 "valid": false
100 },
101 {
102 "description": "an invalid URI",
103 "data": "\\\\WINDOWS\\fileshare",
104 "valid": false
105 },
106 {
107 "description": "an invalid URI though valid URI reference",
108 "data": "abc",
109 "valid": false
110 },
111 {
112 "description": "an invalid URI with spaces",
113 "data": "http:// shouldfail.com",
114 "valid": false
115 },
116 {
117 "description": "an invalid URI with spaces and missing scheme",
118 "data": ":// should fail",
119 "valid": false
120 }
121 ]
122 },
123 {
124 "description": "validation of e-mail addresses",
125 "schema": {"format": "email"},
126 "tests": [
127 {
128 "description": "a valid e-mail address",
129 "data": "joe.bloggs@example.com",
130 "valid": true
131 },
132 {
133 "description": "an invalid e-mail address",
134 "data": "2962",
135 "valid": false
136 }
137 ]
138 },
139 {
140 "description": "validation of IP addresses",
141 "schema": {"format": "ipv4"},
142 "tests": [
143 {
144 "description": "a valid IP address",
145 "data": "192.168.0.1",
146 "valid": true
147 },
148 {
149 "description": "an IP address with too many components",
150 "data": "127.0.0.0.1",
151 "valid": false
152 },
153 {
154 "description": "an IP address with out-of-range values",
155 "data": "256.256.256.256",
156 "valid": false
157 },
158 {
159 "description": "an IP address without 4 components",
160 "data": "127.0",
161 "valid": false
162 },
163 {
164 "description": "an IP address as an integer",
165 "data": "0x7f000001",
166 "valid": false
167 }
168 ]
169 },
170 {
171 "description": "validation of IPv6 addresses",
172 "schema": {"format": "ipv6"},
173 "tests": [
174 {
175 "description": "a valid IPv6 address",
176 "data": "::1",
177 "valid": true
178 },
179 {
180 "description": "an IPv6 address with out-of-range values",
181 "data": "12345::",
182 "valid": false
183 },
184 {
185 "description": "an IPv6 address with too many components",
186 "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
187 "valid": false
188 },
189 {
190 "description": "an IPv6 address containing illegal characters",
191 "data": "::laptop",
192 "valid": false
193 }
194 ]
195 },
196 {
197 "description": "validation of host names",
198 "schema": {"format": "hostname"},
199 "tests": [
200 {
201 "description": "a valid host name",
202 "data": "www.example.com",
203 "valid": true
204 },
205 {
206 "description": "a host name starting with an illegal character",
207 "data": "-a-host-name-that-starts-with--",
208 "valid": false
209 },
210 {
211 "description": "a host name containing illegal characters",
212 "data": "not_a_valid_host_name",
213 "valid": false
214 },
215 {
216 "description": "a host name with a component too long",
217 "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
218 "valid": false
219 }
220 ]
221 }
222 ]
0 [
1 {
2 "description": "some languages do not distinguish between different types of numeric value",
3 "schema": {
4 "type": "integer"
5 },
6 "tests": [
7 {
8 "description": "a float is not an integer even without fractional part",
9 "data": 1.0,
10 "valid": false
11 }
12 ]
13 }
14 ]
0 [
1 {
2 "description": "pattern validation",
3 "schema": {"pattern": "^a*$"},
4 "tests": [
5 {
6 "description": "a matching pattern is valid",
7 "data": "aaa",
8 "valid": true
9 },
10 {
11 "description": "a non-matching pattern is invalid",
12 "data": "abc",
13 "valid": false
14 },
15 {
16 "description": "ignores non-strings",
17 "data": true,
18 "valid": true
19 }
20 ]
21 },
22 {
23 "description": "pattern is not anchored",
24 "schema": {"pattern": "a+"},
25 "tests": [
26 {
27 "description": "matches a substring",
28 "data": "xxaayy",
29 "valid": true
30 }
31 ]
32 }
33 ]
0 [
1 {
2 "description":
3 "patternProperties validates properties matching a regex",
4 "schema": {
5 "patternProperties": {
6 "f.*o": {"type": "integer"}
7 }
8 },
9 "tests": [
10 {
11 "description": "a single valid match is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "multiple valid matches is valid",
17 "data": {"foo": 1, "foooooo" : 2},
18 "valid": true
19 },
20 {
21 "description": "a single invalid match is invalid",
22 "data": {"foo": "bar", "fooooo": 2},
23 "valid": false
24 },
25 {
26 "description": "multiple invalid matches is invalid",
27 "data": {"foo": "bar", "foooooo" : "baz"},
28 "valid": false
29 },
30 {
31 "description": "ignores arrays",
32 "data": [],
33 "valid": true
34 },
35 {
36 "description": "ignores strings",
37 "data": "",
38 "valid": true
39 },
40 {
41 "description": "ignores other non-objects",
42 "data": 12,
43 "valid": true
44 }
45 ]
46 },
47 {
48 "description": "multiple simultaneous patternProperties are validated",
49 "schema": {
50 "patternProperties": {
51 "a*": {"type": "integer"},
52 "aaa*": {"maximum": 20}
53 }
54 },
55 "tests": [
56 {
57 "description": "a single valid match is valid",
58 "data": {"a": 21},
59 "valid": true
60 },
61 {
62 "description": "a simultaneous match is valid",
63 "data": {"aaaa": 18},
64 "valid": true
65 },
66 {
67 "description": "multiple matches is valid",
68 "data": {"a": 21, "aaaa": 18},
69 "valid": true
70 },
71 {
72 "description": "an invalid due to one is invalid",
73 "data": {"a": "bar"},
74 "valid": false
75 },
76 {
77 "description": "an invalid due to the other is invalid",
78 "data": {"aaaa": 31},
79 "valid": false
80 },
81 {
82 "description": "an invalid due to both is invalid",
83 "data": {"aaa": "foo", "aaaa": 31},
84 "valid": false
85 }
86 ]
87 },
88 {
89 "description": "regexes are not anchored by default and are case sensitive",
90 "schema": {
91 "patternProperties": {
92 "[0-9]{2,}": { "type": "boolean" },
93 "X_": { "type": "string" }
94 }
95 },
96 "tests": [
97 {
98 "description": "non recognized members are ignored",
99 "data": { "answer 1": "42" },
100 "valid": true
101 },
102 {
103 "description": "recognized members are accounted for",
104 "data": { "a31b": null },
105 "valid": false
106 },
107 {
108 "description": "regexes are case sensitive",
109 "data": { "a_x_3": 3 },
110 "valid": true
111 },
112 {
113 "description": "regexes are case sensitive, 2",
114 "data": { "a_X_3": 3 },
115 "valid": false
116 }
117 ]
118 }
119 ]
0 [
1 {
2 "description": "object properties validation",
3 "schema": {
4 "properties": {
5 "foo": {"type": "integer"},
6 "bar": {"type": "string"}
7 }
8 },
9 "tests": [
10 {
11 "description": "both properties present and valid is valid",
12 "data": {"foo": 1, "bar": "baz"},
13 "valid": true
14 },
15 {
16 "description": "one property invalid is invalid",
17 "data": {"foo": 1, "bar": {}},
18 "valid": false
19 },
20 {
21 "description": "both properties invalid is invalid",
22 "data": {"foo": [], "bar": {}},
23 "valid": false
24 },
25 {
26 "description": "doesn't invalidate other properties",
27 "data": {"quux": []},
28 "valid": true
29 },
30 {
31 "description": "ignores arrays",
32 "data": [],
33 "valid": true
34 },
35 {
36 "description": "ignores other non-objects",
37 "data": 12,
38 "valid": true
39 }
40 ]
41 },
42 {
43 "description":
44 "properties, patternProperties, additionalProperties interaction",
45 "schema": {
46 "properties": {
47 "foo": {"type": "array", "maxItems": 3},
48 "bar": {"type": "array"}
49 },
50 "patternProperties": {"f.o": {"minItems": 2}},
51 "additionalProperties": {"type": "integer"}
52 },
53 "tests": [
54 {
55 "description": "property validates property",
56 "data": {"foo": [1, 2]},
57 "valid": true
58 },
59 {
60 "description": "property invalidates property",
61 "data": {"foo": [1, 2, 3, 4]},
62 "valid": false
63 },
64 {
65 "description": "patternProperty invalidates property",
66 "data": {"foo": []},
67 "valid": false
68 },
69 {
70 "description": "patternProperty validates nonproperty",
71 "data": {"fxo": [1, 2]},
72 "valid": true
73 },
74 {
75 "description": "patternProperty invalidates nonproperty",
76 "data": {"fxo": []},
77 "valid": false
78 },
79 {
80 "description": "additionalProperty ignores property",
81 "data": {"bar": []},
82 "valid": true
83 },
84 {
85 "description": "additionalProperty validates others",
86 "data": {"quux": 3},
87 "valid": true
88 },
89 {
90 "description": "additionalProperty invalidates others",
91 "data": {"quux": "foo"},
92 "valid": false
93 }
94 ]
95 }
96 ]
0 [
1 {
2 "description": "root pointer ref",
3 "schema": {
4 "properties": {
5 "foo": {"$ref": "#"}
6 },
7 "additionalProperties": false
8 },
9 "tests": [
10 {
11 "description": "match",
12 "data": {"foo": false},
13 "valid": true
14 },
15 {
16 "description": "recursive match",
17 "data": {"foo": {"foo": false}},
18 "valid": true
19 },
20 {
21 "description": "mismatch",
22 "data": {"bar": false},
23 "valid": false
24 },
25 {
26 "description": "recursive mismatch",
27 "data": {"foo": {"bar": false}},
28 "valid": false
29 }
30 ]
31 },
32 {
33 "description": "relative pointer ref to object",
34 "schema": {
35 "properties": {
36 "foo": {"type": "integer"},
37 "bar": {"$ref": "#/properties/foo"}
38 }
39 },
40 "tests": [
41 {
42 "description": "match",
43 "data": {"bar": 3},
44 "valid": true
45 },
46 {
47 "description": "mismatch",
48 "data": {"bar": true},
49 "valid": false
50 }
51 ]
52 },
53 {
54 "description": "relative pointer ref to array",
55 "schema": {
56 "items": [
57 {"type": "integer"},
58 {"$ref": "#/items/0"}
59 ]
60 },
61 "tests": [
62 {
63 "description": "match array",
64 "data": [1, 2],
65 "valid": true
66 },
67 {
68 "description": "mismatch array",
69 "data": [1, "foo"],
70 "valid": false
71 }
72 ]
73 },
74 {
75 "description": "escaped pointer ref",
76 "schema": {
77 "tilda~field": {"type": "integer"},
78 "slash/field": {"type": "integer"},
79 "percent%field": {"type": "integer"},
80 "properties": {
81 "tilda": {"$ref": "#/tilda~0field"},
82 "slash": {"$ref": "#/slash~1field"},
83 "percent": {"$ref": "#/percent%25field"}
84 }
85 },
86 "tests": [
87 {
88 "description": "slash invalid",
89 "data": {"slash": "aoeu"},
90 "valid": false
91 },
92 {
93 "description": "tilda invalid",
94 "data": {"tilda": "aoeu"},
95 "valid": false
96 },
97 {
98 "description": "percent invalid",
99 "data": {"percent": "aoeu"},
100 "valid": false
101 },
102 {
103 "description": "slash valid",
104 "data": {"slash": 123},
105 "valid": true
106 },
107 {
108 "description": "tilda valid",
109 "data": {"tilda": 123},
110 "valid": true
111 },
112 {
113 "description": "percent valid",
114 "data": {"percent": 123},
115 "valid": true
116 }
117 ]
118 },
119 {
120 "description": "nested refs",
121 "schema": {
122 "definitions": {
123 "a": {"type": "integer"},
124 "b": {"$ref": "#/definitions/a"},
125 "c": {"$ref": "#/definitions/b"}
126 },
127 "$ref": "#/definitions/c"
128 },
129 "tests": [
130 {
131 "description": "nested ref valid",
132 "data": 5,
133 "valid": true
134 },
135 {
136 "description": "nested ref invalid",
137 "data": "a",
138 "valid": false
139 }
140 ]
141 },
142 {
143 "description": "ref overrides any sibling keywords",
144 "schema": {
145 "definitions": {
146 "reffed": {
147 "type": "array"
148 }
149 },
150 "properties": {
151 "foo": {
152 "$ref": "#/definitions/reffed",
153 "maxItems": 2
154 }
155 }
156 },
157 "tests": [
158 {
159 "description": "ref valid",
160 "data": { "foo": [] },
161 "valid": true
162 },
163 {
164 "description": "ref valid, maxItems ignored",
165 "data": { "foo": [ 1, 2, 3] },
166 "valid": true
167 },
168 {
169 "description": "ref invalid",
170 "data": { "foo": "string" },
171 "valid": false
172 }
173 ]
174 },
175 {
176 "description": "remote ref, containing refs itself",
177 "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
178 "tests": [
179 {
180 "description": "remote ref valid",
181 "data": {"minLength": 1},
182 "valid": true
183 },
184 {
185 "description": "remote ref invalid",
186 "data": {"minLength": -1},
187 "valid": false
188 }
189 ]
190 },
191 {
192 "description": "property named $ref that is not a reference",
193 "schema": {
194 "properties": {
195 "$ref": {"type": "string"}
196 }
197 },
198 "tests": [
199 {
200 "description": "property named $ref valid",
201 "data": {"$ref": "a"},
202 "valid": true
203 },
204 {
205 "description": "property named $ref invalid",
206 "data": {"$ref": 2},
207 "valid": false
208 }
209 ]
210 },
211 {
212 "description": "Recursive references between schemas",
213 "schema": {
214 "id": "http://localhost:1234/tree",
215 "description": "tree of nodes",
216 "type": "object",
217 "properties": {
218 "meta": {"type": "string"},
219 "nodes": {
220 "type": "array",
221 "items": {"$ref": "node"}
222 }
223 },
224 "required": ["meta", "nodes"],
225 "definitions": {
226 "node": {
227 "id": "http://localhost:1234/node",
228 "description": "node",
229 "type": "object",
230 "properties": {
231 "value": {"type": "number"},
232 "subtree": {"$ref": "tree"}
233 },
234 "required": ["value"]
235 }
236 }
237 },
238 "tests": [
239 {
240 "description": "valid tree",
241 "data": {
242 "meta": "root",
243 "nodes": [
244 {
245 "value": 1,
246 "subtree": {
247 "meta": "child",
248 "nodes": [
249 {"value": 1.1},
250 {"value": 1.2}
251 ]
252 }
253 },
254 {
255 "value": 2,
256 "subtree": {
257 "meta": "child",
258 "nodes": [
259 {"value": 2.1},
260 {"value": 2.2}
261 ]
262 }
263 }
264 ]
265 },
266 "valid": true
267 },
268 {
269 "description": "invalid tree",
270 "data": {
271 "meta": "root",
272 "nodes": [
273 {
274 "value": 1,
275 "subtree": {
276 "meta": "child",
277 "nodes": [
278 {"value": "string is invalid"},
279 {"value": 1.2}
280 ]
281 }
282 },
283 {
284 "value": 2,
285 "subtree": {
286 "meta": "child",
287 "nodes": [
288 {"value": 2.1},
289 {"value": 2.2}
290 ]
291 }
292 }
293 ]
294 },
295 "valid": false
296 }
297 ]
298 }
299 ]
0 [
1 {
2 "description": "remote ref",
3 "schema": {"$ref": "http://localhost:1234/integer.json"},
4 "tests": [
5 {
6 "description": "remote ref valid",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "remote ref invalid",
12 "data": "a",
13 "valid": false
14 }
15 ]
16 },
17 {
18 "description": "fragment within remote ref",
19 "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
20 "tests": [
21 {
22 "description": "remote fragment valid",
23 "data": 1,
24 "valid": true
25 },
26 {
27 "description": "remote fragment invalid",
28 "data": "a",
29 "valid": false
30 }
31 ]
32 },
33 {
34 "description": "ref within remote ref",
35 "schema": {
36 "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
37 },
38 "tests": [
39 {
40 "description": "ref within ref valid",
41 "data": 1,
42 "valid": true
43 },
44 {
45 "description": "ref within ref invalid",
46 "data": "a",
47 "valid": false
48 }
49 ]
50 },
51 {
52 "description": "base URI change",
53 "schema": {
54 "id": "http://localhost:1234/",
55 "items": {
56 "id": "folder/",
57 "items": {"$ref": "folderInteger.json"}
58 }
59 },
60 "tests": [
61 {
62 "description": "base URI change ref valid",
63 "data": [[1]],
64 "valid": true
65 },
66 {
67 "description": "base URI change ref invalid",
68 "data": [["a"]],
69 "valid": false
70 }
71 ]
72 },
73 {
74 "description": "base URI change - change folder",
75 "schema": {
76 "id": "http://localhost:1234/scope_change_defs1.json",
77 "type" : "object",
78 "properties": {
79 "list": {"$ref": "#/definitions/baz"}
80 },
81 "definitions": {
82 "baz": {
83 "id": "folder/",
84 "type": "array",
85 "items": {"$ref": "folderInteger.json"}
86 }
87 }
88 },
89 "tests": [
90 {
91 "description": "number is valid",
92 "data": {"list": [1]},
93 "valid": true
94 },
95 {
96 "description": "string is invalid",
97 "data": {"list": ["a"]},
98 "valid": false
99 }
100 ]
101 },
102 {
103 "description": "base URI change - change folder in subschema",
104 "schema": {
105 "id": "http://localhost:1234/scope_change_defs2.json",
106 "type" : "object",
107 "properties": {
108 "list": {"$ref": "#/definitions/baz/definitions/bar"}
109 },
110 "definitions": {
111 "baz": {
112 "id": "folder/",
113 "definitions": {
114 "bar": {
115 "type": "array",
116 "items": {"$ref": "folderInteger.json"}
117 }
118 }
119 }
120 }
121 },
122 "tests": [
123 {
124 "description": "number is valid",
125 "data": {"list": [1]},
126 "valid": true
127 },
128 {
129 "description": "string is invalid",
130 "data": {"list": ["a"]},
131 "valid": false
132 }
133 ]
134 },
135 {
136 "description": "root ref in remote ref",
137 "schema": {
138 "id": "http://localhost:1234/object",
139 "type": "object",
140 "properties": {
141 "name": {"$ref": "name.json#/definitions/orNull"}
142 }
143 },
144 "tests": [
145 {
146 "description": "string is valid",
147 "data": {
148 "name": "foo"
149 },
150 "valid": true
151 },
152 {
153 "description": "null is valid",
154 "data": {
155 "name": null
156 },
157 "valid": true
158 },
159 {
160 "description": "object is invalid",
161 "data": {
162 "name": {
163 "name": null
164 }
165 },
166 "valid": false
167 }
168 ]
169 }
170 ]
0 [
1 {
2 "description": "required validation",
3 "schema": {
4 "properties": {
5 "foo": {},
6 "bar": {}
7 },
8 "required": ["foo"]
9 },
10 "tests": [
11 {
12 "description": "present required property is valid",
13 "data": {"foo": 1},
14 "valid": true
15 },
16 {
17 "description": "non-present required property is invalid",
18 "data": {"bar": 1},
19 "valid": false
20 },
21 {
22 "description": "ignores arrays",
23 "data": [],
24 "valid": true
25 },
26 {
27 "description": "ignores strings",
28 "data": "",
29 "valid": true
30 },
31 {
32 "description": "ignores other non-objects",
33 "data": 12,
34 "valid": true
35 }
36 ]
37 },
38 {
39 "description": "required default validation",
40 "schema": {
41 "properties": {
42 "foo": {}
43 }
44 },
45 "tests": [
46 {
47 "description": "not required by default",
48 "data": {},
49 "valid": true
50 }
51 ]
52 }
53 ]
0 [
1 {
2 "description": "integer type matches integers",
3 "schema": {"type": "integer"},
4 "tests": [
5 {
6 "description": "an integer is an integer",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "a float is not an integer",
12 "data": 1.1,
13 "valid": false
14 },
15 {
16 "description": "a string is not an integer",
17 "data": "foo",
18 "valid": false
19 },
20 {
21 "description": "a string is still not an integer, even if it looks like one",
22 "data": "1",
23 "valid": false
24 },
25 {
26 "description": "an object is not an integer",
27 "data": {},
28 "valid": false
29 },
30 {
31 "description": "an array is not an integer",
32 "data": [],
33 "valid": false
34 },
35 {
36 "description": "a boolean is not an integer",
37 "data": true,
38 "valid": false
39 },
40 {
41 "description": "null is not an integer",
42 "data": null,
43 "valid": false
44 }
45 ]
46 },
47 {
48 "description": "number type matches numbers",
49 "schema": {"type": "number"},
50 "tests": [
51 {
52 "description": "an integer is a number",
53 "data": 1,
54 "valid": true
55 },
56 {
57 "description": "a float is a number",
58 "data": 1.1,
59 "valid": true
60 },
61 {
62 "description": "a string is not a number",
63 "data": "foo",
64 "valid": false
65 },
66 {
67 "description": "a string is still not a number, even if it looks like one",
68 "data": "1",
69 "valid": false
70 },
71 {
72 "description": "an object is not a number",
73 "data": {},
74 "valid": false
75 },
76 {
77 "description": "an array is not a number",
78 "data": [],
79 "valid": false
80 },
81 {
82 "description": "a boolean is not a number",
83 "data": true,
84 "valid": false
85 },
86 {
87 "description": "null is not a number",
88 "data": null,
89 "valid": false
90 }
91 ]
92 },
93 {
94 "description": "string type matches strings",
95 "schema": {"type": "string"},
96 "tests": [
97 {
98 "description": "1 is not a string",
99 "data": 1,
100 "valid": false
101 },
102 {
103 "description": "a float is not a string",
104 "data": 1.1,
105 "valid": false
106 },
107 {
108 "description": "a string is a string",
109 "data": "foo",
110 "valid": true
111 },
112 {
113 "description": "a string is still a string, even if it looks like a number",
114 "data": "1",
115 "valid": true
116 },
117 {
118 "description": "an object is not a string",
119 "data": {},
120 "valid": false
121 },
122 {
123 "description": "an array is not a string",
124 "data": [],
125 "valid": false
126 },
127 {
128 "description": "a boolean is not a string",
129 "data": true,
130 "valid": false
131 },
132 {
133 "description": "null is not a string",
134 "data": null,
135 "valid": false
136 }
137 ]
138 },
139 {
140 "description": "object type matches objects",
141 "schema": {"type": "object"},
142 "tests": [
143 {
144 "description": "an integer is not an object",
145 "data": 1,
146 "valid": false
147 },
148 {
149 "description": "a float is not an object",
150 "data": 1.1,
151 "valid": false
152 },
153 {
154 "description": "a string is not an object",
155 "data": "foo",
156 "valid": false
157 },
158 {
159 "description": "an object is an object",
160 "data": {},
161 "valid": true
162 },
163 {
164 "description": "an array is not an object",
165 "data": [],
166 "valid": false
167 },
168 {
169 "description": "a boolean is not an object",
170 "data": true,
171 "valid": false
172 },
173 {
174 "description": "null is not an object",
175 "data": null,
176 "valid": false
177 }
178 ]
179 },
180 {
181 "description": "array type matches arrays",
182 "schema": {"type": "array"},
183 "tests": [
184 {
185 "description": "an integer is not an array",
186 "data": 1,
187 "valid": false
188 },
189 {
190 "description": "a float is not an array",
191 "data": 1.1,
192 "valid": false
193 },
194 {
195 "description": "a string is not an array",
196 "data": "foo",
197 "valid": false
198 },
199 {
200 "description": "an object is not an array",
201 "data": {},
202 "valid": false
203 },
204 {
205 "description": "an array is an array",
206 "data": [],
207 "valid": true
208 },
209 {
210 "description": "a boolean is not an array",
211 "data": true,
212 "valid": false
213 },
214 {
215 "description": "null is not an array",
216 "data": null,
217 "valid": false
218 }
219 ]
220 },
221 {
222 "description": "boolean type matches booleans",
223 "schema": {"type": "boolean"},
224 "tests": [
225 {
226 "description": "an integer is not a boolean",
227 "data": 1,
228 "valid": false
229 },
230 {
231 "description": "a float is not a boolean",
232 "data": 1.1,
233 "valid": false
234 },
235 {
236 "description": "a string is not a boolean",
237 "data": "foo",
238 "valid": false
239 },
240 {
241 "description": "an object is not a boolean",
242 "data": {},
243 "valid": false
244 },
245 {
246 "description": "an array is not a boolean",
247 "data": [],
248 "valid": false
249 },
250 {
251 "description": "a boolean is a boolean",
252 "data": true,
253 "valid": true
254 },
255 {
256 "description": "null is not a boolean",
257 "data": null,
258 "valid": false
259 }
260 ]
261 },
262 {
263 "description": "null type matches only the null object",
264 "schema": {"type": "null"},
265 "tests": [
266 {
267 "description": "an integer is not null",
268 "data": 1,
269 "valid": false
270 },
271 {
272 "description": "a float is not null",
273 "data": 1.1,
274 "valid": false
275 },
276 {
277 "description": "a string is not null",
278 "data": "foo",
279 "valid": false
280 },
281 {
282 "description": "an object is not null",
283 "data": {},
284 "valid": false
285 },
286 {
287 "description": "an array is not null",
288 "data": [],
289 "valid": false
290 },
291 {
292 "description": "a boolean is not null",
293 "data": true,
294 "valid": false
295 },
296 {
297 "description": "null is null",
298 "data": null,
299 "valid": true
300 }
301 ]
302 },
303 {
304 "description": "multiple types can be specified in an array",
305 "schema": {"type": ["integer", "string"]},
306 "tests": [
307 {
308 "description": "an integer is valid",
309 "data": 1,
310 "valid": true
311 },
312 {
313 "description": "a string is valid",
314 "data": "foo",
315 "valid": true
316 },
317 {
318 "description": "a float is invalid",
319 "data": 1.1,
320 "valid": false
321 },
322 {
323 "description": "an object is invalid",
324 "data": {},
325 "valid": false
326 },
327 {
328 "description": "an array is invalid",
329 "data": [],
330 "valid": false
331 },
332 {
333 "description": "a boolean is invalid",
334 "data": true,
335 "valid": false
336 },
337 {
338 "description": "null is invalid",
339 "data": null,
340 "valid": false
341 }
342 ]
343 }
344 ]
0 [
1 {
2 "description": "uniqueItems validation",
3 "schema": {"uniqueItems": true},
4 "tests": [
5 {
6 "description": "unique array of integers is valid",
7 "data": [1, 2],
8 "valid": true
9 },
10 {
11 "description": "non-unique array of integers is invalid",
12 "data": [1, 1],
13 "valid": false
14 },
15 {
16 "description": "numbers are unique if mathematically unequal",
17 "data": [1.0, 1.00, 1],
18 "valid": false
19 },
20 {
21 "description": "unique array of objects is valid",
22 "data": [{"foo": "bar"}, {"foo": "baz"}],
23 "valid": true
24 },
25 {
26 "description": "non-unique array of objects is invalid",
27 "data": [{"foo": "bar"}, {"foo": "bar"}],
28 "valid": false
29 },
30 {
31 "description": "unique array of nested objects is valid",
32 "data": [
33 {"foo": {"bar" : {"baz" : true}}},
34 {"foo": {"bar" : {"baz" : false}}}
35 ],
36 "valid": true
37 },
38 {
39 "description": "non-unique array of nested objects is invalid",
40 "data": [
41 {"foo": {"bar" : {"baz" : true}}},
42 {"foo": {"bar" : {"baz" : true}}}
43 ],
44 "valid": false
45 },
46 {
47 "description": "unique array of arrays is valid",
48 "data": [["foo"], ["bar"]],
49 "valid": true
50 },
51 {
52 "description": "non-unique array of arrays is invalid",
53 "data": [["foo"], ["foo"]],
54 "valid": false
55 },
56 {
57 "description": "1 and true are unique",
58 "data": [1, true],
59 "valid": true
60 },
61 {
62 "description": "0 and false are unique",
63 "data": [0, false],
64 "valid": true
65 },
66 {
67 "description": "unique heterogeneous types are valid",
68 "data": [{}, [1], true, null, 1],
69 "valid": true
70 },
71 {
72 "description": "non-unique heterogeneous types are invalid",
73 "data": [{}, [1], true, null, {}, 1],
74 "valid": false
75 }
76 ]
77 }
78 ]
0 [
1 {
2 "description": "additionalItems as schema",
3 "schema": {
4 "items": [{}],
5 "additionalItems": {"type": "integer"}
6 },
7 "tests": [
8 {
9 "description": "additional items match schema",
10 "data": [ null, 2, 3, 4 ],
11 "valid": true
12 },
13 {
14 "description": "additional items do not match schema",
15 "data": [ null, 2, 3, "foo" ],
16 "valid": false
17 }
18 ]
19 },
20 {
21 "description": "items is schema, no additionalItems",
22 "schema": {
23 "items": {},
24 "additionalItems": false
25 },
26 "tests": [
27 {
28 "description": "all items match schema",
29 "data": [ 1, 2, 3, 4, 5 ],
30 "valid": true
31 }
32 ]
33 },
34 {
35 "description": "array of items with no additionalItems",
36 "schema": {
37 "items": [{}, {}, {}],
38 "additionalItems": false
39 },
40 "tests": [
41 {
42 "description": "fewer number of items present",
43 "data": [ 1, 2 ],
44 "valid": true
45 },
46 {
47 "description": "equal number of items present",
48 "data": [ 1, 2, 3 ],
49 "valid": true
50 },
51 {
52 "description": "additional items are not permitted",
53 "data": [ 1, 2, 3, 4 ],
54 "valid": false
55 }
56 ]
57 },
58 {
59 "description": "additionalItems as false without items",
60 "schema": {"additionalItems": false},
61 "tests": [
62 {
63 "description":
64 "items defaults to empty schema so everything is valid",
65 "data": [ 1, 2, 3, 4, 5 ],
66 "valid": true
67 },
68 {
69 "description": "ignores non-arrays",
70 "data": {"foo" : "bar"},
71 "valid": true
72 }
73 ]
74 },
75 {
76 "description": "additionalItems are allowed by default",
77 "schema": {"items": [{"type": "integer"}]},
78 "tests": [
79 {
80 "description": "only the first item is validated",
81 "data": [1, "foo", false],
82 "valid": true
83 }
84 ]
85 }
86 ]
0 [
1 {
2 "description":
3 "additionalProperties being false does not allow other properties",
4 "schema": {
5 "properties": {"foo": {}, "bar": {}},
6 "patternProperties": { "^v": {} },
7 "additionalProperties": false
8 },
9 "tests": [
10 {
11 "description": "no additional properties is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "an additional property is invalid",
17 "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [1, 2, 3],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "foobarbaz",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 },
35 {
36 "description": "patternProperties are not additional properties",
37 "data": {"foo":1, "vroom": 2},
38 "valid": true
39 }
40 ]
41 },
42 {
43 "description":
44 "additionalProperties allows a schema which should validate",
45 "schema": {
46 "properties": {"foo": {}, "bar": {}},
47 "additionalProperties": {"type": "boolean"}
48 },
49 "tests": [
50 {
51 "description": "no additional properties is valid",
52 "data": {"foo": 1},
53 "valid": true
54 },
55 {
56 "description": "an additional valid property is valid",
57 "data": {"foo" : 1, "bar" : 2, "quux" : true},
58 "valid": true
59 },
60 {
61 "description": "an additional invalid property is invalid",
62 "data": {"foo" : 1, "bar" : 2, "quux" : 12},
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description":
69 "additionalProperties can exist by itself",
70 "schema": {
71 "additionalProperties": {"type": "boolean"}
72 },
73 "tests": [
74 {
75 "description": "an additional valid property is valid",
76 "data": {"foo" : true},
77 "valid": true
78 },
79 {
80 "description": "an additional invalid property is invalid",
81 "data": {"foo" : 1},
82 "valid": false
83 }
84 ]
85 },
86 {
87 "description": "additionalProperties are allowed by default",
88 "schema": {"properties": {"foo": {}, "bar": {}}},
89 "tests": [
90 {
91 "description": "additional properties are allowed",
92 "data": {"foo": 1, "bar": 2, "quux": true},
93 "valid": true
94 }
95 ]
96 }
97 ]
0 [
1 {
2 "description": "allOf",
3 "schema": {
4 "allOf": [
5 {
6 "properties": {
7 "bar": {"type": "integer"}
8 },
9 "required": ["bar"]
10 },
11 {
12 "properties": {
13 "foo": {"type": "string"}
14 },
15 "required": ["foo"]
16 }
17 ]
18 },
19 "tests": [
20 {
21 "description": "allOf",
22 "data": {"foo": "baz", "bar": 2},
23 "valid": true
24 },
25 {
26 "description": "mismatch second",
27 "data": {"foo": "baz"},
28 "valid": false
29 },
30 {
31 "description": "mismatch first",
32 "data": {"bar": 2},
33 "valid": false
34 },
35 {
36 "description": "wrong type",
37 "data": {"foo": "baz", "bar": "quux"},
38 "valid": false
39 }
40 ]
41 },
42 {
43 "description": "allOf with base schema",
44 "schema": {
45 "properties": {"bar": {"type": "integer"}},
46 "required": ["bar"],
47 "allOf" : [
48 {
49 "properties": {
50 "foo": {"type": "string"}
51 },
52 "required": ["foo"]
53 },
54 {
55 "properties": {
56 "baz": {"type": "null"}
57 },
58 "required": ["baz"]
59 }
60 ]
61 },
62 "tests": [
63 {
64 "description": "valid",
65 "data": {"foo": "quux", "bar": 2, "baz": null},
66 "valid": true
67 },
68 {
69 "description": "mismatch base schema",
70 "data": {"foo": "quux", "baz": null},
71 "valid": false
72 },
73 {
74 "description": "mismatch first allOf",
75 "data": {"bar": 2, "baz": null},
76 "valid": false
77 },
78 {
79 "description": "mismatch second allOf",
80 "data": {"foo": "quux", "bar": 2},
81 "valid": false
82 },
83 {
84 "description": "mismatch both",
85 "data": {"bar": 2},
86 "valid": false
87 }
88 ]
89 },
90 {
91 "description": "allOf simple types",
92 "schema": {
93 "allOf": [
94 {"maximum": 30},
95 {"minimum": 20}
96 ]
97 },
98 "tests": [
99 {
100 "description": "valid",
101 "data": 25,
102 "valid": true
103 },
104 {
105 "description": "mismatch one",
106 "data": 35,
107 "valid": false
108 }
109 ]
110 },
111 {
112 "description": "allOf with boolean schemas, all true",
113 "schema": {"allOf": [true, true]},
114 "tests": [
115 {
116 "description": "any value is valid",
117 "data": "foo",
118 "valid": true
119 }
120 ]
121 },
122 {
123 "description": "allOf with boolean schemas, some false",
124 "schema": {"allOf": [true, false]},
125 "tests": [
126 {
127 "description": "any value is invalid",
128 "data": "foo",
129 "valid": false
130 }
131 ]
132 },
133 {
134 "description": "allOf with boolean schemas, all false",
135 "schema": {"allOf": [false, false]},
136 "tests": [
137 {
138 "description": "any value is invalid",
139 "data": "foo",
140 "valid": false
141 }
142 ]
143 }
144 ]
0 [
1 {
2 "description": "anyOf",
3 "schema": {
4 "anyOf": [
5 {
6 "type": "integer"
7 },
8 {
9 "minimum": 2
10 }
11 ]
12 },
13 "tests": [
14 {
15 "description": "first anyOf valid",
16 "data": 1,
17 "valid": true
18 },
19 {
20 "description": "second anyOf valid",
21 "data": 2.5,
22 "valid": true
23 },
24 {
25 "description": "both anyOf valid",
26 "data": 3,
27 "valid": true
28 },
29 {
30 "description": "neither anyOf valid",
31 "data": 1.5,
32 "valid": false
33 }
34 ]
35 },
36 {
37 "description": "anyOf with base schema",
38 "schema": {
39 "type": "string",
40 "anyOf" : [
41 {
42 "maxLength": 2
43 },
44 {
45 "minLength": 4
46 }
47 ]
48 },
49 "tests": [
50 {
51 "description": "mismatch base schema",
52 "data": 3,
53 "valid": false
54 },
55 {
56 "description": "one anyOf valid",
57 "data": "foobar",
58 "valid": true
59 },
60 {
61 "description": "both anyOf invalid",
62 "data": "foo",
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description": "anyOf with boolean schemas, all true",
69 "schema": {"anyOf": [true, true]},
70 "tests": [
71 {
72 "description": "any value is valid",
73 "data": "foo",
74 "valid": true
75 }
76 ]
77 },
78 {
79 "description": "anyOf with boolean schemas, some true",
80 "schema": {"anyOf": [true, false]},
81 "tests": [
82 {
83 "description": "any value is valid",
84 "data": "foo",
85 "valid": true
86 }
87 ]
88 },
89 {
90 "description": "anyOf with boolean schemas, all false",
91 "schema": {"anyOf": [false, false]},
92 "tests": [
93 {
94 "description": "any value is invalid",
95 "data": "foo",
96 "valid": false
97 }
98 ]
99 },
100 {
101 "description": "anyOf complex types",
102 "schema": {
103 "anyOf": [
104 {
105 "properties": {
106 "bar": {"type": "integer"}
107 },
108 "required": ["bar"]
109 },
110 {
111 "properties": {
112 "foo": {"type": "string"}
113 },
114 "required": ["foo"]
115 }
116 ]
117 },
118 "tests": [
119 {
120 "description": "first anyOf valid (complex)",
121 "data": {"bar": 2},
122 "valid": true
123 },
124 {
125 "description": "second anyOf valid (complex)",
126 "data": {"foo": "baz"},
127 "valid": true
128 },
129 {
130 "description": "both anyOf valid (complex)",
131 "data": {"foo": "baz", "bar": 2},
132 "valid": true
133 },
134 {
135 "description": "neither anyOf valid (complex)",
136 "data": {"foo": 2, "bar": "quux"},
137 "valid": false
138 }
139 ]
140 }
141 ]
0 [
1 {
2 "description": "boolean schema 'true'",
3 "schema": true,
4 "tests": [
5 {
6 "description": "number is valid",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "string is valid",
12 "data": "foo",
13 "valid": true
14 },
15 {
16 "description": "boolean true is valid",
17 "data": true,
18 "valid": true
19 },
20 {
21 "description": "boolean false is valid",
22 "data": false,
23 "valid": true
24 },
25 {
26 "description": "null is valid",
27 "data": null,
28 "valid": true
29 },
30 {
31 "description": "object is valid",
32 "data": {"foo": "bar"},
33 "valid": true
34 },
35 {
36 "description": "empty object is valid",
37 "data": {},
38 "valid": true
39 },
40 {
41 "description": "array is valid",
42 "data": ["foo"],
43 "valid": true
44 },
45 {
46 "description": "empty array is valid",
47 "data": [],
48 "valid": true
49 }
50 ]
51 },
52 {
53 "description": "boolean schema 'false'",
54 "schema": false,
55 "tests": [
56 {
57 "description": "number is invalid",
58 "data": 1,
59 "valid": false
60 },
61 {
62 "description": "string is invalid",
63 "data": "foo",
64 "valid": false
65 },
66 {
67 "description": "boolean true is invalid",
68 "data": true,
69 "valid": false
70 },
71 {
72 "description": "boolean false is invalid",
73 "data": false,
74 "valid": false
75 },
76 {
77 "description": "null is invalid",
78 "data": null,
79 "valid": false
80 },
81 {
82 "description": "object is invalid",
83 "data": {"foo": "bar"},
84 "valid": false
85 },
86 {
87 "description": "empty object is invalid",
88 "data": {},
89 "valid": false
90 },
91 {
92 "description": "array is invalid",
93 "data": ["foo"],
94 "valid": false
95 },
96 {
97 "description": "empty array is invalid",
98 "data": [],
99 "valid": false
100 }
101 ]
102 }
103 ]
0 [
1 {
2 "description": "const validation",
3 "schema": {"const": 2},
4 "tests": [
5 {
6 "description": "same value is valid",
7 "data": 2,
8 "valid": true
9 },
10 {
11 "description": "another value is invalid",
12 "data": 5,
13 "valid": false
14 },
15 {
16 "description": "another type is invalid",
17 "data": "a",
18 "valid": false
19 }
20 ]
21 },
22 {
23 "description": "const with object",
24 "schema": {"const": {"foo": "bar", "baz": "bax"}},
25 "tests": [
26 {
27 "description": "same object is valid",
28 "data": {"foo": "bar", "baz": "bax"},
29 "valid": true
30 },
31 {
32 "description": "same object with different property order is valid",
33 "data": {"baz": "bax", "foo": "bar"},
34 "valid": true
35 },
36 {
37 "description": "another object is invalid",
38 "data": {"foo": "bar"},
39 "valid": false
40 },
41 {
42 "description": "another type is invalid",
43 "data": [1, 2],
44 "valid": false
45 }
46 ]
47 },
48 {
49 "description": "const with array",
50 "schema": {"const": [{ "foo": "bar" }]},
51 "tests": [
52 {
53 "description": "same array is valid",
54 "data": [{"foo": "bar"}],
55 "valid": true
56 },
57 {
58 "description": "another array item is invalid",
59 "data": [2],
60 "valid": false
61 },
62 {
63 "description": "array with additional items is invalid",
64 "data": [1, 2, 3],
65 "valid": false
66 }
67 ]
68 },
69 {
70 "description": "const with null",
71 "schema": {"const": null},
72 "tests": [
73 {
74 "description": "null is valid",
75 "data": null,
76 "valid": true
77 },
78 {
79 "description": "not null is invalid",
80 "data": 0,
81 "valid": false
82 }
83 ]
84 }
85 ]
0 [
1 {
2 "description": "contains keyword validation",
3 "schema": {
4 "contains": {"minimum": 5}
5 },
6 "tests": [
7 {
8 "description": "array with item matching schema (5) is valid",
9 "data": [3, 4, 5],
10 "valid": true
11 },
12 {
13 "description": "array with item matching schema (6) is valid",
14 "data": [3, 4, 6],
15 "valid": true
16 },
17 {
18 "description": "array with two items matching schema (5, 6) is valid",
19 "data": [3, 4, 5, 6],
20 "valid": true
21 },
22 {
23 "description": "array without items matching schema is invalid",
24 "data": [2, 3, 4],
25 "valid": false
26 },
27 {
28 "description": "empty array is invalid",
29 "data": [],
30 "valid": false
31 },
32 {
33 "description": "not array is valid",
34 "data": {},
35 "valid": true
36 }
37 ]
38 },
39 {
40 "description": "contains keyword with const keyword",
41 "schema": {
42 "contains": { "const": 5 }
43 },
44 "tests": [
45 {
46 "description": "array with item 5 is valid",
47 "data": [3, 4, 5],
48 "valid": true
49 },
50 {
51 "description": "array with two items 5 is valid",
52 "data": [3, 4, 5, 5],
53 "valid": true
54 },
55 {
56 "description": "array without item 5 is invalid",
57 "data": [1, 2, 3, 4],
58 "valid": false
59 }
60 ]
61 },
62 {
63 "description": "contains keyword with boolean schema true",
64 "schema": {"contains": true},
65 "tests": [
66 {
67 "description": "any non-empty array is valid",
68 "data": ["foo"],
69 "valid": true
70 },
71 {
72 "description": "empty array is invalid",
73 "data": [],
74 "valid": false
75 }
76 ]
77 },
78 {
79 "description": "contains keyword with boolean schema false",
80 "schema": {"contains": false},
81 "tests": [
82 {
83 "description": "any non-empty array is invalid",
84 "data": ["foo"],
85 "valid": false
86 },
87 {
88 "description": "empty array is invalid",
89 "data": [],
90 "valid": false
91 }
92 ]
93 }
94 ]
0 [
1 {
2 "description": "invalid type for default",
3 "schema": {
4 "properties": {
5 "foo": {
6 "type": "integer",
7 "default": []
8 }
9 }
10 },
11 "tests": [
12 {
13 "description": "valid when property is specified",
14 "data": {"foo": 13},
15 "valid": true
16 },
17 {
18 "description": "still valid when the invalid default is used",
19 "data": {},
20 "valid": true
21 }
22 ]
23 },
24 {
25 "description": "invalid string value for default",
26 "schema": {
27 "properties": {
28 "bar": {
29 "type": "string",
30 "minLength": 4,
31 "default": "bad"
32 }
33 }
34 },
35 "tests": [
36 {
37 "description": "valid when property is specified",
38 "data": {"bar": "good"},
39 "valid": true
40 },
41 {
42 "description": "still valid when the invalid default is used",
43 "data": {},
44 "valid": true
45 }
46 ]
47 }
48 ]
0 [
1 {
2 "description": "valid definition",
3 "schema": {"$ref": "http://json-schema.org/draft-06/schema#"},
4 "tests": [
5 {
6 "description": "valid definition schema",
7 "data": {
8 "definitions": {
9 "foo": {"type": "integer"}
10 }
11 },
12 "valid": true
13 }
14 ]
15 },
16 {
17 "description": "invalid definition",
18 "schema": {"$ref": "http://json-schema.org/draft-06/schema#"},
19 "tests": [
20 {
21 "description": "invalid definition schema",
22 "data": {
23 "definitions": {
24 "foo": {"type": 1}
25 }
26 },
27 "valid": false
28 }
29 ]
30 }
31 ]
0 [
1 {
2 "description": "dependencies",
3 "schema": {
4 "dependencies": {"bar": ["foo"]}
5 },
6 "tests": [
7 {
8 "description": "neither",
9 "data": {},
10 "valid": true
11 },
12 {
13 "description": "nondependant",
14 "data": {"foo": 1},
15 "valid": true
16 },
17 {
18 "description": "with dependency",
19 "data": {"foo": 1, "bar": 2},
20 "valid": true
21 },
22 {
23 "description": "missing dependency",
24 "data": {"bar": 2},
25 "valid": false
26 },
27 {
28 "description": "ignores arrays",
29 "data": ["bar"],
30 "valid": true
31 },
32 {
33 "description": "ignores strings",
34 "data": "foobar",
35 "valid": true
36 },
37 {
38 "description": "ignores other non-objects",
39 "data": 12,
40 "valid": true
41 }
42 ]
43 },
44 {
45 "description": "dependencies with empty array",
46 "schema": {
47 "dependencies": {"bar": []}
48 },
49 "tests": [
50 {
51 "description": "empty object",
52 "data": {},
53 "valid": true
54 },
55 {
56 "description": "object with one property",
57 "data": {"bar": 2},
58 "valid": true
59 }
60 ]
61 },
62 {
63 "description": "multiple dependencies",
64 "schema": {
65 "dependencies": {"quux": ["foo", "bar"]}
66 },
67 "tests": [
68 {
69 "description": "neither",
70 "data": {},
71 "valid": true
72 },
73 {
74 "description": "nondependants",
75 "data": {"foo": 1, "bar": 2},
76 "valid": true
77 },
78 {
79 "description": "with dependencies",
80 "data": {"foo": 1, "bar": 2, "quux": 3},
81 "valid": true
82 },
83 {
84 "description": "missing dependency",
85 "data": {"foo": 1, "quux": 2},
86 "valid": false
87 },
88 {
89 "description": "missing other dependency",
90 "data": {"bar": 1, "quux": 2},
91 "valid": false
92 },
93 {
94 "description": "missing both dependencies",
95 "data": {"quux": 1},
96 "valid": false
97 }
98 ]
99 },
100 {
101 "description": "multiple dependencies subschema",
102 "schema": {
103 "dependencies": {
104 "bar": {
105 "properties": {
106 "foo": {"type": "integer"},
107 "bar": {"type": "integer"}
108 }
109 }
110 }
111 },
112 "tests": [
113 {
114 "description": "valid",
115 "data": {"foo": 1, "bar": 2},
116 "valid": true
117 },
118 {
119 "description": "no dependency",
120 "data": {"foo": "quux"},
121 "valid": true
122 },
123 {
124 "description": "wrong type",
125 "data": {"foo": "quux", "bar": 2},
126 "valid": false
127 },
128 {
129 "description": "wrong type other",
130 "data": {"foo": 2, "bar": "quux"},
131 "valid": false
132 },
133 {
134 "description": "wrong type both",
135 "data": {"foo": "quux", "bar": "quux"},
136 "valid": false
137 }
138 ]
139 },
140 {
141 "description": "dependencies with boolean subschemas",
142 "schema": {
143 "dependencies": {
144 "foo": true,
145 "bar": false
146 }
147 },
148 "tests": [
149 {
150 "description": "object with property having schema true is valid",
151 "data": {"foo": 1},
152 "valid": true
153 },
154 {
155 "description": "object with property having schema false is invalid",
156 "data": {"bar": 2},
157 "valid": false
158 },
159 {
160 "description": "object with both properties is invalid",
161 "data": {"foo": 1, "bar": 2},
162 "valid": false
163 },
164 {
165 "description": "empty object is valid",
166 "data": {},
167 "valid": true
168 }
169 ]
170 }
171 ]
0 [
1 {
2 "description": "simple enum validation",
3 "schema": {"enum": [1, 2, 3]},
4 "tests": [
5 {
6 "description": "one of the enum is valid",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "something else is invalid",
12 "data": 4,
13 "valid": false
14 }
15 ]
16 },
17 {
18 "description": "heterogeneous enum validation",
19 "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
20 "tests": [
21 {
22 "description": "one of the enum is valid",
23 "data": [],
24 "valid": true
25 },
26 {
27 "description": "something else is invalid",
28 "data": null,
29 "valid": false
30 },
31 {
32 "description": "objects are deep compared",
33 "data": {"foo": false},
34 "valid": false
35 }
36 ]
37 },
38 {
39 "description": "enums in properties",
40 "schema": {
41 "type":"object",
42 "properties": {
43 "foo": {"enum":["foo"]},
44 "bar": {"enum":["bar"]}
45 },
46 "required": ["bar"]
47 },
48 "tests": [
49 {
50 "description": "both properties are valid",
51 "data": {"foo":"foo", "bar":"bar"},
52 "valid": true
53 },
54 {
55 "description": "missing optional property is valid",
56 "data": {"bar":"bar"},
57 "valid": true
58 },
59 {
60 "description": "missing required property is invalid",
61 "data": {"foo":"foo"},
62 "valid": false
63 },
64 {
65 "description": "missing all properties is invalid",
66 "data": {},
67 "valid": false
68 }
69 ]
70 }
71 ]
0 [
1 {
2 "description": "exclusiveMaximum validation",
3 "schema": {
4 "exclusiveMaximum": 3.0
5 },
6 "tests": [
7 {
8 "description": "below the exclusiveMaximum is valid",
9 "data": 2.2,
10 "valid": true
11 },
12 {
13 "description": "boundary point is invalid",
14 "data": 3.0,
15 "valid": false
16 },
17 {
18 "description": "above the exclusiveMaximum is invalid",
19 "data": 3.5,
20 "valid": false
21 },
22 {
23 "description": "ignores non-numbers",
24 "data": "x",
25 "valid": true
26 }
27 ]
28 }
29 ]
0 [
1 {
2 "description": "exclusiveMinimum validation",
3 "schema": {
4 "exclusiveMinimum": 1.1
5 },
6 "tests": [
7 {
8 "description": "above the exclusiveMinimum is valid",
9 "data": 1.2,
10 "valid": true
11 },
12 {
13 "description": "boundary point is invalid",
14 "data": 1.1,
15 "valid": false
16 },
17 {
18 "description": "below the exclusiveMinimum is invalid",
19 "data": 0.6,
20 "valid": false
21 },
22 {
23 "description": "ignores non-numbers",
24 "data": "x",
25 "valid": true
26 }
27 ]
28 }
29 ]
0 [
1 {
2 "description": "a schema given for items",
3 "schema": {
4 "items": {"type": "integer"}
5 },
6 "tests": [
7 {
8 "description": "valid items",
9 "data": [ 1, 2, 3 ],
10 "valid": true
11 },
12 {
13 "description": "wrong type of items",
14 "data": [1, "x"],
15 "valid": false
16 },
17 {
18 "description": "ignores non-arrays",
19 "data": {"foo" : "bar"},
20 "valid": true
21 },
22 {
23 "description": "JavaScript pseudo-array is valid",
24 "data": {
25 "0": "invalid",
26 "length": 1
27 },
28 "valid": true
29 }
30 ]
31 },
32 {
33 "description": "an array of schemas for items",
34 "schema": {
35 "items": [
36 {"type": "integer"},
37 {"type": "string"}
38 ]
39 },
40 "tests": [
41 {
42 "description": "correct types",
43 "data": [ 1, "foo" ],
44 "valid": true
45 },
46 {
47 "description": "wrong types",
48 "data": [ "foo", 1 ],
49 "valid": false
50 },
51 {
52 "description": "incomplete array of items",
53 "data": [ 1 ],
54 "valid": true
55 },
56 {
57 "description": "array with additional items",
58 "data": [ 1, "foo", true ],
59 "valid": true
60 },
61 {
62 "description": "empty array",
63 "data": [ ],
64 "valid": true
65 },
66 {
67 "description": "JavaScript pseudo-array is valid",
68 "data": {
69 "0": "invalid",
70 "1": "valid",
71 "length": 2
72 },
73 "valid": true
74 }
75 ]
76 },
77 {
78 "description": "items with boolean schema (true)",
79 "schema": {"items": true},
80 "tests": [
81 {
82 "description": "any array is valid",
83 "data": [ 1, "foo", true ],
84 "valid": true
85 },
86 {
87 "description": "empty array is valid",
88 "data": [],
89 "valid": true
90 }
91 ]
92 },
93 {
94 "description": "items with boolean schema (false)",
95 "schema": {"items": false},
96 "tests": [
97 {
98 "description": "any non-empty array is invalid",
99 "data": [ 1, "foo", true ],
100 "valid": false
101 },
102 {
103 "description": "empty array is valid",
104 "data": [],
105 "valid": true
106 }
107 ]
108 },
109 {
110 "description": "items with boolean schemas",
111 "schema": {
112 "items": [true, false]
113 },
114 "tests": [
115 {
116 "description": "array with one item is valid",
117 "data": [ 1 ],
118 "valid": true
119 },
120 {
121 "description": "array with two items is invalid",
122 "data": [ 1, "foo" ],
123 "valid": false
124 },
125 {
126 "description": "empty array is valid",
127 "data": [],
128 "valid": true
129 }
130 ]
131 }
132 ]
0 [
1 {
2 "description": "maxItems validation",
3 "schema": {"maxItems": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": [1],
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": [1, 2],
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": [1, 2, 3],
18 "valid": false
19 },
20 {
21 "description": "ignores non-arrays",
22 "data": "foobar",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "maxLength validation",
3 "schema": {"maxLength": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": "f",
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": "fo",
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": "foo",
18 "valid": false
19 },
20 {
21 "description": "ignores non-strings",
22 "data": 100,
23 "valid": true
24 },
25 {
26 "description": "two supplementary Unicode code points is long enough",
27 "data": "\uD83D\uDCA9\uD83D\uDCA9",
28 "valid": true
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "maxProperties validation",
3 "schema": {"maxProperties": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": {"foo": 1},
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": {"foo": 1, "bar": 2},
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": {"foo": 1, "bar": 2, "baz": 3},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [1, 2, 3],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "foobar",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 }
35 ]
36 }
37 ]
0 [
1 {
2 "description": "maximum validation",
3 "schema": {"maximum": 3.0},
4 "tests": [
5 {
6 "description": "below the maximum is valid",
7 "data": 2.6,
8 "valid": true
9 },
10 {
11 "description": "boundary point is valid",
12 "data": 3.0,
13 "valid": true
14 },
15 {
16 "description": "above the maximum is invalid",
17 "data": 3.5,
18 "valid": false
19 },
20 {
21 "description": "ignores non-numbers",
22 "data": "x",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "minItems validation",
3 "schema": {"minItems": 1},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": [1, 2],
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": [1],
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": [],
18 "valid": false
19 },
20 {
21 "description": "ignores non-arrays",
22 "data": "",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "minLength validation",
3 "schema": {"minLength": 2},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": "foo",
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": "fo",
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": "f",
18 "valid": false
19 },
20 {
21 "description": "ignores non-strings",
22 "data": 1,
23 "valid": true
24 },
25 {
26 "description": "one supplementary Unicode code point is not long enough",
27 "data": "\uD83D\uDCA9",
28 "valid": false
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "minProperties validation",
3 "schema": {"minProperties": 1},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": {"foo": 1, "bar": 2},
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": {},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 }
35 ]
36 }
37 ]
0 [
1 {
2 "description": "minimum validation",
3 "schema": {"minimum": 1.1},
4 "tests": [
5 {
6 "description": "above the minimum is valid",
7 "data": 2.6,
8 "valid": true
9 },
10 {
11 "description": "boundary point is valid",
12 "data": 1.1,
13 "valid": true
14 },
15 {
16 "description": "below the minimum is invalid",
17 "data": 0.6,
18 "valid": false
19 },
20 {
21 "description": "ignores non-numbers",
22 "data": "x",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "by int",
3 "schema": {"multipleOf": 2},
4 "tests": [
5 {
6 "description": "int by int",
7 "data": 10,
8 "valid": true
9 },
10 {
11 "description": "int by int fail",
12 "data": 7,
13 "valid": false
14 },
15 {
16 "description": "ignores non-numbers",
17 "data": "foo",
18 "valid": true
19 }
20 ]
21 },
22 {
23 "description": "by number",
24 "schema": {"multipleOf": 1.5},
25 "tests": [
26 {
27 "description": "zero is multiple of anything",
28 "data": 0,
29 "valid": true
30 },
31 {
32 "description": "4.5 is multiple of 1.5",
33 "data": 4.5,
34 "valid": true
35 },
36 {
37 "description": "35 is not multiple of 1.5",
38 "data": 35,
39 "valid": false
40 }
41 ]
42 },
43 {
44 "description": "by small number",
45 "schema": {"multipleOf": 0.0001},
46 "tests": [
47 {
48 "description": "0.0075 is multiple of 0.0001",
49 "data": 0.0075,
50 "valid": true
51 },
52 {
53 "description": "0.00751 is not multiple of 0.0001",
54 "data": 0.00751,
55 "valid": false
56 }
57 ]
58 }
59 ]
0 [
1 {
2 "description": "not",
3 "schema": {
4 "not": {"type": "integer"}
5 },
6 "tests": [
7 {
8 "description": "allowed",
9 "data": "foo",
10 "valid": true
11 },
12 {
13 "description": "disallowed",
14 "data": 1,
15 "valid": false
16 }
17 ]
18 },
19 {
20 "description": "not multiple types",
21 "schema": {
22 "not": {"type": ["integer", "boolean"]}
23 },
24 "tests": [
25 {
26 "description": "valid",
27 "data": "foo",
28 "valid": true
29 },
30 {
31 "description": "mismatch",
32 "data": 1,
33 "valid": false
34 },
35 {
36 "description": "other mismatch",
37 "data": true,
38 "valid": false
39 }
40 ]
41 },
42 {
43 "description": "not more complex schema",
44 "schema": {
45 "not": {
46 "type": "object",
47 "properties": {
48 "foo": {
49 "type": "string"
50 }
51 }
52 }
53 },
54 "tests": [
55 {
56 "description": "match",
57 "data": 1,
58 "valid": true
59 },
60 {
61 "description": "other match",
62 "data": {"foo": 1},
63 "valid": true
64 },
65 {
66 "description": "mismatch",
67 "data": {"foo": "bar"},
68 "valid": false
69 }
70 ]
71 },
72 {
73 "description": "forbidden property",
74 "schema": {
75 "properties": {
76 "foo": {
77 "not": {}
78 }
79 }
80 },
81 "tests": [
82 {
83 "description": "property present",
84 "data": {"foo": 1, "bar": 2},
85 "valid": false
86 },
87 {
88 "description": "property absent",
89 "data": {"bar": 1, "baz": 2},
90 "valid": true
91 }
92 ]
93 },
94 {
95 "description": "not with boolean schema true",
96 "schema": {"not": true},
97 "tests": [
98 {
99 "description": "any value is invalid",
100 "data": "foo",
101 "valid": false
102 }
103 ]
104 },
105 {
106 "description": "not with boolean schema false",
107 "schema": {"not": false},
108 "tests": [
109 {
110 "description": "any value is valid",
111 "data": "foo",
112 "valid": true
113 }
114 ]
115 }
116 ]
0 [
1 {
2 "description": "oneOf",
3 "schema": {
4 "oneOf": [
5 {
6 "type": "integer"
7 },
8 {
9 "minimum": 2
10 }
11 ]
12 },
13 "tests": [
14 {
15 "description": "first oneOf valid",
16 "data": 1,
17 "valid": true
18 },
19 {
20 "description": "second oneOf valid",
21 "data": 2.5,
22 "valid": true
23 },
24 {
25 "description": "both oneOf valid",
26 "data": 3,
27 "valid": false
28 },
29 {
30 "description": "neither oneOf valid",
31 "data": 1.5,
32 "valid": false
33 }
34 ]
35 },
36 {
37 "description": "oneOf with base schema",
38 "schema": {
39 "type": "string",
40 "oneOf" : [
41 {
42 "minLength": 2
43 },
44 {
45 "maxLength": 4
46 }
47 ]
48 },
49 "tests": [
50 {
51 "description": "mismatch base schema",
52 "data": 3,
53 "valid": false
54 },
55 {
56 "description": "one oneOf valid",
57 "data": "foobar",
58 "valid": true
59 },
60 {
61 "description": "both oneOf valid",
62 "data": "foo",
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description": "oneOf with boolean schemas, all true",
69 "schema": {"oneOf": [true, true, true]},
70 "tests": [
71 {
72 "description": "any value is invalid",
73 "data": "foo",
74 "valid": false
75 }
76 ]
77 },
78 {
79 "description": "oneOf with boolean schemas, one true",
80 "schema": {"oneOf": [true, false, false]},
81 "tests": [
82 {
83 "description": "any value is valid",
84 "data": "foo",
85 "valid": true
86 }
87 ]
88 },
89 {
90 "description": "oneOf with boolean schemas, more than one true",
91 "schema": {"oneOf": [true, true, false]},
92 "tests": [
93 {
94 "description": "any value is invalid",
95 "data": "foo",
96 "valid": false
97 }
98 ]
99 },
100 {
101 "description": "oneOf with boolean schemas, all false",
102 "schema": {"oneOf": [false, false, false]},
103 "tests": [
104 {
105 "description": "any value is invalid",
106 "data": "foo",
107 "valid": false
108 }
109 ]
110 },
111 {
112 "description": "oneOf complex types",
113 "schema": {
114 "oneOf": [
115 {
116 "properties": {
117 "bar": {"type": "integer"}
118 },
119 "required": ["bar"]
120 },
121 {
122 "properties": {
123 "foo": {"type": "string"}
124 },
125 "required": ["foo"]
126 }
127 ]
128 },
129 "tests": [
130 {
131 "description": "first oneOf valid (complex)",
132 "data": {"bar": 2},
133 "valid": true
134 },
135 {
136 "description": "second oneOf valid (complex)",
137 "data": {"foo": "baz"},
138 "valid": true
139 },
140 {
141 "description": "both oneOf valid (complex)",
142 "data": {"foo": "baz", "bar": 2},
143 "valid": false
144 },
145 {
146 "description": "neither oneOf valid (complex)",
147 "data": {"foo": 2, "bar": "quux"},
148 "valid": false
149 }
150 ]
151 }
152 ]
0 [
1 {
2 "description": "integer",
3 "schema": {"type": "integer"},
4 "tests": [
5 {
6 "description": "a bignum is an integer",
7 "data": 12345678910111213141516171819202122232425262728293031,
8 "valid": true
9 }
10 ]
11 },
12 {
13 "description": "number",
14 "schema": {"type": "number"},
15 "tests": [
16 {
17 "description": "a bignum is a number",
18 "data": 98249283749234923498293171823948729348710298301928331,
19 "valid": true
20 }
21 ]
22 },
23 {
24 "description": "integer",
25 "schema": {"type": "integer"},
26 "tests": [
27 {
28 "description": "a negative bignum is an integer",
29 "data": -12345678910111213141516171819202122232425262728293031,
30 "valid": true
31 }
32 ]
33 },
34 {
35 "description": "number",
36 "schema": {"type": "number"},
37 "tests": [
38 {
39 "description": "a negative bignum is a number",
40 "data": -98249283749234923498293171823948729348710298301928331,
41 "valid": true
42 }
43 ]
44 },
45 {
46 "description": "string",
47 "schema": {"type": "string"},
48 "tests": [
49 {
50 "description": "a bignum is not a string",
51 "data": 98249283749234923498293171823948729348710298301928331,
52 "valid": false
53 }
54 ]
55 },
56 {
57 "description": "integer comparison",
58 "schema": {"maximum": 18446744073709551615},
59 "tests": [
60 {
61 "description": "comparison works for high numbers",
62 "data": 18446744073709551600,
63 "valid": true
64 }
65 ]
66 },
67 {
68 "description": "float comparison with high precision",
69 "schema": {
70 "exclusiveMaximum": 972783798187987123879878123.18878137
71 },
72 "tests": [
73 {
74 "description": "comparison works for high numbers",
75 "data": 972783798187987123879878123.188781371,
76 "valid": false
77 }
78 ]
79 },
80 {
81 "description": "integer comparison",
82 "schema": {"minimum": -18446744073709551615},
83 "tests": [
84 {
85 "description": "comparison works for very negative numbers",
86 "data": -18446744073709551600,
87 "valid": true
88 }
89 ]
90 },
91 {
92 "description": "float comparison with high precision on negative numbers",
93 "schema": {
94 "exclusiveMinimum": -972783798187987123879878123.18878137
95 },
96 "tests": [
97 {
98 "description": "comparison works for very negative numbers",
99 "data": -972783798187987123879878123.188781371,
100 "valid": false
101 }
102 ]
103 }
104 ]
0 [
1 {
2 "description": "ECMA 262 regex non-compliance",
3 "schema": { "format": "regex" },
4 "tests": [
5 {
6 "description": "ECMA 262 has no support for \\Z anchor from .NET",
7 "data": "^\\S(|(.|\\n)*\\S)\\Z",
8 "valid": false
9 }
10 ]
11 }
12 ]
0 [
1 {
2 "description": "validation of date-time strings",
3 "schema": {"format": "date-time"},
4 "tests": [
5 {
6 "description": "a valid date-time string",
7 "data": "1963-06-19T08:30:06.283185Z",
8 "valid": true
9 },
10 {
11 "description": "an invalid date-time string",
12 "data": "06/19/1963 08:30:06 PST",
13 "valid": false
14 },
15 {
16 "description": "only RFC3339 not all of ISO 8601 are valid",
17 "data": "2013-350T01:01:01",
18 "valid": false
19 }
20 ]
21 },
22 {
23 "description": "validation of URIs",
24 "schema": {"format": "uri"},
25 "tests": [
26 {
27 "description": "a valid URL with anchor tag",
28 "data": "http://foo.bar/?baz=qux#quux",
29 "valid": true
30 },
31 {
32 "description": "a valid URL with anchor tag and parantheses",
33 "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
34 "valid": true
35 },
36 {
37 "description": "a valid URL with URL-encoded stuff",
38 "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
39 "valid": true
40 },
41 {
42 "description": "a valid puny-coded URL ",
43 "data": "http://xn--nw2a.xn--j6w193g/",
44 "valid": true
45 },
46 {
47 "description": "a valid URL with many special characters",
48 "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
49 "valid": true
50 },
51 {
52 "description": "a valid URL based on IPv4",
53 "data": "http://223.255.255.254",
54 "valid": true
55 },
56 {
57 "description": "a valid URL with ftp scheme",
58 "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
59 "valid": true
60 },
61 {
62 "description": "a valid URL for a simple text file",
63 "data": "http://www.ietf.org/rfc/rfc2396.txt",
64 "valid": true
65 },
66 {
67 "description": "a valid URL ",
68 "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
69 "valid": true
70 },
71 {
72 "description": "a valid mailto URI",
73 "data": "mailto:John.Doe@example.com",
74 "valid": true
75 },
76 {
77 "description": "a valid newsgroup URI",
78 "data": "news:comp.infosystems.www.servers.unix",
79 "valid": true
80 },
81 {
82 "description": "a valid tel URI",
83 "data": "tel:+1-816-555-1212",
84 "valid": true
85 },
86 {
87 "description": "a valid URN",
88 "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
89 "valid": true
90 },
91 {
92 "description": "an invalid protocol-relative URI Reference",
93 "data": "//foo.bar/?baz=qux#quux",
94 "valid": false
95 },
96 {
97 "description": "an invalid relative URI Reference",
98 "data": "/abc",
99 "valid": false
100 },
101 {
102 "description": "an invalid URI",
103 "data": "\\\\WINDOWS\\fileshare",
104 "valid": false
105 },
106 {
107 "description": "an invalid URI though valid URI reference",
108 "data": "abc",
109 "valid": false
110 },
111 {
112 "description": "an invalid URI with spaces",
113 "data": "http:// shouldfail.com",
114 "valid": false
115 },
116 {
117 "description": "an invalid URI with spaces and missing scheme",
118 "data": ":// should fail",
119 "valid": false
120 }
121 ]
122 },
123 {
124 "description": "validation of URI References",
125 "schema": {"format": "uri-reference"},
126 "tests": [
127 {
128 "description": "a valid URI",
129 "data": "http://foo.bar/?baz=qux#quux",
130 "valid": true
131 },
132 {
133 "description": "a valid protocol-relative URI Reference",
134 "data": "//foo.bar/?baz=qux#quux",
135 "valid": true
136 },
137 {
138 "description": "a valid relative URI Reference",
139 "data": "/abc",
140 "valid": true
141 },
142 {
143 "description": "an invalid URI Reference",
144 "data": "\\\\WINDOWS\\fileshare",
145 "valid": false
146 },
147 {
148 "description": "a valid URI Reference",
149 "data": "abc",
150 "valid": true
151 },
152 {
153 "description": "a valid URI fragment",
154 "data": "#fragment",
155 "valid": true
156 },
157 {
158 "description": "an invalid URI fragment",
159 "data": "#frag\\ment",
160 "valid": false
161 }
162 ]
163 },
164 {
165 "description": "format: uri-template",
166 "schema": {
167 "format": "uri-template"
168 },
169 "tests": [
170 {
171 "description": "a valid uri-template",
172 "data": "http://example.com/dictionary/{term:1}/{term}",
173 "valid": true
174 },
175 {
176 "description": "an invalid uri-template",
177 "data": "http://example.com/dictionary/{term:1}/{term",
178 "valid": false
179 },
180 {
181 "description": "a valid uri-template without variables",
182 "data": "http://example.com/dictionary",
183 "valid": true
184 },
185 {
186 "description": "a valid relative uri-template",
187 "data": "dictionary/{term:1}/{term}",
188 "valid": true
189 }
190 ]
191 },
192 {
193 "description": "validation of e-mail addresses",
194 "schema": {"format": "email"},
195 "tests": [
196 {
197 "description": "a valid e-mail address",
198 "data": "joe.bloggs@example.com",
199 "valid": true
200 },
201 {
202 "description": "an invalid e-mail address",
203 "data": "2962",
204 "valid": false
205 }
206 ]
207 },
208 {
209 "description": "validation of IP addresses",
210 "schema": {"format": "ipv4"},
211 "tests": [
212 {
213 "description": "a valid IP address",
214 "data": "192.168.0.1",
215 "valid": true
216 },
217 {
218 "description": "an IP address with too many components",
219 "data": "127.0.0.0.1",
220 "valid": false
221 },
222 {
223 "description": "an IP address with out-of-range values",
224 "data": "256.256.256.256",
225 "valid": false
226 },
227 {
228 "description": "an IP address without 4 components",
229 "data": "127.0",
230 "valid": false
231 },
232 {
233 "description": "an IP address as an integer",
234 "data": "0x7f000001",
235 "valid": false
236 }
237 ]
238 },
239 {
240 "description": "validation of IPv6 addresses",
241 "schema": {"format": "ipv6"},
242 "tests": [
243 {
244 "description": "a valid IPv6 address",
245 "data": "::1",
246 "valid": true
247 },
248 {
249 "description": "an IPv6 address with out-of-range values",
250 "data": "12345::",
251 "valid": false
252 },
253 {
254 "description": "an IPv6 address with too many components",
255 "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
256 "valid": false
257 },
258 {
259 "description": "an IPv6 address containing illegal characters",
260 "data": "::laptop",
261 "valid": false
262 }
263 ]
264 },
265 {
266 "description": "validation of host names",
267 "schema": {"format": "hostname"},
268 "tests": [
269 {
270 "description": "a valid host name",
271 "data": "www.example.com",
272 "valid": true
273 },
274 {
275 "description": "a host name starting with an illegal character",
276 "data": "-a-host-name-that-starts-with--",
277 "valid": false
278 },
279 {
280 "description": "a host name containing illegal characters",
281 "data": "not_a_valid_host_name",
282 "valid": false
283 },
284 {
285 "description": "a host name with a component too long",
286 "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
287 "valid": false
288 }
289 ]
290 },
291 {
292 "description": "validation of JSON-pointers (JSON String Representation)",
293 "schema": {"format": "json-pointer"},
294 "tests": [
295 {
296 "description": "a valid JSON-pointer",
297 "data": "/foo/bar~0/baz~1/%a",
298 "valid": true
299 },
300 {
301 "description": "not a valid JSON-pointer (~ not escaped)",
302 "data": "/foo/bar~",
303 "valid": false
304 },
305 {
306 "description": "valid JSON-pointer with empty segment",
307 "data": "/foo//bar",
308 "valid": true
309 },
310 {
311 "description": "valid JSON-pointer with the last empty segment",
312 "data": "/foo/bar/",
313 "valid": true
314 },
315 {
316 "description": "valid JSON-pointer as stated in RFC 6901 #1",
317 "data": "",
318 "valid": true
319 },
320 {
321 "description": "valid JSON-pointer as stated in RFC 6901 #2",
322 "data": "/foo",
323 "valid": true
324 },
325 {
326 "description": "valid JSON-pointer as stated in RFC 6901 #3",
327 "data": "/foo/0",
328 "valid": true
329 },
330 {
331 "description": "valid JSON-pointer as stated in RFC 6901 #4",
332 "data": "/",
333 "valid": true
334 },
335 {
336 "description": "valid JSON-pointer as stated in RFC 6901 #5",
337 "data": "/a~1b",
338 "valid": true
339 },
340 {
341 "description": "valid JSON-pointer as stated in RFC 6901 #6",
342 "data": "/c%d",
343 "valid": true
344 },
345 {
346 "description": "valid JSON-pointer as stated in RFC 6901 #7",
347 "data": "/e^f",
348 "valid": true
349 },
350 {
351 "description": "valid JSON-pointer as stated in RFC 6901 #8",
352 "data": "/g|h",
353 "valid": true
354 },
355 {
356 "description": "valid JSON-pointer as stated in RFC 6901 #9",
357 "data": "/i\\j",
358 "valid": true
359 },
360 {
361 "description": "valid JSON-pointer as stated in RFC 6901 #10",
362 "data": "/k\"l",
363 "valid": true
364 },
365 {
366 "description": "valid JSON-pointer as stated in RFC 6901 #11",
367 "data": "/ ",
368 "valid": true
369 },
370 {
371 "description": "valid JSON-pointer as stated in RFC 6901 #12",
372 "data": "/m~0n",
373 "valid": true
374 },
375 {
376 "description": "valid JSON-pointer used adding to the last array position",
377 "data": "/foo/-",
378 "valid": true
379 },
380 {
381 "description": "valid JSON-pointer (- used as object member name)",
382 "data": "/foo/-/bar",
383 "valid": true
384 },
385 {
386 "description": "valid JSON-pointer (multiple escaped characters)",
387 "data": "/~1~0~0~1~1",
388 "valid": true
389 },
390 {
391 "description": "valid JSON-pointer (escaped with fraction part) #1",
392 "data": "/~1.1",
393 "valid": true
394 },
395 {
396 "description": "valid JSON-pointer (escaped with fraction part) #2",
397 "data": "/~0.1",
398 "valid": true
399 },
400 {
401 "description": "not a valid JSON-pointer (URI Fragment Identifier) #1",
402 "data": "#",
403 "valid": false
404 },
405 {
406 "description": "not a valid JSON-pointer (URI Fragment Identifier) #2",
407 "data": "#/",
408 "valid": false
409 },
410 {
411 "description": "not a valid JSON-pointer (URI Fragment Identifier) #3",
412 "data": "#a",
413 "valid": false
414 },
415 {
416 "description": "not a valid JSON-pointer (some escaped, but not all) #1",
417 "data": "/~0~",
418 "valid": false
419 },
420 {
421 "description": "not a valid JSON-pointer (some escaped, but not all) #2",
422 "data": "/~0/~",
423 "valid": false
424 },
425 {
426 "description": "not a valid JSON-pointer (wrong escape character) #1",
427 "data": "/~2",
428 "valid": false
429 },
430 {
431 "description": "not a valid JSON-pointer (wrong escape character) #2",
432 "data": "/~-1",
433 "valid": false
434 },
435 {
436 "description": "not a valid JSON-pointer (multiple characters not escaped)",
437 "data": "/~~",
438 "valid": false
439 },
440 {
441 "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1",
442 "data": "a",
443 "valid": false
444 },
445 {
446 "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2",
447 "data": "0",
448 "valid": false
449 },
450 {
451 "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3",
452 "data": "a/a",
453 "valid": false
454 }
455 ]
456 }
457 ]
0 [
1 {
2 "description": "some languages do not distinguish between different types of numeric value",
3 "schema": {
4 "type": "integer"
5 },
6 "tests": [
7 {
8 "description": "a float without fractional part is an integer",
9 "data": 1.0,
10 "valid": true
11 }
12 ]
13 }
14 ]
0 [
1 {
2 "description": "pattern validation",
3 "schema": {"pattern": "^a*$"},
4 "tests": [
5 {
6 "description": "a matching pattern is valid",
7 "data": "aaa",
8 "valid": true
9 },
10 {
11 "description": "a non-matching pattern is invalid",
12 "data": "abc",
13 "valid": false
14 },
15 {
16 "description": "ignores non-strings",
17 "data": true,
18 "valid": true
19 }
20 ]
21 },
22 {
23 "description": "pattern is not anchored",
24 "schema": {"pattern": "a+"},
25 "tests": [
26 {
27 "description": "matches a substring",
28 "data": "xxaayy",
29 "valid": true
30 }
31 ]
32 }
33 ]
0 [
1 {
2 "description":
3 "patternProperties validates properties matching a regex",
4 "schema": {
5 "patternProperties": {
6 "f.*o": {"type": "integer"}
7 }
8 },
9 "tests": [
10 {
11 "description": "a single valid match is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "multiple valid matches is valid",
17 "data": {"foo": 1, "foooooo" : 2},
18 "valid": true
19 },
20 {
21 "description": "a single invalid match is invalid",
22 "data": {"foo": "bar", "fooooo": 2},
23 "valid": false
24 },
25 {
26 "description": "multiple invalid matches is invalid",
27 "data": {"foo": "bar", "foooooo" : "baz"},
28 "valid": false
29 },
30 {
31 "description": "ignores arrays",
32 "data": ["foo"],
33 "valid": true
34 },
35 {
36 "description": "ignores strings",
37 "data": "foo",
38 "valid": true
39 },
40 {
41 "description": "ignores other non-objects",
42 "data": 12,
43 "valid": true
44 }
45 ]
46 },
47 {
48 "description": "multiple simultaneous patternProperties are validated",
49 "schema": {
50 "patternProperties": {
51 "a*": {"type": "integer"},
52 "aaa*": {"maximum": 20}
53 }
54 },
55 "tests": [
56 {
57 "description": "a single valid match is valid",
58 "data": {"a": 21},
59 "valid": true
60 },
61 {
62 "description": "a simultaneous match is valid",
63 "data": {"aaaa": 18},
64 "valid": true
65 },
66 {
67 "description": "multiple matches is valid",
68 "data": {"a": 21, "aaaa": 18},
69 "valid": true
70 },
71 {
72 "description": "an invalid due to one is invalid",
73 "data": {"a": "bar"},
74 "valid": false
75 },
76 {
77 "description": "an invalid due to the other is invalid",
78 "data": {"aaaa": 31},
79 "valid": false
80 },
81 {
82 "description": "an invalid due to both is invalid",
83 "data": {"aaa": "foo", "aaaa": 31},
84 "valid": false
85 }
86 ]
87 },
88 {
89 "description": "regexes are not anchored by default and are case sensitive",
90 "schema": {
91 "patternProperties": {
92 "[0-9]{2,}": { "type": "boolean" },
93 "X_": { "type": "string" }
94 }
95 },
96 "tests": [
97 {
98 "description": "non recognized members are ignored",
99 "data": { "answer 1": "42" },
100 "valid": true
101 },
102 {
103 "description": "recognized members are accounted for",
104 "data": { "a31b": null },
105 "valid": false
106 },
107 {
108 "description": "regexes are case sensitive",
109 "data": { "a_x_3": 3 },
110 "valid": true
111 },
112 {
113 "description": "regexes are case sensitive, 2",
114 "data": { "a_X_3": 3 },
115 "valid": false
116 }
117 ]
118 },
119 {
120 "description": "patternProperties with boolean schemas",
121 "schema": {
122 "patternProperties": {
123 "f.*": true,
124 "b.*": false
125 }
126 },
127 "tests": [
128 {
129 "description": "object with property matching schema true is valid",
130 "data": {"foo": 1},
131 "valid": true
132 },
133 {
134 "description": "object with property matching schema false is invalid",
135 "data": {"bar": 2},
136 "valid": false
137 },
138 {
139 "description": "object with both properties is invalid",
140 "data": {"foo": 1, "bar": 2},
141 "valid": false
142 },
143 {
144 "description": "empty object is valid",
145 "data": {},
146 "valid": true
147 }
148 ]
149 }
150 ]
0 [
1 {
2 "description": "object properties validation",
3 "schema": {
4 "properties": {
5 "foo": {"type": "integer"},
6 "bar": {"type": "string"}
7 }
8 },
9 "tests": [
10 {
11 "description": "both properties present and valid is valid",
12 "data": {"foo": 1, "bar": "baz"},
13 "valid": true
14 },
15 {
16 "description": "one property invalid is invalid",
17 "data": {"foo": 1, "bar": {}},
18 "valid": false
19 },
20 {
21 "description": "both properties invalid is invalid",
22 "data": {"foo": [], "bar": {}},
23 "valid": false
24 },
25 {
26 "description": "doesn't invalidate other properties",
27 "data": {"quux": []},
28 "valid": true
29 },
30 {
31 "description": "ignores arrays",
32 "data": [],
33 "valid": true
34 },
35 {
36 "description": "ignores other non-objects",
37 "data": 12,
38 "valid": true
39 }
40 ]
41 },
42 {
43 "description":
44 "properties, patternProperties, additionalProperties interaction",
45 "schema": {
46 "properties": {
47 "foo": {"type": "array", "maxItems": 3},
48 "bar": {"type": "array"}
49 },
50 "patternProperties": {"f.o": {"minItems": 2}},
51 "additionalProperties": {"type": "integer"}
52 },
53 "tests": [
54 {
55 "description": "property validates property",
56 "data": {"foo": [1, 2]},
57 "valid": true
58 },
59 {
60 "description": "property invalidates property",
61 "data": {"foo": [1, 2, 3, 4]},
62 "valid": false
63 },
64 {
65 "description": "patternProperty invalidates property",
66 "data": {"foo": []},
67 "valid": false
68 },
69 {
70 "description": "patternProperty validates nonproperty",
71 "data": {"fxo": [1, 2]},
72 "valid": true
73 },
74 {
75 "description": "patternProperty invalidates nonproperty",
76 "data": {"fxo": []},
77 "valid": false
78 },
79 {
80 "description": "additionalProperty ignores property",
81 "data": {"bar": []},
82 "valid": true
83 },
84 {
85 "description": "additionalProperty validates others",
86 "data": {"quux": 3},
87 "valid": true
88 },
89 {
90 "description": "additionalProperty invalidates others",
91 "data": {"quux": "foo"},
92 "valid": false
93 }
94 ]
95 },
96 {
97 "description": "properties with boolean schema",
98 "schema": {
99 "properties": {
100 "foo": true,
101 "bar": false
102 }
103 },
104 "tests": [
105 {
106 "description": "no property present is valid",
107 "data": {},
108 "valid": true
109 },
110 {
111 "description": "only 'true' property present is valid",
112 "data": {"foo": 1},
113 "valid": true
114 },
115 {
116 "description": "only 'false' property present is invalid",
117 "data": {"bar": 2},
118 "valid": false
119 },
120 {
121 "description": "both properties present is invalid",
122 "data": {"foo": 1, "bar": 2},
123 "valid": false
124 }
125 ]
126 }
127 ]
0 [
1 {
2 "description": "propertyNames validation",
3 "schema": {
4 "propertyNames": {"maxLength": 3}
5 },
6 "tests": [
7 {
8 "description": "all property names valid",
9 "data": {
10 "f": {},
11 "foo": {}
12 },
13 "valid": true
14 },
15 {
16 "description": "some property names invalid",
17 "data": {
18 "foo": {},
19 "foobar": {}
20 },
21 "valid": false
22 },
23 {
24 "description": "object without properties is valid",
25 "data": {},
26 "valid": true
27 },
28 {
29 "description": "ignores arrays",
30 "data": [1, 2, 3, 4],
31 "valid": true
32 },
33 {
34 "description": "ignores strings",
35 "data": "foobar",
36 "valid": true
37 },
38 {
39 "description": "ignores other non-objects",
40 "data": 12,
41 "valid": true
42 }
43 ]
44 },
45 {
46 "description": "propertyNames with boolean schema true",
47 "schema": {"propertyNames": true},
48 "tests": [
49 {
50 "description": "object with any properties is valid",
51 "data": {"foo": 1},
52 "valid": true
53 },
54 {
55 "description": "empty object is valid",
56 "data": {},
57 "valid": true
58 }
59 ]
60 },
61 {
62 "description": "propertyNames with boolean schema false",
63 "schema": {"propertyNames": false},
64 "tests": [
65 {
66 "description": "object with any properties is invalid",
67 "data": {"foo": 1},
68 "valid": false
69 },
70 {
71 "description": "empty object is valid",
72 "data": {},
73 "valid": true
74 }
75 ]
76 }
77 ]
0 [
1 {
2 "description": "root pointer ref",
3 "schema": {
4 "properties": {
5 "foo": {"$ref": "#"}
6 },
7 "additionalProperties": false
8 },
9 "tests": [
10 {
11 "description": "match",
12 "data": {"foo": false},
13 "valid": true
14 },
15 {
16 "description": "recursive match",
17 "data": {"foo": {"foo": false}},
18 "valid": true
19 },
20 {
21 "description": "mismatch",
22 "data": {"bar": false},
23 "valid": false
24 },
25 {
26 "description": "recursive mismatch",
27 "data": {"foo": {"bar": false}},
28 "valid": false
29 }
30 ]
31 },
32 {
33 "description": "relative pointer ref to object",
34 "schema": {
35 "properties": {
36 "foo": {"type": "integer"},
37 "bar": {"$ref": "#/properties/foo"}
38 }
39 },
40 "tests": [
41 {
42 "description": "match",
43 "data": {"bar": 3},
44 "valid": true
45 },
46 {
47 "description": "mismatch",
48 "data": {"bar": true},
49 "valid": false
50 }
51 ]
52 },
53 {
54 "description": "relative pointer ref to array",
55 "schema": {
56 "items": [
57 {"type": "integer"},
58 {"$ref": "#/items/0"}
59 ]
60 },
61 "tests": [
62 {
63 "description": "match array",
64 "data": [1, 2],
65 "valid": true
66 },
67 {
68 "description": "mismatch array",
69 "data": [1, "foo"],
70 "valid": false
71 }
72 ]
73 },
74 {
75 "description": "escaped pointer ref",
76 "schema": {
77 "tilda~field": {"type": "integer"},
78 "slash/field": {"type": "integer"},
79 "percent%field": {"type": "integer"},
80 "properties": {
81 "tilda": {"$ref": "#/tilda~0field"},
82 "slash": {"$ref": "#/slash~1field"},
83 "percent": {"$ref": "#/percent%25field"}
84 }
85 },
86 "tests": [
87 {
88 "description": "slash invalid",
89 "data": {"slash": "aoeu"},
90 "valid": false
91 },
92 {
93 "description": "tilda invalid",
94 "data": {"tilda": "aoeu"},
95 "valid": false
96 },
97 {
98 "description": "percent invalid",
99 "data": {"percent": "aoeu"},
100 "valid": false
101 },
102 {
103 "description": "slash valid",
104 "data": {"slash": 123},
105 "valid": true
106 },
107 {
108 "description": "tilda valid",
109 "data": {"tilda": 123},
110 "valid": true
111 },
112 {
113 "description": "percent valid",
114 "data": {"percent": 123},
115 "valid": true
116 }
117 ]
118 },
119 {
120 "description": "nested refs",
121 "schema": {
122 "definitions": {
123 "a": {"type": "integer"},
124 "b": {"$ref": "#/definitions/a"},
125 "c": {"$ref": "#/definitions/b"}
126 },
127 "$ref": "#/definitions/c"
128 },
129 "tests": [
130 {
131 "description": "nested ref valid",
132 "data": 5,
133 "valid": true
134 },
135 {
136 "description": "nested ref invalid",
137 "data": "a",
138 "valid": false
139 }
140 ]
141 },
142 {
143 "description": "ref overrides any sibling keywords",
144 "schema": {
145 "definitions": {
146 "reffed": {
147 "type": "array"
148 }
149 },
150 "properties": {
151 "foo": {
152 "$ref": "#/definitions/reffed",
153 "maxItems": 2
154 }
155 }
156 },
157 "tests": [
158 {
159 "description": "ref valid",
160 "data": { "foo": [] },
161 "valid": true
162 },
163 {
164 "description": "ref valid, maxItems ignored",
165 "data": { "foo": [ 1, 2, 3] },
166 "valid": true
167 },
168 {
169 "description": "ref invalid",
170 "data": { "foo": "string" },
171 "valid": false
172 }
173 ]
174 },
175 {
176 "description": "remote ref, containing refs itself",
177 "schema": {"$ref": "http://json-schema.org/draft-06/schema#"},
178 "tests": [
179 {
180 "description": "remote ref valid",
181 "data": {"minLength": 1},
182 "valid": true
183 },
184 {
185 "description": "remote ref invalid",
186 "data": {"minLength": -1},
187 "valid": false
188 }
189 ]
190 },
191 {
192 "description": "property named $ref that is not a reference",
193 "schema": {
194 "properties": {
195 "$ref": {"type": "string"}
196 }
197 },
198 "tests": [
199 {
200 "description": "property named $ref valid",
201 "data": {"$ref": "a"},
202 "valid": true
203 },
204 {
205 "description": "property named $ref invalid",
206 "data": {"$ref": 2},
207 "valid": false
208 }
209 ]
210 },
211 {
212 "description": "$ref to boolean schema true",
213 "schema": {
214 "$ref": "#/definitions/bool",
215 "definitions": {
216 "bool": true
217 }
218 },
219 "tests": [
220 {
221 "description": "any value is valid",
222 "data": "foo",
223 "valid": true
224 }
225 ]
226 },
227 {
228 "description": "$ref to boolean schema false",
229 "schema": {
230 "$ref": "#/definitions/bool",
231 "definitions": {
232 "bool": false
233 }
234 },
235 "tests": [
236 {
237 "description": "any value is invalid",
238 "data": "foo",
239 "valid": false
240 }
241 ]
242 },
243 {
244 "description": "Recursive references between schemas",
245 "schema": {
246 "$id": "http://localhost:1234/tree",
247 "description": "tree of nodes",
248 "type": "object",
249 "properties": {
250 "meta": {"type": "string"},
251 "nodes": {
252 "type": "array",
253 "items": {"$ref": "node"}
254 }
255 },
256 "required": ["meta", "nodes"],
257 "definitions": {
258 "node": {
259 "$id": "http://localhost:1234/node",
260 "description": "node",
261 "type": "object",
262 "properties": {
263 "value": {"type": "number"},
264 "subtree": {"$ref": "tree"}
265 },
266 "required": ["value"]
267 }
268 }
269 },
270 "tests": [
271 {
272 "description": "valid tree",
273 "data": {
274 "meta": "root",
275 "nodes": [
276 {
277 "value": 1,
278 "subtree": {
279 "meta": "child",
280 "nodes": [
281 {"value": 1.1},
282 {"value": 1.2}
283 ]
284 }
285 },
286 {
287 "value": 2,
288 "subtree": {
289 "meta": "child",
290 "nodes": [
291 {"value": 2.1},
292 {"value": 2.2}
293 ]
294 }
295 }
296 ]
297 },
298 "valid": true
299 },
300 {
301 "description": "invalid tree",
302 "data": {
303 "meta": "root",
304 "nodes": [
305 {
306 "value": 1,
307 "subtree": {
308 "meta": "child",
309 "nodes": [
310 {"value": "string is invalid"},
311 {"value": 1.2}
312 ]
313 }
314 },
315 {
316 "value": 2,
317 "subtree": {
318 "meta": "child",
319 "nodes": [
320 {"value": 2.1},
321 {"value": 2.2}
322 ]
323 }
324 }
325 ]
326 },
327 "valid": false
328 }
329 ]
330 }
331 ]
0 [
1 {
2 "description": "remote ref",
3 "schema": {"$ref": "http://localhost:1234/integer.json"},
4 "tests": [
5 {
6 "description": "remote ref valid",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "remote ref invalid",
12 "data": "a",
13 "valid": false
14 }
15 ]
16 },
17 {
18 "description": "fragment within remote ref",
19 "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
20 "tests": [
21 {
22 "description": "remote fragment valid",
23 "data": 1,
24 "valid": true
25 },
26 {
27 "description": "remote fragment invalid",
28 "data": "a",
29 "valid": false
30 }
31 ]
32 },
33 {
34 "description": "ref within remote ref",
35 "schema": {
36 "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
37 },
38 "tests": [
39 {
40 "description": "ref within ref valid",
41 "data": 1,
42 "valid": true
43 },
44 {
45 "description": "ref within ref invalid",
46 "data": "a",
47 "valid": false
48 }
49 ]
50 },
51 {
52 "description": "base URI change",
53 "schema": {
54 "$id": "http://localhost:1234/",
55 "items": {
56 "$id": "folder/",
57 "items": {"$ref": "folderInteger.json"}
58 }
59 },
60 "tests": [
61 {
62 "description": "base URI change ref valid",
63 "data": [[1]],
64 "valid": true
65 },
66 {
67 "description": "base URI change ref invalid",
68 "data": [["a"]],
69 "valid": false
70 }
71 ]
72 },
73 {
74 "description": "base URI change - change folder",
75 "schema": {
76 "$id": "http://localhost:1234/scope_change_defs1.json",
77 "type" : "object",
78 "properties": {
79 "list": {"$ref": "#/definitions/baz"}
80 },
81 "definitions": {
82 "baz": {
83 "$id": "folder/",
84 "type": "array",
85 "items": {"$ref": "folderInteger.json"}
86 }
87 }
88 },
89 "tests": [
90 {
91 "description": "number is valid",
92 "data": {"list": [1]},
93 "valid": true
94 },
95 {
96 "description": "string is invalid",
97 "data": {"list": ["a"]},
98 "valid": false
99 }
100 ]
101 },
102 {
103 "description": "base URI change - change folder in subschema",
104 "schema": {
105 "$id": "http://localhost:1234/scope_change_defs2.json",
106 "type" : "object",
107 "properties": {
108 "list": {"$ref": "#/definitions/baz/definitions/bar"}
109 },
110 "definitions": {
111 "baz": {
112 "$id": "folder/",
113 "definitions": {
114 "bar": {
115 "type": "array",
116 "items": {"$ref": "folderInteger.json"}
117 }
118 }
119 }
120 }
121 },
122 "tests": [
123 {
124 "description": "number is valid",
125 "data": {"list": [1]},
126 "valid": true
127 },
128 {
129 "description": "string is invalid",
130 "data": {"list": ["a"]},
131 "valid": false
132 }
133 ]
134 },
135 {
136 "description": "root ref in remote ref",
137 "schema": {
138 "$id": "http://localhost:1234/object",
139 "type": "object",
140 "properties": {
141 "name": {"$ref": "name.json#/definitions/orNull"}
142 }
143 },
144 "tests": [
145 {
146 "description": "string is valid",
147 "data": {
148 "name": "foo"
149 },
150 "valid": true
151 },
152 {
153 "description": "null is valid",
154 "data": {
155 "name": null
156 },
157 "valid": true
158 },
159 {
160 "description": "object is invalid",
161 "data": {
162 "name": {
163 "name": null
164 }
165 },
166 "valid": false
167 }
168 ]
169 }
170 ]
0 [
1 {
2 "description": "required validation",
3 "schema": {
4 "properties": {
5 "foo": {},
6 "bar": {}
7 },
8 "required": ["foo"]
9 },
10 "tests": [
11 {
12 "description": "present required property is valid",
13 "data": {"foo": 1},
14 "valid": true
15 },
16 {
17 "description": "non-present required property is invalid",
18 "data": {"bar": 1},
19 "valid": false
20 },
21 {
22 "description": "ignores arrays",
23 "data": [],
24 "valid": true
25 },
26 {
27 "description": "ignores strings",
28 "data": "",
29 "valid": true
30 },
31 {
32 "description": "ignores other non-objects",
33 "data": 12,
34 "valid": true
35 }
36 ]
37 },
38 {
39 "description": "required default validation",
40 "schema": {
41 "properties": {
42 "foo": {}
43 }
44 },
45 "tests": [
46 {
47 "description": "not required by default",
48 "data": {},
49 "valid": true
50 }
51 ]
52 },
53 {
54 "description": "required with empty array",
55 "schema": {
56 "properties": {
57 "foo": {}
58 },
59 "required": []
60 },
61 "tests": [
62 {
63 "description": "property not required",
64 "data": {},
65 "valid": true
66 }
67 ]
68 }
69 ]
0 [
1 {
2 "description": "integer type matches integers",
3 "schema": {"type": "integer"},
4 "tests": [
5 {
6 "description": "an integer is an integer",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "a float is not an integer",
12 "data": 1.1,
13 "valid": false
14 },
15 {
16 "description": "a string is not an integer",
17 "data": "foo",
18 "valid": false
19 },
20 {
21 "description": "a string is still not an integer, even if it looks like one",
22 "data": "1",
23 "valid": false
24 },
25 {
26 "description": "an object is not an integer",
27 "data": {},
28 "valid": false
29 },
30 {
31 "description": "an array is not an integer",
32 "data": [],
33 "valid": false
34 },
35 {
36 "description": "a boolean is not an integer",
37 "data": true,
38 "valid": false
39 },
40 {
41 "description": "null is not an integer",
42 "data": null,
43 "valid": false
44 }
45 ]
46 },
47 {
48 "description": "number type matches numbers",
49 "schema": {"type": "number"},
50 "tests": [
51 {
52 "description": "an integer is a number",
53 "data": 1,
54 "valid": true
55 },
56 {
57 "description": "a float is a number",
58 "data": 1.1,
59 "valid": true
60 },
61 {
62 "description": "a string is not a number",
63 "data": "foo",
64 "valid": false
65 },
66 {
67 "description": "a string is still not a number, even if it looks like one",
68 "data": "1",
69 "valid": false
70 },
71 {
72 "description": "an object is not a number",
73 "data": {},
74 "valid": false
75 },
76 {
77 "description": "an array is not a number",
78 "data": [],
79 "valid": false
80 },
81 {
82 "description": "a boolean is not a number",
83 "data": true,
84 "valid": false
85 },
86 {
87 "description": "null is not a number",
88 "data": null,
89 "valid": false
90 }
91 ]
92 },
93 {
94 "description": "string type matches strings",
95 "schema": {"type": "string"},
96 "tests": [
97 {
98 "description": "1 is not a string",
99 "data": 1,
100 "valid": false
101 },
102 {
103 "description": "a float is not a string",
104 "data": 1.1,
105 "valid": false
106 },
107 {
108 "description": "a string is a string",
109 "data": "foo",
110 "valid": true
111 },
112 {
113 "description": "a string is still a string, even if it looks like a number",
114 "data": "1",
115 "valid": true
116 },
117 {
118 "description": "an object is not a string",
119 "data": {},
120 "valid": false
121 },
122 {
123 "description": "an array is not a string",
124 "data": [],
125 "valid": false
126 },
127 {
128 "description": "a boolean is not a string",
129 "data": true,
130 "valid": false
131 },
132 {
133 "description": "null is not a string",
134 "data": null,
135 "valid": false
136 }
137 ]
138 },
139 {
140 "description": "object type matches objects",
141 "schema": {"type": "object"},
142 "tests": [
143 {
144 "description": "an integer is not an object",
145 "data": 1,
146 "valid": false
147 },
148 {
149 "description": "a float is not an object",
150 "data": 1.1,
151 "valid": false
152 },
153 {
154 "description": "a string is not an object",
155 "data": "foo",
156 "valid": false
157 },
158 {
159 "description": "an object is an object",
160 "data": {},
161 "valid": true
162 },
163 {
164 "description": "an array is not an object",
165 "data": [],
166 "valid": false
167 },
168 {
169 "description": "a boolean is not an object",
170 "data": true,
171 "valid": false
172 },
173 {
174 "description": "null is not an object",
175 "data": null,
176 "valid": false
177 }
178 ]
179 },
180 {
181 "description": "array type matches arrays",
182 "schema": {"type": "array"},
183 "tests": [
184 {
185 "description": "an integer is not an array",
186 "data": 1,
187 "valid": false
188 },
189 {
190 "description": "a float is not an array",
191 "data": 1.1,
192 "valid": false
193 },
194 {
195 "description": "a string is not an array",
196 "data": "foo",
197 "valid": false
198 },
199 {
200 "description": "an object is not an array",
201 "data": {},
202 "valid": false
203 },
204 {
205 "description": "an array is an array",
206 "data": [],
207 "valid": true
208 },
209 {
210 "description": "a boolean is not an array",
211 "data": true,
212 "valid": false
213 },
214 {
215 "description": "null is not an array",
216 "data": null,
217 "valid": false
218 }
219 ]
220 },
221 {
222 "description": "boolean type matches booleans",
223 "schema": {"type": "boolean"},
224 "tests": [
225 {
226 "description": "an integer is not a boolean",
227 "data": 1,
228 "valid": false
229 },
230 {
231 "description": "a float is not a boolean",
232 "data": 1.1,
233 "valid": false
234 },
235 {
236 "description": "a string is not a boolean",
237 "data": "foo",
238 "valid": false
239 },
240 {
241 "description": "an object is not a boolean",
242 "data": {},
243 "valid": false
244 },
245 {
246 "description": "an array is not a boolean",
247 "data": [],
248 "valid": false
249 },
250 {
251 "description": "a boolean is a boolean",
252 "data": true,
253 "valid": true
254 },
255 {
256 "description": "null is not a boolean",
257 "data": null,
258 "valid": false
259 }
260 ]
261 },
262 {
263 "description": "null type matches only the null object",
264 "schema": {"type": "null"},
265 "tests": [
266 {
267 "description": "an integer is not null",
268 "data": 1,
269 "valid": false
270 },
271 {
272 "description": "a float is not null",
273 "data": 1.1,
274 "valid": false
275 },
276 {
277 "description": "a string is not null",
278 "data": "foo",
279 "valid": false
280 },
281 {
282 "description": "an object is not null",
283 "data": {},
284 "valid": false
285 },
286 {
287 "description": "an array is not null",
288 "data": [],
289 "valid": false
290 },
291 {
292 "description": "a boolean is not null",
293 "data": true,
294 "valid": false
295 },
296 {
297 "description": "null is null",
298 "data": null,
299 "valid": true
300 }
301 ]
302 },
303 {
304 "description": "multiple types can be specified in an array",
305 "schema": {"type": ["integer", "string"]},
306 "tests": [
307 {
308 "description": "an integer is valid",
309 "data": 1,
310 "valid": true
311 },
312 {
313 "description": "a string is valid",
314 "data": "foo",
315 "valid": true
316 },
317 {
318 "description": "a float is invalid",
319 "data": 1.1,
320 "valid": false
321 },
322 {
323 "description": "an object is invalid",
324 "data": {},
325 "valid": false
326 },
327 {
328 "description": "an array is invalid",
329 "data": [],
330 "valid": false
331 },
332 {
333 "description": "a boolean is invalid",
334 "data": true,
335 "valid": false
336 },
337 {
338 "description": "null is invalid",
339 "data": null,
340 "valid": false
341 }
342 ]
343 }
344 ]
0 [
1 {
2 "description": "uniqueItems validation",
3 "schema": {"uniqueItems": true},
4 "tests": [
5 {
6 "description": "unique array of integers is valid",
7 "data": [1, 2],
8 "valid": true
9 },
10 {
11 "description": "non-unique array of integers is invalid",
12 "data": [1, 1],
13 "valid": false
14 },
15 {
16 "description": "numbers are unique if mathematically unequal",
17 "data": [1.0, 1.00, 1],
18 "valid": false
19 },
20 {
21 "description": "unique array of objects is valid",
22 "data": [{"foo": "bar"}, {"foo": "baz"}],
23 "valid": true
24 },
25 {
26 "description": "non-unique array of objects is invalid",
27 "data": [{"foo": "bar"}, {"foo": "bar"}],
28 "valid": false
29 },
30 {
31 "description": "unique array of nested objects is valid",
32 "data": [
33 {"foo": {"bar" : {"baz" : true}}},
34 {"foo": {"bar" : {"baz" : false}}}
35 ],
36 "valid": true
37 },
38 {
39 "description": "non-unique array of nested objects is invalid",
40 "data": [
41 {"foo": {"bar" : {"baz" : true}}},
42 {"foo": {"bar" : {"baz" : true}}}
43 ],
44 "valid": false
45 },
46 {
47 "description": "unique array of arrays is valid",
48 "data": [["foo"], ["bar"]],
49 "valid": true
50 },
51 {
52 "description": "non-unique array of arrays is invalid",
53 "data": [["foo"], ["foo"]],
54 "valid": false
55 },
56 {
57 "description": "1 and true are unique",
58 "data": [1, true],
59 "valid": true
60 },
61 {
62 "description": "0 and false are unique",
63 "data": [0, false],
64 "valid": true
65 },
66 {
67 "description": "unique heterogeneous types are valid",
68 "data": [{}, [1], true, null, 1],
69 "valid": true
70 },
71 {
72 "description": "non-unique heterogeneous types are invalid",
73 "data": [{}, [1], true, null, {}, 1],
74 "valid": false
75 }
76 ]
77 }
78 ]
0 [
1 {
2 "description": "additionalItems as schema",
3 "schema": {
4 "items": [{}],
5 "additionalItems": {"type": "integer"}
6 },
7 "tests": [
8 {
9 "description": "additional items match schema",
10 "data": [ null, 2, 3, 4 ],
11 "valid": true
12 },
13 {
14 "description": "additional items do not match schema",
15 "data": [ null, 2, 3, "foo" ],
16 "valid": false
17 }
18 ]
19 },
20 {
21 "description": "items is schema, no additionalItems",
22 "schema": {
23 "items": {},
24 "additionalItems": false
25 },
26 "tests": [
27 {
28 "description": "all items match schema",
29 "data": [ 1, 2, 3, 4, 5 ],
30 "valid": true
31 }
32 ]
33 },
34 {
35 "description": "array of items with no additionalItems",
36 "schema": {
37 "items": [{}, {}, {}],
38 "additionalItems": false
39 },
40 "tests": [
41 {
42 "description": "fewer number of items present",
43 "data": [ 1, 2 ],
44 "valid": true
45 },
46 {
47 "description": "equal number of items present",
48 "data": [ 1, 2, 3 ],
49 "valid": true
50 },
51 {
52 "description": "additional items are not permitted",
53 "data": [ 1, 2, 3, 4 ],
54 "valid": false
55 }
56 ]
57 },
58 {
59 "description": "additionalItems as false without items",
60 "schema": {"additionalItems": false},
61 "tests": [
62 {
63 "description":
64 "items defaults to empty schema so everything is valid",
65 "data": [ 1, 2, 3, 4, 5 ],
66 "valid": true
67 },
68 {
69 "description": "ignores non-arrays",
70 "data": {"foo" : "bar"},
71 "valid": true
72 }
73 ]
74 },
75 {
76 "description": "additionalItems are allowed by default",
77 "schema": {"items": [{"type": "integer"}]},
78 "tests": [
79 {
80 "description": "only the first item is validated",
81 "data": [1, "foo", false],
82 "valid": true
83 }
84 ]
85 }
86 ]
0 [
1 {
2 "description":
3 "additionalProperties being false does not allow other properties",
4 "schema": {
5 "properties": {"foo": {}, "bar": {}},
6 "patternProperties": { "^v": {} },
7 "additionalProperties": false
8 },
9 "tests": [
10 {
11 "description": "no additional properties is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "an additional property is invalid",
17 "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [1, 2, 3],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "foobarbaz",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 },
35 {
36 "description": "patternProperties are not additional properties",
37 "data": {"foo":1, "vroom": 2},
38 "valid": true
39 }
40 ]
41 },
42 {
43 "description":
44 "additionalProperties allows a schema which should validate",
45 "schema": {
46 "properties": {"foo": {}, "bar": {}},
47 "additionalProperties": {"type": "boolean"}
48 },
49 "tests": [
50 {
51 "description": "no additional properties is valid",
52 "data": {"foo": 1},
53 "valid": true
54 },
55 {
56 "description": "an additional valid property is valid",
57 "data": {"foo" : 1, "bar" : 2, "quux" : true},
58 "valid": true
59 },
60 {
61 "description": "an additional invalid property is invalid",
62 "data": {"foo" : 1, "bar" : 2, "quux" : 12},
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description":
69 "additionalProperties can exist by itself",
70 "schema": {
71 "additionalProperties": {"type": "boolean"}
72 },
73 "tests": [
74 {
75 "description": "an additional valid property is valid",
76 "data": {"foo" : true},
77 "valid": true
78 },
79 {
80 "description": "an additional invalid property is invalid",
81 "data": {"foo" : 1},
82 "valid": false
83 }
84 ]
85 },
86 {
87 "description": "additionalProperties are allowed by default",
88 "schema": {"properties": {"foo": {}, "bar": {}}},
89 "tests": [
90 {
91 "description": "additional properties are allowed",
92 "data": {"foo": 1, "bar": 2, "quux": true},
93 "valid": true
94 }
95 ]
96 }
97 ]
0 [
1 {
2 "description": "allOf",
3 "schema": {
4 "allOf": [
5 {
6 "properties": {
7 "bar": {"type": "integer"}
8 },
9 "required": ["bar"]
10 },
11 {
12 "properties": {
13 "foo": {"type": "string"}
14 },
15 "required": ["foo"]
16 }
17 ]
18 },
19 "tests": [
20 {
21 "description": "allOf",
22 "data": {"foo": "baz", "bar": 2},
23 "valid": true
24 },
25 {
26 "description": "mismatch second",
27 "data": {"foo": "baz"},
28 "valid": false
29 },
30 {
31 "description": "mismatch first",
32 "data": {"bar": 2},
33 "valid": false
34 },
35 {
36 "description": "wrong type",
37 "data": {"foo": "baz", "bar": "quux"},
38 "valid": false
39 }
40 ]
41 },
42 {
43 "description": "allOf with base schema",
44 "schema": {
45 "properties": {"bar": {"type": "integer"}},
46 "required": ["bar"],
47 "allOf" : [
48 {
49 "properties": {
50 "foo": {"type": "string"}
51 },
52 "required": ["foo"]
53 },
54 {
55 "properties": {
56 "baz": {"type": "null"}
57 },
58 "required": ["baz"]
59 }
60 ]
61 },
62 "tests": [
63 {
64 "description": "valid",
65 "data": {"foo": "quux", "bar": 2, "baz": null},
66 "valid": true
67 },
68 {
69 "description": "mismatch base schema",
70 "data": {"foo": "quux", "baz": null},
71 "valid": false
72 },
73 {
74 "description": "mismatch first allOf",
75 "data": {"bar": 2, "baz": null},
76 "valid": false
77 },
78 {
79 "description": "mismatch second allOf",
80 "data": {"foo": "quux", "bar": 2},
81 "valid": false
82 },
83 {
84 "description": "mismatch both",
85 "data": {"bar": 2},
86 "valid": false
87 }
88 ]
89 },
90 {
91 "description": "allOf simple types",
92 "schema": {
93 "allOf": [
94 {"maximum": 30},
95 {"minimum": 20}
96 ]
97 },
98 "tests": [
99 {
100 "description": "valid",
101 "data": 25,
102 "valid": true
103 },
104 {
105 "description": "mismatch one",
106 "data": 35,
107 "valid": false
108 }
109 ]
110 },
111 {
112 "description": "allOf with boolean schemas, all true",
113 "schema": {"allOf": [true, true]},
114 "tests": [
115 {
116 "description": "any value is valid",
117 "data": "foo",
118 "valid": true
119 }
120 ]
121 },
122 {
123 "description": "allOf with boolean schemas, some false",
124 "schema": {"allOf": [true, false]},
125 "tests": [
126 {
127 "description": "any value is invalid",
128 "data": "foo",
129 "valid": false
130 }
131 ]
132 },
133 {
134 "description": "allOf with boolean schemas, all false",
135 "schema": {"allOf": [false, false]},
136 "tests": [
137 {
138 "description": "any value is invalid",
139 "data": "foo",
140 "valid": false
141 }
142 ]
143 }
144 ]
0 [
1 {
2 "description": "anyOf",
3 "schema": {
4 "anyOf": [
5 {
6 "type": "integer"
7 },
8 {
9 "minimum": 2
10 }
11 ]
12 },
13 "tests": [
14 {
15 "description": "first anyOf valid",
16 "data": 1,
17 "valid": true
18 },
19 {
20 "description": "second anyOf valid",
21 "data": 2.5,
22 "valid": true
23 },
24 {
25 "description": "both anyOf valid",
26 "data": 3,
27 "valid": true
28 },
29 {
30 "description": "neither anyOf valid",
31 "data": 1.5,
32 "valid": false
33 }
34 ]
35 },
36 {
37 "description": "anyOf with base schema",
38 "schema": {
39 "type": "string",
40 "anyOf" : [
41 {
42 "maxLength": 2
43 },
44 {
45 "minLength": 4
46 }
47 ]
48 },
49 "tests": [
50 {
51 "description": "mismatch base schema",
52 "data": 3,
53 "valid": false
54 },
55 {
56 "description": "one anyOf valid",
57 "data": "foobar",
58 "valid": true
59 },
60 {
61 "description": "both anyOf invalid",
62 "data": "foo",
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description": "anyOf with boolean schemas, all true",
69 "schema": {"anyOf": [true, true]},
70 "tests": [
71 {
72 "description": "any value is valid",
73 "data": "foo",
74 "valid": true
75 }
76 ]
77 },
78 {
79 "description": "anyOf with boolean schemas, some true",
80 "schema": {"anyOf": [true, false]},
81 "tests": [
82 {
83 "description": "any value is valid",
84 "data": "foo",
85 "valid": true
86 }
87 ]
88 },
89 {
90 "description": "anyOf with boolean schemas, all false",
91 "schema": {"anyOf": [false, false]},
92 "tests": [
93 {
94 "description": "any value is invalid",
95 "data": "foo",
96 "valid": false
97 }
98 ]
99 },
100 {
101 "description": "anyOf complex types",
102 "schema": {
103 "anyOf": [
104 {
105 "properties": {
106 "bar": {"type": "integer"}
107 },
108 "required": ["bar"]
109 },
110 {
111 "properties": {
112 "foo": {"type": "string"}
113 },
114 "required": ["foo"]
115 }
116 ]
117 },
118 "tests": [
119 {
120 "description": "first anyOf valid (complex)",
121 "data": {"bar": 2},
122 "valid": true
123 },
124 {
125 "description": "second anyOf valid (complex)",
126 "data": {"foo": "baz"},
127 "valid": true
128 },
129 {
130 "description": "both anyOf valid (complex)",
131 "data": {"foo": "baz", "bar": 2},
132 "valid": true
133 },
134 {
135 "description": "neither anyOf valid (complex)",
136 "data": {"foo": 2, "bar": "quux"},
137 "valid": false
138 }
139 ]
140 }
141 ]
0 [
1 {
2 "description": "boolean schema 'true'",
3 "schema": true,
4 "tests": [
5 {
6 "description": "number is valid",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "string is valid",
12 "data": "foo",
13 "valid": true
14 },
15 {
16 "description": "boolean true is valid",
17 "data": true,
18 "valid": true
19 },
20 {
21 "description": "boolean false is valid",
22 "data": false,
23 "valid": true
24 },
25 {
26 "description": "null is valid",
27 "data": null,
28 "valid": true
29 },
30 {
31 "description": "object is valid",
32 "data": {"foo": "bar"},
33 "valid": true
34 },
35 {
36 "description": "empty object is valid",
37 "data": {},
38 "valid": true
39 },
40 {
41 "description": "array is valid",
42 "data": ["foo"],
43 "valid": true
44 },
45 {
46 "description": "empty array is valid",
47 "data": [],
48 "valid": true
49 }
50 ]
51 },
52 {
53 "description": "boolean schema 'false'",
54 "schema": false,
55 "tests": [
56 {
57 "description": "number is invalid",
58 "data": 1,
59 "valid": false
60 },
61 {
62 "description": "string is invalid",
63 "data": "foo",
64 "valid": false
65 },
66 {
67 "description": "boolean true is invalid",
68 "data": true,
69 "valid": false
70 },
71 {
72 "description": "boolean false is invalid",
73 "data": false,
74 "valid": false
75 },
76 {
77 "description": "null is invalid",
78 "data": null,
79 "valid": false
80 },
81 {
82 "description": "object is invalid",
83 "data": {"foo": "bar"},
84 "valid": false
85 },
86 {
87 "description": "empty object is invalid",
88 "data": {},
89 "valid": false
90 },
91 {
92 "description": "array is invalid",
93 "data": ["foo"],
94 "valid": false
95 },
96 {
97 "description": "empty array is invalid",
98 "data": [],
99 "valid": false
100 }
101 ]
102 }
103 ]
0 [
1 {
2 "description": "const validation",
3 "schema": {"const": 2},
4 "tests": [
5 {
6 "description": "same value is valid",
7 "data": 2,
8 "valid": true
9 },
10 {
11 "description": "another value is invalid",
12 "data": 5,
13 "valid": false
14 },
15 {
16 "description": "another type is invalid",
17 "data": "a",
18 "valid": false
19 }
20 ]
21 },
22 {
23 "description": "const with object",
24 "schema": {"const": {"foo": "bar", "baz": "bax"}},
25 "tests": [
26 {
27 "description": "same object is valid",
28 "data": {"foo": "bar", "baz": "bax"},
29 "valid": true
30 },
31 {
32 "description": "same object with different property order is valid",
33 "data": {"baz": "bax", "foo": "bar"},
34 "valid": true
35 },
36 {
37 "description": "another object is invalid",
38 "data": {"foo": "bar"},
39 "valid": false
40 },
41 {
42 "description": "another type is invalid",
43 "data": [1, 2],
44 "valid": false
45 }
46 ]
47 },
48 {
49 "description": "const with array",
50 "schema": {"const": [{ "foo": "bar" }]},
51 "tests": [
52 {
53 "description": "same array is valid",
54 "data": [{"foo": "bar"}],
55 "valid": true
56 },
57 {
58 "description": "another array item is invalid",
59 "data": [2],
60 "valid": false
61 },
62 {
63 "description": "array with additional items is invalid",
64 "data": [1, 2, 3],
65 "valid": false
66 }
67 ]
68 },
69 {
70 "description": "const with null",
71 "schema": {"const": null},
72 "tests": [
73 {
74 "description": "null is valid",
75 "data": null,
76 "valid": true
77 },
78 {
79 "description": "not null is invalid",
80 "data": 0,
81 "valid": false
82 }
83 ]
84 }
85 ]
0 [
1 {
2 "description": "contains keyword validation",
3 "schema": {
4 "contains": {"minimum": 5}
5 },
6 "tests": [
7 {
8 "description": "array with item matching schema (5) is valid",
9 "data": [3, 4, 5],
10 "valid": true
11 },
12 {
13 "description": "array with item matching schema (6) is valid",
14 "data": [3, 4, 6],
15 "valid": true
16 },
17 {
18 "description": "array with two items matching schema (5, 6) is valid",
19 "data": [3, 4, 5, 6],
20 "valid": true
21 },
22 {
23 "description": "array without items matching schema is invalid",
24 "data": [2, 3, 4],
25 "valid": false
26 },
27 {
28 "description": "empty array is invalid",
29 "data": [],
30 "valid": false
31 },
32 {
33 "description": "not array is valid",
34 "data": {},
35 "valid": true
36 }
37 ]
38 },
39 {
40 "description": "contains keyword with const keyword",
41 "schema": {
42 "contains": { "const": 5 }
43 },
44 "tests": [
45 {
46 "description": "array with item 5 is valid",
47 "data": [3, 4, 5],
48 "valid": true
49 },
50 {
51 "description": "array with two items 5 is valid",
52 "data": [3, 4, 5, 5],
53 "valid": true
54 },
55 {
56 "description": "array without item 5 is invalid",
57 "data": [1, 2, 3, 4],
58 "valid": false
59 }
60 ]
61 },
62 {
63 "description": "contains keyword with boolean schema true",
64 "schema": {"contains": true},
65 "tests": [
66 {
67 "description": "any non-empty array is valid",
68 "data": ["foo"],
69 "valid": true
70 },
71 {
72 "description": "empty array is invalid",
73 "data": [],
74 "valid": false
75 }
76 ]
77 },
78 {
79 "description": "contains keyword with boolean schema false",
80 "schema": {"contains": false},
81 "tests": [
82 {
83 "description": "any non-empty array is invalid",
84 "data": ["foo"],
85 "valid": false
86 },
87 {
88 "description": "empty array is invalid",
89 "data": [],
90 "valid": false
91 }
92 ]
93 }
94 ]
0 [
1 {
2 "description": "invalid type for default",
3 "schema": {
4 "properties": {
5 "foo": {
6 "type": "integer",
7 "default": []
8 }
9 }
10 },
11 "tests": [
12 {
13 "description": "valid when property is specified",
14 "data": {"foo": 13},
15 "valid": true
16 },
17 {
18 "description": "still valid when the invalid default is used",
19 "data": {},
20 "valid": true
21 }
22 ]
23 },
24 {
25 "description": "invalid string value for default",
26 "schema": {
27 "properties": {
28 "bar": {
29 "type": "string",
30 "minLength": 4,
31 "default": "bad"
32 }
33 }
34 },
35 "tests": [
36 {
37 "description": "valid when property is specified",
38 "data": {"bar": "good"},
39 "valid": true
40 },
41 {
42 "description": "still valid when the invalid default is used",
43 "data": {},
44 "valid": true
45 }
46 ]
47 }
48 ]
0 [
1 {
2 "description": "valid definition",
3 "schema": {"$ref": "http://json-schema.org/draft-07/schema#"},
4 "tests": [
5 {
6 "description": "valid definition schema",
7 "data": {
8 "definitions": {
9 "foo": {"type": "integer"}
10 }
11 },
12 "valid": true
13 }
14 ]
15 },
16 {
17 "description": "invalid definition",
18 "schema": {"$ref": "http://json-schema.org/draft-07/schema#"},
19 "tests": [
20 {
21 "description": "invalid definition schema",
22 "data": {
23 "definitions": {
24 "foo": {"type": 1}
25 }
26 },
27 "valid": false
28 }
29 ]
30 }
31 ]
0 [
1 {
2 "description": "dependencies",
3 "schema": {
4 "dependencies": {"bar": ["foo"]}
5 },
6 "tests": [
7 {
8 "description": "neither",
9 "data": {},
10 "valid": true
11 },
12 {
13 "description": "nondependant",
14 "data": {"foo": 1},
15 "valid": true
16 },
17 {
18 "description": "with dependency",
19 "data": {"foo": 1, "bar": 2},
20 "valid": true
21 },
22 {
23 "description": "missing dependency",
24 "data": {"bar": 2},
25 "valid": false
26 },
27 {
28 "description": "ignores arrays",
29 "data": ["bar"],
30 "valid": true
31 },
32 {
33 "description": "ignores strings",
34 "data": "foobar",
35 "valid": true
36 },
37 {
38 "description": "ignores other non-objects",
39 "data": 12,
40 "valid": true
41 }
42 ]
43 },
44 {
45 "description": "dependencies with empty array",
46 "schema": {
47 "dependencies": {"bar": []}
48 },
49 "tests": [
50 {
51 "description": "empty object",
52 "data": {},
53 "valid": true
54 },
55 {
56 "description": "object with one property",
57 "data": {"bar": 2},
58 "valid": true
59 }
60 ]
61 },
62 {
63 "description": "multiple dependencies",
64 "schema": {
65 "dependencies": {"quux": ["foo", "bar"]}
66 },
67 "tests": [
68 {
69 "description": "neither",
70 "data": {},
71 "valid": true
72 },
73 {
74 "description": "nondependants",
75 "data": {"foo": 1, "bar": 2},
76 "valid": true
77 },
78 {
79 "description": "with dependencies",
80 "data": {"foo": 1, "bar": 2, "quux": 3},
81 "valid": true
82 },
83 {
84 "description": "missing dependency",
85 "data": {"foo": 1, "quux": 2},
86 "valid": false
87 },
88 {
89 "description": "missing other dependency",
90 "data": {"bar": 1, "quux": 2},
91 "valid": false
92 },
93 {
94 "description": "missing both dependencies",
95 "data": {"quux": 1},
96 "valid": false
97 }
98 ]
99 },
100 {
101 "description": "multiple dependencies subschema",
102 "schema": {
103 "dependencies": {
104 "bar": {
105 "properties": {
106 "foo": {"type": "integer"},
107 "bar": {"type": "integer"}
108 }
109 }
110 }
111 },
112 "tests": [
113 {
114 "description": "valid",
115 "data": {"foo": 1, "bar": 2},
116 "valid": true
117 },
118 {
119 "description": "no dependency",
120 "data": {"foo": "quux"},
121 "valid": true
122 },
123 {
124 "description": "wrong type",
125 "data": {"foo": "quux", "bar": 2},
126 "valid": false
127 },
128 {
129 "description": "wrong type other",
130 "data": {"foo": 2, "bar": "quux"},
131 "valid": false
132 },
133 {
134 "description": "wrong type both",
135 "data": {"foo": "quux", "bar": "quux"},
136 "valid": false
137 }
138 ]
139 },
140 {
141 "description": "dependencies with boolean subschemas",
142 "schema": {
143 "dependencies": {
144 "foo": true,
145 "bar": false
146 }
147 },
148 "tests": [
149 {
150 "description": "object with property having schema true is valid",
151 "data": {"foo": 1},
152 "valid": true
153 },
154 {
155 "description": "object with property having schema false is invalid",
156 "data": {"bar": 2},
157 "valid": false
158 },
159 {
160 "description": "object with both properties is invalid",
161 "data": {"foo": 1, "bar": 2},
162 "valid": false
163 },
164 {
165 "description": "empty object is valid",
166 "data": {},
167 "valid": true
168 }
169 ]
170 }
171 ]
0 [
1 {
2 "description": "simple enum validation",
3 "schema": {"enum": [1, 2, 3]},
4 "tests": [
5 {
6 "description": "one of the enum is valid",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "something else is invalid",
12 "data": 4,
13 "valid": false
14 }
15 ]
16 },
17 {
18 "description": "heterogeneous enum validation",
19 "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
20 "tests": [
21 {
22 "description": "one of the enum is valid",
23 "data": [],
24 "valid": true
25 },
26 {
27 "description": "something else is invalid",
28 "data": null,
29 "valid": false
30 },
31 {
32 "description": "objects are deep compared",
33 "data": {"foo": false},
34 "valid": false
35 }
36 ]
37 },
38 {
39 "description": "enums in properties",
40 "schema": {
41 "type":"object",
42 "properties": {
43 "foo": {"enum":["foo"]},
44 "bar": {"enum":["bar"]}
45 },
46 "required": ["bar"]
47 },
48 "tests": [
49 {
50 "description": "both properties are valid",
51 "data": {"foo":"foo", "bar":"bar"},
52 "valid": true
53 },
54 {
55 "description": "missing optional property is valid",
56 "data": {"bar":"bar"},
57 "valid": true
58 },
59 {
60 "description": "missing required property is invalid",
61 "data": {"foo":"foo"},
62 "valid": false
63 },
64 {
65 "description": "missing all properties is invalid",
66 "data": {},
67 "valid": false
68 }
69 ]
70 }
71 ]
0 [
1 {
2 "description": "exclusiveMaximum validation",
3 "schema": {
4 "exclusiveMaximum": 3.0
5 },
6 "tests": [
7 {
8 "description": "below the exclusiveMaximum is valid",
9 "data": 2.2,
10 "valid": true
11 },
12 {
13 "description": "boundary point is invalid",
14 "data": 3.0,
15 "valid": false
16 },
17 {
18 "description": "above the exclusiveMaximum is invalid",
19 "data": 3.5,
20 "valid": false
21 },
22 {
23 "description": "ignores non-numbers",
24 "data": "x",
25 "valid": true
26 }
27 ]
28 }
29 ]
0 [
1 {
2 "description": "exclusiveMinimum validation",
3 "schema": {
4 "exclusiveMinimum": 1.1
5 },
6 "tests": [
7 {
8 "description": "above the exclusiveMinimum is valid",
9 "data": 1.2,
10 "valid": true
11 },
12 {
13 "description": "boundary point is invalid",
14 "data": 1.1,
15 "valid": false
16 },
17 {
18 "description": "below the exclusiveMinimum is invalid",
19 "data": 0.6,
20 "valid": false
21 },
22 {
23 "description": "ignores non-numbers",
24 "data": "x",
25 "valid": true
26 }
27 ]
28 }
29 ]
0 [
1 {
2 "description": "ignore if without then or else",
3 "schema": {
4 "if": {
5 "const": 0
6 }
7 },
8 "tests": [
9 {
10 "description": "valid when valid against lone if",
11 "data": 0,
12 "valid": true
13 },
14 {
15 "description": "valid when invailid against lone if",
16 "data": "hello",
17 "valid": true
18 }
19 ]
20 },
21 {
22 "description": "ignore then without if",
23 "schema": {
24 "then": {
25 "const": 0
26 }
27 },
28 "tests": [
29 {
30 "description": "valid when valid against lone then",
31 "data": 0,
32 "valid": true
33 },
34 {
35 "description": "valid when invailid against lone then",
36 "data": "hello",
37 "valid": true
38 }
39 ]
40 },
41 {
42 "description": "ignore else without if",
43 "schema": {
44 "else": {
45 "const": 0
46 }
47 },
48 "tests": [
49 {
50 "description": "valid when valid against lone else",
51 "data": 0,
52 "valid": true
53 },
54 {
55 "description": "valid when invailid against lone else",
56 "data": "hello",
57 "valid": true
58 }
59 ]
60 },
61 {
62 "description": "if and then without else",
63 "schema": {
64 "if": {
65 "exclusiveMaximum": 0
66 },
67 "then": {
68 "minimum": -10
69 }
70 },
71 "tests": [
72 {
73 "description": "valid through then",
74 "data": -1,
75 "valid": true
76 },
77 {
78 "description": "invalid through then",
79 "data": -100,
80 "valid": false
81 },
82 {
83 "description": "valid when if test fails",
84 "data": 3,
85 "valid": true
86 }
87 ]
88 },
89 {
90 "description": "if and else without then",
91 "schema": {
92 "if": {
93 "exclusiveMaximum": 0
94 },
95 "else": {
96 "multipleOf": 2
97 }
98 },
99 "tests": [
100 {
101 "description": "valid when if test passes",
102 "data": -1,
103 "valid": true
104 },
105 {
106 "description": "valid through else",
107 "data": 4,
108 "valid": true
109 },
110 {
111 "description": "invalid through else",
112 "data": 3,
113 "valid": false
114 }
115 ]
116 },
117 {
118 "description": "validate against correct branch, then vs else",
119 "schema": {
120 "if": {
121 "exclusiveMaximum": 0
122 },
123 "then": {
124 "minimum": -10
125 },
126 "else": {
127 "multipleOf": 2
128 }
129 },
130 "tests": [
131 {
132 "description": "valid through then",
133 "data": -1,
134 "valid": true
135 },
136 {
137 "description": "invalid through then",
138 "data": -100,
139 "valid": false
140 },
141 {
142 "description": "valid through else",
143 "data": 4,
144 "valid": true
145 },
146 {
147 "description": "invalid through else",
148 "data": 3,
149 "valid": false
150 }
151 ]
152 },
153 {
154 "description": "non-interference across combined schemas",
155 "schema": {
156 "allOf": [
157 {
158 "if": {
159 "exclusiveMaximum": 0
160 }
161 },
162 {
163 "then": {
164 "minimum": -10
165 }
166 },
167 {
168 "else": {
169 "multipleOf": 2
170 }
171 }
172 ]
173 },
174 "tests": [
175 {
176 "description": "valid, but woud have been invalid through then",
177 "data": -100,
178 "valid": true
179 },
180 {
181 "description": "valid, but would have been invalid through else",
182 "data": 3,
183 "valid": true
184 }
185 ]
186 }
187 ]
0 [
1 {
2 "description": "a schema given for items",
3 "schema": {
4 "items": {"type": "integer"}
5 },
6 "tests": [
7 {
8 "description": "valid items",
9 "data": [ 1, 2, 3 ],
10 "valid": true
11 },
12 {
13 "description": "wrong type of items",
14 "data": [1, "x"],
15 "valid": false
16 },
17 {
18 "description": "ignores non-arrays",
19 "data": {"foo" : "bar"},
20 "valid": true
21 },
22 {
23 "description": "JavaScript pseudo-array is valid",
24 "data": {
25 "0": "invalid",
26 "length": 1
27 },
28 "valid": true
29 }
30 ]
31 },
32 {
33 "description": "an array of schemas for items",
34 "schema": {
35 "items": [
36 {"type": "integer"},
37 {"type": "string"}
38 ]
39 },
40 "tests": [
41 {
42 "description": "correct types",
43 "data": [ 1, "foo" ],
44 "valid": true
45 },
46 {
47 "description": "wrong types",
48 "data": [ "foo", 1 ],
49 "valid": false
50 },
51 {
52 "description": "incomplete array of items",
53 "data": [ 1 ],
54 "valid": true
55 },
56 {
57 "description": "array with additional items",
58 "data": [ 1, "foo", true ],
59 "valid": true
60 },
61 {
62 "description": "empty array",
63 "data": [ ],
64 "valid": true
65 },
66 {
67 "description": "JavaScript pseudo-array is valid",
68 "data": {
69 "0": "invalid",
70 "1": "valid",
71 "length": 2
72 },
73 "valid": true
74 }
75 ]
76 },
77 {
78 "description": "items with boolean schema (true)",
79 "schema": {"items": true},
80 "tests": [
81 {
82 "description": "any array is valid",
83 "data": [ 1, "foo", true ],
84 "valid": true
85 },
86 {
87 "description": "empty array is valid",
88 "data": [],
89 "valid": true
90 }
91 ]
92 },
93 {
94 "description": "items with boolean schema (false)",
95 "schema": {"items": false},
96 "tests": [
97 {
98 "description": "any non-empty array is invalid",
99 "data": [ 1, "foo", true ],
100 "valid": false
101 },
102 {
103 "description": "empty array is valid",
104 "data": [],
105 "valid": true
106 }
107 ]
108 },
109 {
110 "description": "items with boolean schemas",
111 "schema": {
112 "items": [true, false]
113 },
114 "tests": [
115 {
116 "description": "array with one item is valid",
117 "data": [ 1 ],
118 "valid": true
119 },
120 {
121 "description": "array with two items is invalid",
122 "data": [ 1, "foo" ],
123 "valid": false
124 },
125 {
126 "description": "empty array is valid",
127 "data": [],
128 "valid": true
129 }
130 ]
131 }
132 ]
0 [
1 {
2 "description": "maxItems validation",
3 "schema": {"maxItems": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": [1],
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": [1, 2],
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": [1, 2, 3],
18 "valid": false
19 },
20 {
21 "description": "ignores non-arrays",
22 "data": "foobar",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "maxLength validation",
3 "schema": {"maxLength": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": "f",
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": "fo",
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": "foo",
18 "valid": false
19 },
20 {
21 "description": "ignores non-strings",
22 "data": 100,
23 "valid": true
24 },
25 {
26 "description": "two supplementary Unicode code points is long enough",
27 "data": "\uD83D\uDCA9\uD83D\uDCA9",
28 "valid": true
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "maxProperties validation",
3 "schema": {"maxProperties": 2},
4 "tests": [
5 {
6 "description": "shorter is valid",
7 "data": {"foo": 1},
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": {"foo": 1, "bar": 2},
13 "valid": true
14 },
15 {
16 "description": "too long is invalid",
17 "data": {"foo": 1, "bar": 2, "baz": 3},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [1, 2, 3],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "foobar",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 }
35 ]
36 }
37 ]
0 [
1 {
2 "description": "maximum validation",
3 "schema": {"maximum": 3.0},
4 "tests": [
5 {
6 "description": "below the maximum is valid",
7 "data": 2.6,
8 "valid": true
9 },
10 {
11 "description": "boundary point is valid",
12 "data": 3.0,
13 "valid": true
14 },
15 {
16 "description": "above the maximum is invalid",
17 "data": 3.5,
18 "valid": false
19 },
20 {
21 "description": "ignores non-numbers",
22 "data": "x",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "minItems validation",
3 "schema": {"minItems": 1},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": [1, 2],
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": [1],
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": [],
18 "valid": false
19 },
20 {
21 "description": "ignores non-arrays",
22 "data": "",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "minLength validation",
3 "schema": {"minLength": 2},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": "foo",
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": "fo",
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": "f",
18 "valid": false
19 },
20 {
21 "description": "ignores non-strings",
22 "data": 1,
23 "valid": true
24 },
25 {
26 "description": "one supplementary Unicode code point is not long enough",
27 "data": "\uD83D\uDCA9",
28 "valid": false
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "minProperties validation",
3 "schema": {"minProperties": 1},
4 "tests": [
5 {
6 "description": "longer is valid",
7 "data": {"foo": 1, "bar": 2},
8 "valid": true
9 },
10 {
11 "description": "exact length is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "too short is invalid",
17 "data": {},
18 "valid": false
19 },
20 {
21 "description": "ignores arrays",
22 "data": [],
23 "valid": true
24 },
25 {
26 "description": "ignores strings",
27 "data": "",
28 "valid": true
29 },
30 {
31 "description": "ignores other non-objects",
32 "data": 12,
33 "valid": true
34 }
35 ]
36 }
37 ]
0 [
1 {
2 "description": "minimum validation",
3 "schema": {"minimum": 1.1},
4 "tests": [
5 {
6 "description": "above the minimum is valid",
7 "data": 2.6,
8 "valid": true
9 },
10 {
11 "description": "boundary point is valid",
12 "data": 1.1,
13 "valid": true
14 },
15 {
16 "description": "below the minimum is invalid",
17 "data": 0.6,
18 "valid": false
19 },
20 {
21 "description": "ignores non-numbers",
22 "data": "x",
23 "valid": true
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "by int",
3 "schema": {"multipleOf": 2},
4 "tests": [
5 {
6 "description": "int by int",
7 "data": 10,
8 "valid": true
9 },
10 {
11 "description": "int by int fail",
12 "data": 7,
13 "valid": false
14 },
15 {
16 "description": "ignores non-numbers",
17 "data": "foo",
18 "valid": true
19 }
20 ]
21 },
22 {
23 "description": "by number",
24 "schema": {"multipleOf": 1.5},
25 "tests": [
26 {
27 "description": "zero is multiple of anything",
28 "data": 0,
29 "valid": true
30 },
31 {
32 "description": "4.5 is multiple of 1.5",
33 "data": 4.5,
34 "valid": true
35 },
36 {
37 "description": "35 is not multiple of 1.5",
38 "data": 35,
39 "valid": false
40 }
41 ]
42 },
43 {
44 "description": "by small number",
45 "schema": {"multipleOf": 0.0001},
46 "tests": [
47 {
48 "description": "0.0075 is multiple of 0.0001",
49 "data": 0.0075,
50 "valid": true
51 },
52 {
53 "description": "0.00751 is not multiple of 0.0001",
54 "data": 0.00751,
55 "valid": false
56 }
57 ]
58 }
59 ]
0 [
1 {
2 "description": "not",
3 "schema": {
4 "not": {"type": "integer"}
5 },
6 "tests": [
7 {
8 "description": "allowed",
9 "data": "foo",
10 "valid": true
11 },
12 {
13 "description": "disallowed",
14 "data": 1,
15 "valid": false
16 }
17 ]
18 },
19 {
20 "description": "not multiple types",
21 "schema": {
22 "not": {"type": ["integer", "boolean"]}
23 },
24 "tests": [
25 {
26 "description": "valid",
27 "data": "foo",
28 "valid": true
29 },
30 {
31 "description": "mismatch",
32 "data": 1,
33 "valid": false
34 },
35 {
36 "description": "other mismatch",
37 "data": true,
38 "valid": false
39 }
40 ]
41 },
42 {
43 "description": "not more complex schema",
44 "schema": {
45 "not": {
46 "type": "object",
47 "properties": {
48 "foo": {
49 "type": "string"
50 }
51 }
52 }
53 },
54 "tests": [
55 {
56 "description": "match",
57 "data": 1,
58 "valid": true
59 },
60 {
61 "description": "other match",
62 "data": {"foo": 1},
63 "valid": true
64 },
65 {
66 "description": "mismatch",
67 "data": {"foo": "bar"},
68 "valid": false
69 }
70 ]
71 },
72 {
73 "description": "forbidden property",
74 "schema": {
75 "properties": {
76 "foo": {
77 "not": {}
78 }
79 }
80 },
81 "tests": [
82 {
83 "description": "property present",
84 "data": {"foo": 1, "bar": 2},
85 "valid": false
86 },
87 {
88 "description": "property absent",
89 "data": {"bar": 1, "baz": 2},
90 "valid": true
91 }
92 ]
93 },
94 {
95 "description": "not with boolean schema true",
96 "schema": {"not": true},
97 "tests": [
98 {
99 "description": "any value is invalid",
100 "data": "foo",
101 "valid": false
102 }
103 ]
104 },
105 {
106 "description": "not with boolean schema false",
107 "schema": {"not": false},
108 "tests": [
109 {
110 "description": "any value is valid",
111 "data": "foo",
112 "valid": true
113 }
114 ]
115 }
116 ]
0 [
1 {
2 "description": "oneOf",
3 "schema": {
4 "oneOf": [
5 {
6 "type": "integer"
7 },
8 {
9 "minimum": 2
10 }
11 ]
12 },
13 "tests": [
14 {
15 "description": "first oneOf valid",
16 "data": 1,
17 "valid": true
18 },
19 {
20 "description": "second oneOf valid",
21 "data": 2.5,
22 "valid": true
23 },
24 {
25 "description": "both oneOf valid",
26 "data": 3,
27 "valid": false
28 },
29 {
30 "description": "neither oneOf valid",
31 "data": 1.5,
32 "valid": false
33 }
34 ]
35 },
36 {
37 "description": "oneOf with base schema",
38 "schema": {
39 "type": "string",
40 "oneOf" : [
41 {
42 "minLength": 2
43 },
44 {
45 "maxLength": 4
46 }
47 ]
48 },
49 "tests": [
50 {
51 "description": "mismatch base schema",
52 "data": 3,
53 "valid": false
54 },
55 {
56 "description": "one oneOf valid",
57 "data": "foobar",
58 "valid": true
59 },
60 {
61 "description": "both oneOf valid",
62 "data": "foo",
63 "valid": false
64 }
65 ]
66 },
67 {
68 "description": "oneOf with boolean schemas, all true",
69 "schema": {"oneOf": [true, true, true]},
70 "tests": [
71 {
72 "description": "any value is invalid",
73 "data": "foo",
74 "valid": false
75 }
76 ]
77 },
78 {
79 "description": "oneOf with boolean schemas, one true",
80 "schema": {"oneOf": [true, false, false]},
81 "tests": [
82 {
83 "description": "any value is valid",
84 "data": "foo",
85 "valid": true
86 }
87 ]
88 },
89 {
90 "description": "oneOf with boolean schemas, more than one true",
91 "schema": {"oneOf": [true, true, false]},
92 "tests": [
93 {
94 "description": "any value is invalid",
95 "data": "foo",
96 "valid": false
97 }
98 ]
99 },
100 {
101 "description": "oneOf with boolean schemas, all false",
102 "schema": {"oneOf": [false, false, false]},
103 "tests": [
104 {
105 "description": "any value is invalid",
106 "data": "foo",
107 "valid": false
108 }
109 ]
110 },
111 {
112 "description": "oneOf complex types",
113 "schema": {
114 "oneOf": [
115 {
116 "properties": {
117 "bar": {"type": "integer"}
118 },
119 "required": ["bar"]
120 },
121 {
122 "properties": {
123 "foo": {"type": "string"}
124 },
125 "required": ["foo"]
126 }
127 ]
128 },
129 "tests": [
130 {
131 "description": "first oneOf valid (complex)",
132 "data": {"bar": 2},
133 "valid": true
134 },
135 {
136 "description": "second oneOf valid (complex)",
137 "data": {"foo": "baz"},
138 "valid": true
139 },
140 {
141 "description": "both oneOf valid (complex)",
142 "data": {"foo": "baz", "bar": 2},
143 "valid": false
144 },
145 {
146 "description": "neither oneOf valid (complex)",
147 "data": {"foo": 2, "bar": "quux"},
148 "valid": false
149 }
150 ]
151 }
152 ]
0 [
1 {
2 "description": "integer",
3 "schema": {"type": "integer"},
4 "tests": [
5 {
6 "description": "a bignum is an integer",
7 "data": 12345678910111213141516171819202122232425262728293031,
8 "valid": true
9 }
10 ]
11 },
12 {
13 "description": "number",
14 "schema": {"type": "number"},
15 "tests": [
16 {
17 "description": "a bignum is a number",
18 "data": 98249283749234923498293171823948729348710298301928331,
19 "valid": true
20 }
21 ]
22 },
23 {
24 "description": "integer",
25 "schema": {"type": "integer"},
26 "tests": [
27 {
28 "description": "a negative bignum is an integer",
29 "data": -12345678910111213141516171819202122232425262728293031,
30 "valid": true
31 }
32 ]
33 },
34 {
35 "description": "number",
36 "schema": {"type": "number"},
37 "tests": [
38 {
39 "description": "a negative bignum is a number",
40 "data": -98249283749234923498293171823948729348710298301928331,
41 "valid": true
42 }
43 ]
44 },
45 {
46 "description": "string",
47 "schema": {"type": "string"},
48 "tests": [
49 {
50 "description": "a bignum is not a string",
51 "data": 98249283749234923498293171823948729348710298301928331,
52 "valid": false
53 }
54 ]
55 },
56 {
57 "description": "integer comparison",
58 "schema": {"maximum": 18446744073709551615},
59 "tests": [
60 {
61 "description": "comparison works for high numbers",
62 "data": 18446744073709551600,
63 "valid": true
64 }
65 ]
66 },
67 {
68 "description": "float comparison with high precision",
69 "schema": {
70 "exclusiveMaximum": 972783798187987123879878123.18878137
71 },
72 "tests": [
73 {
74 "description": "comparison works for high numbers",
75 "data": 972783798187987123879878123.188781371,
76 "valid": false
77 }
78 ]
79 },
80 {
81 "description": "integer comparison",
82 "schema": {"minimum": -18446744073709551615},
83 "tests": [
84 {
85 "description": "comparison works for very negative numbers",
86 "data": -18446744073709551600,
87 "valid": true
88 }
89 ]
90 },
91 {
92 "description": "float comparison with high precision on negative numbers",
93 "schema": {
94 "exclusiveMinimum": -972783798187987123879878123.18878137
95 },
96 "tests": [
97 {
98 "description": "comparison works for very negative numbers",
99 "data": -972783798187987123879878123.188781371,
100 "valid": false
101 }
102 ]
103 }
104 ]
0 [
1 {
2 "description": "validation of string-encoded content based on media type",
3 "schema": {
4 "contentMediaType": "application/json"
5 },
6 "tests": [
7 {
8 "description": "a valid JSON document",
9 "data": "{\"foo\": \"bar\"}",
10 "valid": true
11 },
12 {
13 "description": "an invalid JSON document",
14 "data": "{:}",
15 "valid": false
16 }
17 ]
18 },
19 {
20 "description": "validation of binary string-encoding",
21 "schema": {
22 "contentEncoding": "base64"
23 },
24 "tests": [
25 {
26 "description": "a valid base64 string",
27 "data": "eyJmb28iOiAiYmFyIn0K",
28 "valid": true
29 },
30 {
31 "description": "an invalid base64 string (% is not a valid character)",
32 "data": "eyJmb28iOi%iYmFyIn0K",
33 "valid": false
34 }
35 ]
36 },
37 {
38 "description": "validation of binary-encoded media type documents",
39 "schema": {
40 "contentMediaType": "application/json",
41 "contentEncoding": "base64"
42 },
43 "tests": [
44 {
45 "description": "a valid base64-encoded JSON document",
46 "data": "eyJmb28iOiAiYmFyIn0K",
47 "valid": true
48 },
49 {
50 "description": "a validly-encoded invalid JSON document",
51 "data": "ezp9Cg==",
52 "valid": false
53 },
54 {
55 "description": "an invalid base64 string that is valid JSON",
56 "data": "{}",
57 "valid": false
58 }
59 ]
60 }
61 ]
0 [
1 {
2 "description": "ECMA 262 regex non-compliance",
3 "schema": { "format": "regex" },
4 "tests": [
5 {
6 "description": "ECMA 262 has no support for \\Z anchor from .NET",
7 "data": "^\\S(|(.|\\n)*\\S)\\Z",
8 "valid": false
9 }
10 ]
11 }
12 ]
0 [
1 {
2 "description": "validation of date-time strings",
3 "schema": {"format": "date-time"},
4 "tests": [
5 {
6 "description": "a valid date-time string",
7 "data": "1963-06-19T08:30:06.283185Z",
8 "valid": true
9 },
10 {
11 "description": "an invalid date-time string",
12 "data": "06/19/1963 08:30:06 PST",
13 "valid": false
14 },
15 {
16 "description": "only RFC3339 not all of ISO 8601 are valid",
17 "data": "2013-350T01:01:01",
18 "valid": false
19 }
20 ]
21 }
22 ]
0 [
1 {
2 "description": "validation of date strings",
3 "schema": {"format": "date"},
4 "tests": [
5 {
6 "description": "a valid date string",
7 "data": "1963-06-19",
8 "valid": true
9 },
10 {
11 "description": "an invalid date-time string",
12 "data": "06/19/1963",
13 "valid": false
14 },
15 {
16 "description": "only RFC3339 not all of ISO 8601 are valid",
17 "data": "2013-350",
18 "valid": false
19 }
20 ]
21 }
22 ]
0 [
1 {
2 "description": "validation of e-mail addresses",
3 "schema": {"format": "email"},
4 "tests": [
5 {
6 "description": "a valid e-mail address",
7 "data": "joe.bloggs@example.com",
8 "valid": true
9 },
10 {
11 "description": "an invalid e-mail address",
12 "data": "2962",
13 "valid": false
14 }
15 ]
16 }
17 ]
0 [
1 {
2 "description": "validation of host names",
3 "schema": {"format": "hostname"},
4 "tests": [
5 {
6 "description": "a valid host name",
7 "data": "www.example.com",
8 "valid": true
9 },
10 {
11 "description": "a valid punycoded IDN hostname",
12 "data": "xn--4gbwdl.xn--wgbh1c",
13 "valid": true
14 },
15 {
16 "description": "a host name starting with an illegal character",
17 "data": "-a-host-name-that-starts-with--",
18 "valid": false
19 },
20 {
21 "description": "a host name containing illegal characters",
22 "data": "not_a_valid_host_name",
23 "valid": false
24 },
25 {
26 "description": "a host name with a component too long",
27 "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
28 "valid": false
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "validation of an internationalized e-mail addresses",
3 "schema": {"format": "idn-email"},
4 "tests": [
5 {
6 "description": "a valid idn e-mail (example@example.test in Hangul)",
7 "data": "실례@실례.테스트",
8 "valid": true
9 },
10 {
11 "description": "an invalid idn e-mail address",
12 "data": "2962",
13 "valid": false
14 }
15 ]
16 }
17 ]
0 [
1 {
2 "description": "validation of internationalized host names",
3 "schema": {"format": "idn-hostname"},
4 "disabled" : true,
5 "tests": [
6 {
7 "description": "a valid host name (example.test in Hangul)",
8 "data": "실례.테스트",
9 "valid": true
10 },
11 {
12 "description": "illegal first char U+302E Hangul single dot tone mark",
13 "data": "〮실례.테스트",
14 "valid": false
15 },
16 {
17 "description": "contains illegal char U+302E Hangul single dot tone mark",
18 "data": "실〮례.테스트",
19 "valid": false
20 },
21 {
22 "description": "a host name with a component too long",
23 "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트",
24 "valid": false
25 }
26 ]
27 }
28 ]
0 [
1 {
2 "description": "validation of IP addresses",
3 "schema": {"format": "ipv4"},
4 "tests": [
5 {
6 "description": "a valid IP address",
7 "data": "192.168.0.1",
8 "valid": true
9 },
10 {
11 "description": "an IP address with too many components",
12 "data": "127.0.0.0.1",
13 "valid": false
14 },
15 {
16 "description": "an IP address with out-of-range values",
17 "data": "256.256.256.256",
18 "valid": false
19 },
20 {
21 "description": "an IP address without 4 components",
22 "data": "127.0",
23 "valid": false
24 },
25 {
26 "description": "an IP address as an integer",
27 "data": "0x7f000001",
28 "valid": false
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "validation of IPv6 addresses",
3 "schema": {"format": "ipv6"},
4 "tests": [
5 {
6 "description": "a valid IPv6 address",
7 "data": "::1",
8 "valid": true
9 },
10 {
11 "description": "an IPv6 address with out-of-range values",
12 "data": "12345::",
13 "valid": false
14 },
15 {
16 "description": "an IPv6 address with too many components",
17 "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
18 "valid": false
19 },
20 {
21 "description": "an IPv6 address containing illegal characters",
22 "data": "::laptop",
23 "valid": false
24 }
25 ]
26 }
27 ]
0 [
1 {
2 "description": "validation of IRI References",
3 "schema": {"format": "iri-reference"},
4 "tests": [
5 {
6 "description": "a valid IRI",
7 "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
8 "valid": true
9 },
10 {
11 "description": "a valid protocol-relative IRI Reference",
12 "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx",
13 "valid": true
14 },
15 {
16 "description": "a valid relative IRI Reference",
17 "data": "/âππ",
18 "valid": true
19 },
20 {
21 "description": "an invalid IRI Reference",
22 "data": "\\\\WINDOWS\\filëßåré",
23 "valid": false
24 },
25 {
26 "description": "a valid IRI Reference",
27 "data": "âππ",
28 "valid": true
29 },
30 {
31 "description": "a valid IRI fragment",
32 "data": "#ƒrägmênt",
33 "valid": true
34 },
35 {
36 "description": "an invalid IRI fragment",
37 "data": "#ƒräg\\mênt",
38 "valid": false
39 }
40 ]
41 }
42 ]
0 [
1 {
2 "description": "validation of IRIs",
3 "schema": {"format": "iri"},
4 "tests": [
5 {
6 "description": "a valid IRI with anchor tag",
7 "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
8 "valid": true
9 },
10 {
11 "description": "a valid IRI with anchor tag and parantheses",
12 "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1",
13 "valid": true
14 },
15 {
16 "description": "a valid IRI with URL-encoded stuff",
17 "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff",
18 "valid": true
19 },
20 {
21 "description": "a valid IRI with many special characters",
22 "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
23 "valid": true
24 },
25 {
26 "description": "a valid IRI based on IPv6",
27 "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334",
28 "valid": true
29 },
30 {
31 "description": "an invalid relative IRI Reference",
32 "data": "/abc",
33 "valid": false
34 },
35 {
36 "description": "an invalid IRI",
37 "data": "\\\\WINDOWS\\filëßåré",
38 "valid": false
39 },
40 {
41 "description": "an invalid IRI though valid IRI reference",
42 "data": "âππ",
43 "valid": false
44 }
45 ]
46 }
47 ]
0 [
1 {
2 "description": "validation of JSON-pointers (JSON String Representation)",
3 "schema": {"format": "json-pointer"},
4 "tests": [
5 {
6 "description": "a valid JSON-pointer",
7 "data": "/foo/bar~0/baz~1/%a",
8 "valid": true
9 },
10 {
11 "description": "not a valid JSON-pointer (~ not escaped)",
12 "data": "/foo/bar~",
13 "valid": false
14 },
15 {
16 "description": "valid JSON-pointer with empty segment",
17 "data": "/foo//bar",
18 "valid": true
19 },
20 {
21 "description": "valid JSON-pointer with the last empty segment",
22 "data": "/foo/bar/",
23 "valid": true
24 },
25 {
26 "description": "valid JSON-pointer as stated in RFC 6901 #1",
27 "data": "",
28 "valid": true
29 },
30 {
31 "description": "valid JSON-pointer as stated in RFC 6901 #2",
32 "data": "/foo",
33 "valid": true
34 },
35 {
36 "description": "valid JSON-pointer as stated in RFC 6901 #3",
37 "data": "/foo/0",
38 "valid": true
39 },
40 {
41 "description": "valid JSON-pointer as stated in RFC 6901 #4",
42 "data": "/",
43 "valid": true
44 },
45 {
46 "description": "valid JSON-pointer as stated in RFC 6901 #5",
47 "data": "/a~1b",
48 "valid": true
49 },
50 {
51 "description": "valid JSON-pointer as stated in RFC 6901 #6",
52 "data": "/c%d",
53 "valid": true
54 },
55 {
56 "description": "valid JSON-pointer as stated in RFC 6901 #7",
57 "data": "/e^f",
58 "valid": true
59 },
60 {
61 "description": "valid JSON-pointer as stated in RFC 6901 #8",
62 "data": "/g|h",
63 "valid": true
64 },
65 {
66 "description": "valid JSON-pointer as stated in RFC 6901 #9",
67 "data": "/i\\j",
68 "valid": true
69 },
70 {
71 "description": "valid JSON-pointer as stated in RFC 6901 #10",
72 "data": "/k\"l",
73 "valid": true
74 },
75 {
76 "description": "valid JSON-pointer as stated in RFC 6901 #11",
77 "data": "/ ",
78 "valid": true
79 },
80 {
81 "description": "valid JSON-pointer as stated in RFC 6901 #12",
82 "data": "/m~0n",
83 "valid": true
84 },
85 {
86 "description": "valid JSON-pointer used adding to the last array position",
87 "data": "/foo/-",
88 "valid": true
89 },
90 {
91 "description": "valid JSON-pointer (- used as object member name)",
92 "data": "/foo/-/bar",
93 "valid": true
94 },
95 {
96 "description": "valid JSON-pointer (multiple escaped characters)",
97 "data": "/~1~0~0~1~1",
98 "valid": true
99 },
100 {
101 "description": "valid JSON-pointer (escaped with fraction part) #1",
102 "data": "/~1.1",
103 "valid": true
104 },
105 {
106 "description": "valid JSON-pointer (escaped with fraction part) #2",
107 "data": "/~0.1",
108 "valid": true
109 },
110 {
111 "description": "not a valid JSON-pointer (URI Fragment Identifier) #1",
112 "data": "#",
113 "valid": false
114 },
115 {
116 "description": "not a valid JSON-pointer (URI Fragment Identifier) #2",
117 "data": "#/",
118 "valid": false
119 },
120 {
121 "description": "not a valid JSON-pointer (URI Fragment Identifier) #3",
122 "data": "#a",
123 "valid": false
124 },
125 {
126 "description": "not a valid JSON-pointer (some escaped, but not all) #1",
127 "data": "/~0~",
128 "valid": false
129 },
130 {
131 "description": "not a valid JSON-pointer (some escaped, but not all) #2",
132 "data": "/~0/~",
133 "valid": false
134 },
135 {
136 "description": "not a valid JSON-pointer (wrong escape character) #1",
137 "data": "/~2",
138 "valid": false
139 },
140 {
141 "description": "not a valid JSON-pointer (wrong escape character) #2",
142 "data": "/~-1",
143 "valid": false
144 },
145 {
146 "description": "not a valid JSON-pointer (multiple characters not escaped)",
147 "data": "/~~",
148 "valid": false
149 },
150 {
151 "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1",
152 "data": "a",
153 "valid": false
154 },
155 {
156 "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2",
157 "data": "0",
158 "valid": false
159 },
160 {
161 "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3",
162 "data": "a/a",
163 "valid": false
164 }
165 ]
166 }
167 ]
0 [
1 {
2 "description": "validation of regular expressions",
3 "schema": {"format": "regex"},
4 "tests": [
5 {
6 "description": "a valid regular expression",
7 "data": "([abc])+\\s+$",
8 "valid": true
9 },
10 {
11 "description": "a regular expression with unclosed parens is invalid",
12 "data": "^(abc]",
13 "valid": false
14 }
15 ]
16 }
17 ]
0 [
1 {
2 "description": "validation of Relative JSON Pointers (RJP)",
3 "schema": {"format": "relative-json-pointer"},
4 "tests": [
5 {
6 "description": "a valid upwards RJP",
7 "data": "1",
8 "valid": true
9 },
10 {
11 "description": "a valid downwards RJP",
12 "data": "0/foo/bar",
13 "valid": true
14 },
15 {
16 "description": "a valid up and then down RJP, with array index",
17 "data": "2/0/baz/1/zip",
18 "valid": true
19 },
20 {
21 "description": "a valid RJP taking the member or index name",
22 "data": "0#",
23 "valid": true
24 },
25 {
26 "description": "an invalid RJP that is a valid JSON Pointer",
27 "data": "/foo/bar",
28 "valid": false
29 }
30 ]
31 }
32 ]
0 [
1 {
2 "description": "validation of time strings",
3 "schema": {"format": "time"},
4 "tests": [
5 {
6 "description": "a valid time string",
7 "data": "08:30:06.283185Z",
8 "valid": true
9 },
10 {
11 "description": "an invalid time string",
12 "data": "08:30:06 PST",
13 "valid": false
14 },
15 {
16 "description": "only RFC3339 not all of ISO 8601 are valid",
17 "data": "01:01:01,1111",
18 "valid": false
19 }
20 ]
21 }
22 ]
0 [
1 {
2 "description": "validation of URI References",
3 "schema": {"format": "uri-reference"},
4 "tests": [
5 {
6 "description": "a valid URI",
7 "data": "http://foo.bar/?baz=qux#quux",
8 "valid": true
9 },
10 {
11 "description": "a valid protocol-relative URI Reference",
12 "data": "//foo.bar/?baz=qux#quux",
13 "valid": true
14 },
15 {
16 "description": "a valid relative URI Reference",
17 "data": "/abc",
18 "valid": true
19 },
20 {
21 "description": "an invalid URI Reference",
22 "data": "\\\\WINDOWS\\fileshare",
23 "valid": false
24 },
25 {
26 "description": "a valid URI Reference",
27 "data": "abc",
28 "valid": true
29 },
30 {
31 "description": "a valid URI fragment",
32 "data": "#fragment",
33 "valid": true
34 },
35 {
36 "description": "an invalid URI fragment",
37 "data": "#frag\\ment",
38 "valid": false
39 }
40 ]
41 }
42 ]
0 [
1 {
2 "description": "format: uri-template",
3 "schema": {
4 "format": "uri-template"
5 },
6 "tests": [
7 {
8 "description": "a valid uri-template",
9 "data": "http://example.com/dictionary/{term:1}/{term}",
10 "valid": true
11 },
12 {
13 "description": "an invalid uri-template",
14 "data": "http://example.com/dictionary/{term:1}/{term",
15 "valid": false
16 },
17 {
18 "description": "a valid uri-template without variables",
19 "data": "http://example.com/dictionary",
20 "valid": true
21 },
22 {
23 "description": "a valid relative uri-template",
24 "data": "dictionary/{term:1}/{term}",
25 "valid": true
26 }
27 ]
28 }
29 ]
0 [
1 {
2 "description": "validation of URIs",
3 "schema": {"format": "uri"},
4 "tests": [
5 {
6 "description": "a valid URL with anchor tag",
7 "data": "http://foo.bar/?baz=qux#quux",
8 "valid": true
9 },
10 {
11 "description": "a valid URL with anchor tag and parantheses",
12 "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
13 "valid": true
14 },
15 {
16 "description": "a valid URL with URL-encoded stuff",
17 "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
18 "valid": true
19 },
20 {
21 "description": "a valid puny-coded URL ",
22 "data": "http://xn--nw2a.xn--j6w193g/",
23 "valid": true
24 },
25 {
26 "description": "a valid URL with many special characters",
27 "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
28 "valid": true
29 },
30 {
31 "description": "a valid URL based on IPv4",
32 "data": "http://223.255.255.254",
33 "valid": true
34 },
35 {
36 "description": "a valid URL with ftp scheme",
37 "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
38 "valid": true
39 },
40 {
41 "description": "a valid URL for a simple text file",
42 "data": "http://www.ietf.org/rfc/rfc2396.txt",
43 "valid": true
44 },
45 {
46 "description": "a valid URL ",
47 "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
48 "valid": true
49 },
50 {
51 "description": "a valid mailto URI",
52 "data": "mailto:John.Doe@example.com",
53 "valid": true
54 },
55 {
56 "description": "a valid newsgroup URI",
57 "data": "news:comp.infosystems.www.servers.unix",
58 "valid": true
59 },
60 {
61 "description": "a valid tel URI",
62 "data": "tel:+1-816-555-1212",
63 "valid": true
64 },
65 {
66 "description": "a valid URN",
67 "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
68 "valid": true
69 },
70 {
71 "description": "an invalid protocol-relative URI Reference",
72 "data": "//foo.bar/?baz=qux#quux",
73 "valid": false
74 },
75 {
76 "description": "an invalid relative URI Reference",
77 "data": "/abc",
78 "valid": false
79 },
80 {
81 "description": "an invalid URI",
82 "data": "\\\\WINDOWS\\fileshare",
83 "valid": false
84 },
85 {
86 "description": "an invalid URI though valid URI reference",
87 "data": "abc",
88 "valid": false
89 },
90 {
91 "description": "an invalid URI with spaces",
92 "data": "http:// shouldfail.com",
93 "valid": false
94 },
95 {
96 "description": "an invalid URI with spaces and missing scheme",
97 "data": ":// should fail",
98 "valid": false
99 }
100 ]
101 }
102 ]
0 [
1 {
2 "description": "some languages do not distinguish between different types of numeric value",
3 "schema": {
4 "type": "integer"
5 },
6 "tests": [
7 {
8 "description": "a float without fractional part is an integer",
9 "data": 1.0,
10 "valid": true
11 }
12 ]
13 }
14 ]
0 [
1 {
2 "description": "pattern validation",
3 "schema": {"pattern": "^a*$"},
4 "tests": [
5 {
6 "description": "a matching pattern is valid",
7 "data": "aaa",
8 "valid": true
9 },
10 {
11 "description": "a non-matching pattern is invalid",
12 "data": "abc",
13 "valid": false
14 },
15 {
16 "description": "ignores non-strings",
17 "data": true,
18 "valid": true
19 }
20 ]
21 },
22 {
23 "description": "pattern is not anchored",
24 "schema": {"pattern": "a+"},
25 "tests": [
26 {
27 "description": "matches a substring",
28 "data": "xxaayy",
29 "valid": true
30 }
31 ]
32 }
33 ]
0 [
1 {
2 "description":
3 "patternProperties validates properties matching a regex",
4 "schema": {
5 "patternProperties": {
6 "f.*o": {"type": "integer"}
7 }
8 },
9 "tests": [
10 {
11 "description": "a single valid match is valid",
12 "data": {"foo": 1},
13 "valid": true
14 },
15 {
16 "description": "multiple valid matches is valid",
17 "data": {"foo": 1, "foooooo" : 2},
18 "valid": true
19 },
20 {
21 "description": "a single invalid match is invalid",
22 "data": {"foo": "bar", "fooooo": 2},
23 "valid": false
24 },
25 {
26 "description": "multiple invalid matches is invalid",
27 "data": {"foo": "bar", "foooooo" : "baz"},
28 "valid": false
29 },
30 {
31 "description": "ignores arrays",
32 "data": ["foo"],
33 "valid": true
34 },
35 {
36 "description": "ignores strings",
37 "data": "foo",
38 "valid": true
39 },
40 {
41 "description": "ignores other non-objects",
42 "data": 12,
43 "valid": true
44 }
45 ]
46 },
47 {
48 "description": "multiple simultaneous patternProperties are validated",
49 "schema": {
50 "patternProperties": {
51 "a*": {"type": "integer"},
52 "aaa*": {"maximum": 20}
53 }
54 },
55 "tests": [
56 {
57 "description": "a single valid match is valid",
58 "data": {"a": 21},
59 "valid": true
60 },
61 {
62 "description": "a simultaneous match is valid",
63 "data": {"aaaa": 18},
64 "valid": true
65 },
66 {
67 "description": "multiple matches is valid",
68 "data": {"a": 21, "aaaa": 18},
69 "valid": true
70 },
71 {
72 "description": "an invalid due to one is invalid",
73 "data": {"a": "bar"},
74 "valid": false
75 },
76 {
77 "description": "an invalid due to the other is invalid",
78 "data": {"aaaa": 31},
79 "valid": false
80 },
81 {
82 "description": "an invalid due to both is invalid",
83 "data": {"aaa": "foo", "aaaa": 31},
84 "valid": false
85 }
86 ]
87 },
88 {
89 "description": "regexes are not anchored by default and are case sensitive",
90 "schema": {
91 "patternProperties": {
92 "[0-9]{2,}": { "type": "boolean" },
93 "X_": { "type": "string" }
94 }
95 },
96 "tests": [
97 {
98 "description": "non recognized members are ignored",
99 "data": { "answer 1": "42" },
100 "valid": true
101 },
102 {
103 "description": "recognized members are accounted for",
104 "data": { "a31b": null },
105 "valid": false
106 },
107 {
108 "description": "regexes are case sensitive",
109 "data": { "a_x_3": 3 },
110 "valid": true
111 },
112 {
113 "description": "regexes are case sensitive, 2",
114 "data": { "a_X_3": 3 },
115 "valid": false
116 }
117 ]
118 },
119 {
120 "description": "patternProperties with boolean schemas",
121 "schema": {
122 "patternProperties": {
123 "f.*": true,
124 "b.*": false
125 }
126 },
127 "tests": [
128 {
129 "description": "object with property matching schema true is valid",
130 "data": {"foo": 1},
131 "valid": true
132 },
133 {
134 "description": "object with property matching schema false is invalid",
135 "data": {"bar": 2},
136 "valid": false
137 },
138 {
139 "description": "object with both properties is invalid",
140 "data": {"foo": 1, "bar": 2},
141 "valid": false
142 },
143 {
144 "description": "empty object is valid",
145 "data": {},
146 "valid": true
147 }
148 ]
149 }
150 ]
0 [
1 {
2 "description": "object properties validation",
3 "schema": {
4 "properties": {
5 "foo": {"type": "integer"},
6 "bar": {"type": "string"}
7 }
8 },
9 "tests": [
10 {
11 "description": "both properties present and valid is valid",
12 "data": {"foo": 1, "bar": "baz"},
13 "valid": true
14 },
15 {
16 "description": "one property invalid is invalid",
17 "data": {"foo": 1, "bar": {}},
18 "valid": false
19 },
20 {
21 "description": "both properties invalid is invalid",
22 "data": {"foo": [], "bar": {}},
23 "valid": false
24 },
25 {
26 "description": "doesn't invalidate other properties",
27 "data": {"quux": []},
28 "valid": true
29 },
30 {
31 "description": "ignores arrays",
32 "data": [],
33 "valid": true
34 },
35 {
36 "description": "ignores other non-objects",
37 "data": 12,
38 "valid": true
39 }
40 ]
41 },
42 {
43 "description":
44 "properties, patternProperties, additionalProperties interaction",
45 "schema": {
46 "properties": {
47 "foo": {"type": "array", "maxItems": 3},
48 "bar": {"type": "array"}
49 },
50 "patternProperties": {"f.o": {"minItems": 2}},
51 "additionalProperties": {"type": "integer"}
52 },
53 "tests": [
54 {
55 "description": "property validates property",
56 "data": {"foo": [1, 2]},
57 "valid": true
58 },
59 {
60 "description": "property invalidates property",
61 "data": {"foo": [1, 2, 3, 4]},
62 "valid": false
63 },
64 {
65 "description": "patternProperty invalidates property",
66 "data": {"foo": []},
67 "valid": false
68 },
69 {
70 "description": "patternProperty validates nonproperty",
71 "data": {"fxo": [1, 2]},
72 "valid": true
73 },
74 {
75 "description": "patternProperty invalidates nonproperty",
76 "data": {"fxo": []},
77 "valid": false
78 },
79 {
80 "description": "additionalProperty ignores property",
81 "data": {"bar": []},
82 "valid": true
83 },
84 {
85 "description": "additionalProperty validates others",
86 "data": {"quux": 3},
87 "valid": true
88 },
89 {
90 "description": "additionalProperty invalidates others",
91 "data": {"quux": "foo"},
92 "valid": false
93 }
94 ]
95 },
96 {
97 "description": "properties with boolean schema",
98 "schema": {
99 "properties": {
100 "foo": true,
101 "bar": false
102 }
103 },
104 "tests": [
105 {
106 "description": "no property present is valid",
107 "data": {},
108 "valid": true
109 },
110 {
111 "description": "only 'true' property present is valid",
112 "data": {"foo": 1},
113 "valid": true
114 },
115 {
116 "description": "only 'false' property present is invalid",
117 "data": {"bar": 2},
118 "valid": false
119 },
120 {
121 "description": "both properties present is invalid",
122 "data": {"foo": 1, "bar": 2},
123 "valid": false
124 }
125 ]
126 }
127 ]
0 [
1 {
2 "description": "propertyNames validation",
3 "schema": {
4 "propertyNames": {"maxLength": 3}
5 },
6 "tests": [
7 {
8 "description": "all property names valid",
9 "data": {
10 "f": {},
11 "foo": {}
12 },
13 "valid": true
14 },
15 {
16 "description": "some property names invalid",
17 "data": {
18 "foo": {},
19 "foobar": {}
20 },
21 "valid": false
22 },
23 {
24 "description": "object without properties is valid",
25 "data": {},
26 "valid": true
27 },
28 {
29 "description": "ignores arrays",
30 "data": [1, 2, 3, 4],
31 "valid": true
32 },
33 {
34 "description": "ignores strings",
35 "data": "foobar",
36 "valid": true
37 },
38 {
39 "description": "ignores other non-objects",
40 "data": 12,
41 "valid": true
42 }
43 ]
44 },
45 {
46 "description": "propertyNames with boolean schema true",
47 "schema": {"propertyNames": true},
48 "tests": [
49 {
50 "description": "object with any properties is valid",
51 "data": {"foo": 1},
52 "valid": true
53 },
54 {
55 "description": "empty object is valid",
56 "data": {},
57 "valid": true
58 }
59 ]
60 },
61 {
62 "description": "propertyNames with boolean schema false",
63 "schema": {"propertyNames": false},
64 "tests": [
65 {
66 "description": "object with any properties is invalid",
67 "data": {"foo": 1},
68 "valid": false
69 },
70 {
71 "description": "empty object is valid",
72 "data": {},
73 "valid": true
74 }
75 ]
76 }
77 ]
0 [
1 {
2 "description": "root pointer ref",
3 "schema": {
4 "properties": {
5 "foo": {"$ref": "#"}
6 },
7 "additionalProperties": false
8 },
9 "tests": [
10 {
11 "description": "match",
12 "data": {"foo": false},
13 "valid": true
14 },
15 {
16 "description": "recursive match",
17 "data": {"foo": {"foo": false}},
18 "valid": true
19 },
20 {
21 "description": "mismatch",
22 "data": {"bar": false},
23 "valid": false
24 },
25 {
26 "description": "recursive mismatch",
27 "data": {"foo": {"bar": false}},
28 "valid": false
29 }
30 ]
31 },
32 {
33 "description": "relative pointer ref to object",
34 "schema": {
35 "properties": {
36 "foo": {"type": "integer"},
37 "bar": {"$ref": "#/properties/foo"}
38 }
39 },
40 "tests": [
41 {
42 "description": "match",
43 "data": {"bar": 3},
44 "valid": true
45 },
46 {
47 "description": "mismatch",
48 "data": {"bar": true},
49 "valid": false
50 }
51 ]
52 },
53 {
54 "description": "relative pointer ref to array",
55 "schema": {
56 "items": [
57 {"type": "integer"},
58 {"$ref": "#/items/0"}
59 ]
60 },
61 "tests": [
62 {
63 "description": "match array",
64 "data": [1, 2],
65 "valid": true
66 },
67 {
68 "description": "mismatch array",
69 "data": [1, "foo"],
70 "valid": false
71 }
72 ]
73 },
74 {
75 "description": "escaped pointer ref",
76 "schema": {
77 "tilda~field": {"type": "integer"},
78 "slash/field": {"type": "integer"},
79 "percent%field": {"type": "integer"},
80 "properties": {
81 "tilda": {"$ref": "#/tilda~0field"},
82 "slash": {"$ref": "#/slash~1field"},
83 "percent": {"$ref": "#/percent%25field"}
84 }
85 },
86 "tests": [
87 {
88 "description": "slash invalid",
89 "data": {"slash": "aoeu"},
90 "valid": false
91 },
92 {
93 "description": "tilda invalid",
94 "data": {"tilda": "aoeu"},
95 "valid": false
96 },
97 {
98 "description": "percent invalid",
99 "data": {"percent": "aoeu"},
100 "valid": false
101 },
102 {
103 "description": "slash valid",
104 "data": {"slash": 123},
105 "valid": true
106 },
107 {
108 "description": "tilda valid",
109 "data": {"tilda": 123},
110 "valid": true
111 },
112 {
113 "description": "percent valid",
114 "data": {"percent": 123},
115 "valid": true
116 }
117 ]
118 },
119 {
120 "description": "nested refs",
121 "schema": {
122 "definitions": {
123 "a": {"type": "integer"},
124 "b": {"$ref": "#/definitions/a"},
125 "c": {"$ref": "#/definitions/b"}
126 },
127 "$ref": "#/definitions/c"
128 },
129 "tests": [
130 {
131 "description": "nested ref valid",
132 "data": 5,
133 "valid": true
134 },
135 {
136 "description": "nested ref invalid",
137 "data": "a",
138 "valid": false
139 }
140 ]
141 },
142 {
143 "description": "ref overrides any sibling keywords",
144 "schema": {
145 "definitions": {
146 "reffed": {
147 "type": "array"
148 }
149 },
150 "properties": {
151 "foo": {
152 "$ref": "#/definitions/reffed",
153 "maxItems": 2
154 }
155 }
156 },
157 "tests": [
158 {
159 "description": "ref valid",
160 "data": { "foo": [] },
161 "valid": true
162 },
163 {
164 "description": "ref valid, maxItems ignored",
165 "data": { "foo": [ 1, 2, 3] },
166 "valid": true
167 },
168 {
169 "description": "ref invalid",
170 "data": { "foo": "string" },
171 "valid": false
172 }
173 ]
174 },
175 {
176 "description": "remote ref, containing refs itself",
177 "schema": {"$ref": "http://json-schema.org/draft-07/schema#"},
178 "tests": [
179 {
180 "description": "remote ref valid",
181 "data": {"minLength": 1},
182 "valid": true
183 },
184 {
185 "description": "remote ref invalid",
186 "data": {"minLength": -1},
187 "valid": false
188 }
189 ]
190 },
191 {
192 "description": "property named $ref that is not a reference",
193 "schema": {
194 "properties": {
195 "$ref": {"type": "string"}
196 }
197 },
198 "tests": [
199 {
200 "description": "property named $ref valid",
201 "data": {"$ref": "a"},
202 "valid": true
203 },
204 {
205 "description": "property named $ref invalid",
206 "data": {"$ref": 2},
207 "valid": false
208 }
209 ]
210 },
211 {
212 "description": "$ref to boolean schema true",
213 "schema": {
214 "$ref": "#/definitions/bool",
215 "definitions": {
216 "bool": true
217 }
218 },
219 "tests": [
220 {
221 "description": "any value is valid",
222 "data": "foo",
223 "valid": true
224 }
225 ]
226 },
227 {
228 "description": "$ref to boolean schema false",
229 "schema": {
230 "$ref": "#/definitions/bool",
231 "definitions": {
232 "bool": false
233 }
234 },
235 "tests": [
236 {
237 "description": "any value is invalid",
238 "data": "foo",
239 "valid": false
240 }
241 ]
242 },
243 {
244 "description": "Recursive references between schemas",
245 "schema": {
246 "$id": "http://localhost:1234/tree",
247 "description": "tree of nodes",
248 "type": "object",
249 "properties": {
250 "meta": {"type": "string"},
251 "nodes": {
252 "type": "array",
253 "items": {"$ref": "node"}
254 }
255 },
256 "required": ["meta", "nodes"],
257 "definitions": {
258 "node": {
259 "$id": "http://localhost:1234/node",
260 "description": "node",
261 "type": "object",
262 "properties": {
263 "value": {"type": "number"},
264 "subtree": {"$ref": "tree"}
265 },
266 "required": ["value"]
267 }
268 }
269 },
270 "tests": [
271 {
272 "description": "valid tree",
273 "data": {
274 "meta": "root",
275 "nodes": [
276 {
277 "value": 1,
278 "subtree": {
279 "meta": "child",
280 "nodes": [
281 {"value": 1.1},
282 {"value": 1.2}
283 ]
284 }
285 },
286 {
287 "value": 2,
288 "subtree": {
289 "meta": "child",
290 "nodes": [
291 {"value": 2.1},
292 {"value": 2.2}
293 ]
294 }
295 }
296 ]
297 },
298 "valid": true
299 },
300 {
301 "description": "invalid tree",
302 "data": {
303 "meta": "root",
304 "nodes": [
305 {
306 "value": 1,
307 "subtree": {
308 "meta": "child",
309 "nodes": [
310 {"value": "string is invalid"},
311 {"value": 1.2}
312 ]
313 }
314 },
315 {
316 "value": 2,
317 "subtree": {
318 "meta": "child",
319 "nodes": [
320 {"value": 2.1},
321 {"value": 2.2}
322 ]
323 }
324 }
325 ]
326 },
327 "valid": false
328 }
329 ]
330 }
331 ]
0 [
1 {
2 "description": "remote ref",
3 "schema": {"$ref": "http://localhost:1234/integer.json"},
4 "tests": [
5 {
6 "description": "remote ref valid",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "remote ref invalid",
12 "data": "a",
13 "valid": false
14 }
15 ]
16 },
17 {
18 "description": "fragment within remote ref",
19 "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
20 "tests": [
21 {
22 "description": "remote fragment valid",
23 "data": 1,
24 "valid": true
25 },
26 {
27 "description": "remote fragment invalid",
28 "data": "a",
29 "valid": false
30 }
31 ]
32 },
33 {
34 "description": "ref within remote ref",
35 "schema": {
36 "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
37 },
38 "tests": [
39 {
40 "description": "ref within ref valid",
41 "data": 1,
42 "valid": true
43 },
44 {
45 "description": "ref within ref invalid",
46 "data": "a",
47 "valid": false
48 }
49 ]
50 },
51 {
52 "description": "base URI change",
53 "schema": {
54 "$id": "http://localhost:1234/",
55 "items": {
56 "$id": "folder/",
57 "items": {"$ref": "folderInteger.json"}
58 }
59 },
60 "tests": [
61 {
62 "description": "base URI change ref valid",
63 "data": [[1]],
64 "valid": true
65 },
66 {
67 "description": "base URI change ref invalid",
68 "data": [["a"]],
69 "valid": false
70 }
71 ]
72 },
73 {
74 "description": "base URI change - change folder",
75 "schema": {
76 "$id": "http://localhost:1234/scope_change_defs1.json",
77 "type" : "object",
78 "properties": {
79 "list": {"$ref": "#/definitions/baz"}
80 },
81 "definitions": {
82 "baz": {
83 "$id": "folder/",
84 "type": "array",
85 "items": {"$ref": "folderInteger.json"}
86 }
87 }
88 },
89 "tests": [
90 {
91 "description": "number is valid",
92 "data": {"list": [1]},
93 "valid": true
94 },
95 {
96 "description": "string is invalid",
97 "data": {"list": ["a"]},
98 "valid": false
99 }
100 ]
101 },
102 {
103 "description": "base URI change - change folder in subschema",
104 "schema": {
105 "$id": "http://localhost:1234/scope_change_defs2.json",
106 "type" : "object",
107 "properties": {
108 "list": {"$ref": "#/definitions/baz/definitions/bar"}
109 },
110 "definitions": {
111 "baz": {
112 "$id": "folder/",
113 "definitions": {
114 "bar": {
115 "type": "array",
116 "items": {"$ref": "folderInteger.json"}
117 }
118 }
119 }
120 }
121 },
122 "tests": [
123 {
124 "description": "number is valid",
125 "data": {"list": [1]},
126 "valid": true
127 },
128 {
129 "description": "string is invalid",
130 "data": {"list": ["a"]},
131 "valid": false
132 }
133 ]
134 },
135 {
136 "description": "root ref in remote ref",
137 "schema": {
138 "$id": "http://localhost:1234/object",
139 "type": "object",
140 "properties": {
141 "name": {"$ref": "name.json#/definitions/orNull"}
142 }
143 },
144 "tests": [
145 {
146 "description": "string is valid",
147 "data": {
148 "name": "foo"
149 },
150 "valid": true
151 },
152 {
153 "description": "null is valid",
154 "data": {
155 "name": null
156 },
157 "valid": true
158 },
159 {
160 "description": "object is invalid",
161 "data": {
162 "name": {
163 "name": null
164 }
165 },
166 "valid": false
167 }
168 ]
169 }
170 ]
0 [
1 {
2 "description": "required validation",
3 "schema": {
4 "properties": {
5 "foo": {},
6 "bar": {}
7 },
8 "required": ["foo"]
9 },
10 "tests": [
11 {
12 "description": "present required property is valid",
13 "data": {"foo": 1},
14 "valid": true
15 },
16 {
17 "description": "non-present required property is invalid",
18 "data": {"bar": 1},
19 "valid": false
20 },
21 {
22 "description": "ignores arrays",
23 "data": [],
24 "valid": true
25 },
26 {
27 "description": "ignores strings",
28 "data": "",
29 "valid": true
30 },
31 {
32 "description": "ignores other non-objects",
33 "data": 12,
34 "valid": true
35 }
36 ]
37 },
38 {
39 "description": "required default validation",
40 "schema": {
41 "properties": {
42 "foo": {}
43 }
44 },
45 "tests": [
46 {
47 "description": "not required by default",
48 "data": {},
49 "valid": true
50 }
51 ]
52 },
53 {
54 "description": "required with empty array",
55 "schema": {
56 "properties": {
57 "foo": {}
58 },
59 "required": []
60 },
61 "tests": [
62 {
63 "description": "property not required",
64 "data": {},
65 "valid": true
66 }
67 ]
68 }
69 ]
0 [
1 {
2 "description": "integer type matches integers",
3 "schema": {"type": "integer"},
4 "tests": [
5 {
6 "description": "an integer is an integer",
7 "data": 1,
8 "valid": true
9 },
10 {
11 "description": "a float is not an integer",
12 "data": 1.1,
13 "valid": false
14 },
15 {
16 "description": "a string is not an integer",
17 "data": "foo",
18 "valid": false
19 },
20 {
21 "description": "a string is still not an integer, even if it looks like one",
22 "data": "1",
23 "valid": false
24 },
25 {
26 "description": "an object is not an integer",
27 "data": {},
28 "valid": false
29 },
30 {
31 "description": "an array is not an integer",
32 "data": [],
33 "valid": false
34 },
35 {
36 "description": "a boolean is not an integer",
37 "data": true,
38 "valid": false
39 },
40 {
41 "description": "null is not an integer",
42 "data": null,
43 "valid": false
44 }
45 ]
46 },
47 {
48 "description": "number type matches numbers",
49 "schema": {"type": "number"},
50 "tests": [
51 {
52 "description": "an integer is a number",
53 "data": 1,
54 "valid": true
55 },
56 {
57 "description": "a float is a number",
58 "data": 1.1,
59 "valid": true
60 },
61 {
62 "description": "a string is not a number",
63 "data": "foo",
64 "valid": false
65 },
66 {
67 "description": "a string is still not a number, even if it looks like one",
68 "data": "1",
69 "valid": false
70 },
71 {
72 "description": "an object is not a number",
73 "data": {},
74 "valid": false
75 },
76 {
77 "description": "an array is not a number",
78 "data": [],
79 "valid": false
80 },
81 {
82 "description": "a boolean is not a number",
83 "data": true,
84 "valid": false
85 },
86 {
87 "description": "null is not a number",
88 "data": null,
89 "valid": false
90 }
91 ]
92 },
93 {
94 "description": "string type matches strings",
95 "schema": {"type": "string"},
96 "tests": [
97 {
98 "description": "1 is not a string",
99 "data": 1,
100 "valid": false
101 },
102 {
103 "description": "a float is not a string",
104 "data": 1.1,
105 "valid": false
106 },
107 {
108 "description": "a string is a string",
109 "data": "foo",
110 "valid": true
111 },
112 {
113 "description": "a string is still a string, even if it looks like a number",
114 "data": "1",
115 "valid": true
116 },
117 {
118 "description": "an object is not a string",
119 "data": {},
120 "valid": false
121 },
122 {
123 "description": "an array is not a string",
124 "data": [],
125 "valid": false
126 },
127 {
128 "description": "a boolean is not a string",
129 "data": true,
130 "valid": false
131 },
132 {
133 "description": "null is not a string",
134 "data": null,
135 "valid": false
136 }
137 ]
138 },
139 {
140 "description": "object type matches objects",
141 "schema": {"type": "object"},
142 "tests": [
143 {
144 "description": "an integer is not an object",
145 "data": 1,
146 "valid": false
147 },
148 {
149 "description": "a float is not an object",
150 "data": 1.1,
151 "valid": false
152 },
153 {
154 "description": "a string is not an object",
155 "data": "foo",
156 "valid": false
157 },
158 {
159 "description": "an object is an object",
160 "data": {},
161 "valid": true
162 },
163 {
164 "description": "an array is not an object",
165 "data": [],
166 "valid": false
167 },
168 {
169 "description": "a boolean is not an object",
170 "data": true,
171 "valid": false
172 },
173 {
174 "description": "null is not an object",
175 "data": null,
176 "valid": false
177 }
178 ]
179 },
180 {
181 "description": "array type matches arrays",
182 "schema": {"type": "array"},
183 "tests": [
184 {
185 "description": "an integer is not an array",
186 "data": 1,
187 "valid": false
188 },
189 {
190 "description": "a float is not an array",
191 "data": 1.1,
192 "valid": false
193 },
194 {
195 "description": "a string is not an array",
196 "data": "foo",
197 "valid": false
198 },
199 {
200 "description": "an object is not an array",
201 "data": {},
202 "valid": false
203 },
204 {
205 "description": "an array is an array",
206 "data": [],
207 "valid": true
208 },
209 {
210 "description": "a boolean is not an array",
211 "data": true,
212 "valid": false
213 },
214 {
215 "description": "null is not an array",
216 "data": null,
217 "valid": false
218 }
219 ]
220 },
221 {
222 "description": "boolean type matches booleans",
223 "schema": {"type": "boolean"},
224 "tests": [
225 {
226 "description": "an integer is not a boolean",
227 "data": 1,
228 "valid": false
229 },
230 {
231 "description": "a float is not a boolean",
232 "data": 1.1,
233 "valid": false
234 },
235 {
236 "description": "a string is not a boolean",
237 "data": "foo",
238 "valid": false
239 },
240 {
241 "description": "an object is not a boolean",
242 "data": {},
243 "valid": false
244 },
245 {
246 "description": "an array is not a boolean",
247 "data": [],
248 "valid": false
249 },
250 {
251 "description": "a boolean is a boolean",
252 "data": true,
253 "valid": true
254 },
255 {
256 "description": "null is not a boolean",
257 "data": null,
258 "valid": false
259 }
260 ]
261 },
262 {
263 "description": "null type matches only the null object",
264 "schema": {"type": "null"},
265 "tests": [
266 {
267 "description": "an integer is not null",
268 "data": 1,
269 "valid": false
270 },
271 {
272 "description": "a float is not null",
273 "data": 1.1,
274 "valid": false
275 },
276 {
277 "description": "a string is not null",
278 "data": "foo",
279 "valid": false
280 },
281 {
282 "description": "an object is not null",
283 "data": {},
284 "valid": false
285 },
286 {
287 "description": "an array is not null",
288 "data": [],
289 "valid": false
290 },
291 {
292 "description": "a boolean is not null",
293 "data": true,
294 "valid": false
295 },
296 {
297 "description": "null is null",
298 "data": null,
299 "valid": true
300 }
301 ]
302 },
303 {
304 "description": "multiple types can be specified in an array",
305 "schema": {"type": ["integer", "string"]},
306 "tests": [
307 {
308 "description": "an integer is valid",
309 "data": 1,
310 "valid": true
311 },
312 {
313 "description": "a string is valid",
314 "data": "foo",
315 "valid": true
316 },
317 {
318 "description": "a float is invalid",
319 "data": 1.1,
320 "valid": false
321 },
322 {
323 "description": "an object is invalid",
324 "data": {},
325 "valid": false
326 },
327 {
328 "description": "an array is invalid",
329 "data": [],
330 "valid": false
331 },
332 {
333 "description": "a boolean is invalid",
334 "data": true,
335 "valid": false
336 },
337 {
338 "description": "null is invalid",
339 "data": null,
340 "valid": false
341 }
342 ]
343 }
344 ]
0 [
1 {
2 "description": "uniqueItems validation",
3 "schema": {"uniqueItems": true},
4 "tests": [
5 {
6 "description": "unique array of integers is valid",
7 "data": [1, 2],
8 "valid": true
9 },
10 {
11 "description": "non-unique array of integers is invalid",
12 "data": [1, 1],
13 "valid": false
14 },
15 {
16 "description": "numbers are unique if mathematically unequal",
17 "data": [1.0, 1.00, 1],
18 "valid": false
19 },
20 {
21 "description": "unique array of objects is valid",
22 "data": [{"foo": "bar"}, {"foo": "baz"}],
23 "valid": true
24 },
25 {
26 "description": "non-unique array of objects is invalid",
27 "data": [{"foo": "bar"}, {"foo": "bar"}],
28 "valid": false
29 },
30 {
31 "description": "unique array of nested objects is valid",
32 "data": [
33 {"foo": {"bar" : {"baz" : true}}},
34 {"foo": {"bar" : {"baz" : false}}}
35 ],
36 "valid": true
37 },
38 {
39 "description": "non-unique array of nested objects is invalid",
40 "data": [
41 {"foo": {"bar" : {"baz" : true}}},
42 {"foo": {"bar" : {"baz" : true}}}
43 ],
44 "valid": false
45 },
46 {
47 "description": "unique array of arrays is valid",
48 "data": [["foo"], ["bar"]],
49 "valid": true
50 },
51 {
52 "description": "non-unique array of arrays is invalid",
53 "data": [["foo"], ["foo"]],
54 "valid": false
55 },
56 {
57 "description": "1 and true are unique",
58 "data": [1, true],
59 "valid": true
60 },
61 {
62 "description": "0 and false are unique",
63 "data": [0, false],
64 "valid": true
65 },
66 {
67 "description": "unique heterogeneous types are valid",
68 "data": [{}, [1], true, null, 1],
69 "valid": true
70 },
71 {
72 "description": "non-unique heterogeneous types are invalid",
73 "data": [{}, [1], true, null, {}, 1],
74 "valid": false
75 }
76 ]
77 }
78 ]
0 {"type":"object","additionalProperties":false,"definitions":{"x":{"type":"integer"}}}
0 {
1 "type": "integer"
2 }
0 {
1 "definitions": {
2 "orNull": {
3 "anyOf": [
4 {"type": "null"},
5 {"$ref": "#"}
6 ]
7 }
8 },
9 "type": "string"
10 }
0 {
1 "integer": {
2 "type": "integer"
3 },
4 "refToInteger": {
5 "$ref": "#/integer"
6 }
7 }
2424
2525 package gojsonschema
2626
27 // Type constants
2728 const (
2829 TYPE_ARRAY = `array`
2930 TYPE_BOOLEAN = `boolean`
3435 TYPE_STRING = `string`
3536 )
3637
38 // JSON_TYPES hosts the list of type that are supported in JSON
3739 var JSON_TYPES []string
40
41 // SCHEMA_TYPES hosts the list of type that are supported in schemas
3842 var SCHEMA_TYPES []string
3943
4044 func init() {
2626
2727 import (
2828 "encoding/json"
29 "fmt"
30 "math"
29 "math/big"
3130 "reflect"
32 "strconv"
3331 )
3432
35 func isKind(what interface{}, kind reflect.Kind) bool {
33 func isKind(what interface{}, kinds ...reflect.Kind) bool {
3634 target := what
37 if isJsonNumber(what) {
35 if isJSONNumber(what) {
3836 // JSON Numbers are strings!
3937 target = *mustBeNumber(what)
4038 }
41 return reflect.ValueOf(target).Kind() == kind
39 targetKind := reflect.ValueOf(target).Kind()
40 for _, kind := range kinds {
41 if targetKind == kind {
42 return true
43 }
44 }
45 return false
4246 }
4347
4448 func existsMapKey(m map[string]interface{}, k string) bool {
5559 return false
5660 }
5761
58 func marshalToJsonString(value interface{}) (*string, error) {
62 // indexStringInSlice returns the index of the first instance of 'what' in s or -1 if it is not found in s.
63 func indexStringInSlice(s []string, what string) int {
64 for i := range s {
65 if s[i] == what {
66 return i
67 }
68 }
69 return -1
70 }
71
72 func marshalToJSONString(value interface{}) (*string, error) {
5973
6074 mBytes, err := json.Marshal(value)
6175 if err != nil {
6680 return &sBytes, nil
6781 }
6882
69 func isJsonNumber(what interface{}) bool {
83 func marshalWithoutNumber(value interface{}) (*string, error) {
84
85 // The JSON is decoded using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
86 // This means the numbers are internally still represented as strings and therefore 1.00 is unequal to 1
87 // One way to eliminate these differences is to decode and encode the JSON one more time without Decoder.UseNumber
88 // so that these differences in representation are removed
89
90 jsonString, err := marshalToJSONString(value)
91 if err != nil {
92 return nil, err
93 }
94
95 var document interface{}
96
97 err = json.Unmarshal([]byte(*jsonString), &document)
98 if err != nil {
99 return nil, err
100 }
101
102 return marshalToJSONString(document)
103 }
104
105 func isJSONNumber(what interface{}) bool {
70106
71107 switch what.(type) {
72108
77113 return false
78114 }
79115
80 func checkJsonNumber(what interface{}) (isValidFloat64 bool, isValidInt64 bool, isValidInt32 bool) {
116 func checkJSONInteger(what interface{}) (isInt bool) {
81117
82118 jsonNumber := what.(json.Number)
83119
84 f64, errFloat64 := jsonNumber.Float64()
85 s64 := strconv.FormatFloat(f64, 'f', -1, 64)
86 _, errInt64 := strconv.ParseInt(s64, 10, 64)
120 bigFloat, isValidNumber := new(big.Rat).SetString(string(jsonNumber))
87121
88 isValidFloat64 = errFloat64 == nil
89 isValidInt64 = errInt64 == nil
90
91 _, errInt32 := strconv.ParseInt(s64, 10, 32)
92 isValidInt32 = isValidInt64 && errInt32 == nil
93
94 return
122 return isValidNumber && bigFloat.IsInt()
95123
96124 }
97125
98126 // same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
99127 const (
100 max_json_float = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
101 min_json_float = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
128 maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
129 minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
102130 )
103
104 func isFloat64AnInteger(f float64) bool {
105
106 if math.IsNaN(f) || math.IsInf(f, 0) || f < min_json_float || f > max_json_float {
107 return false
108 }
109
110 return f == float64(int64(f)) || f == float64(uint64(f))
111 }
112131
113132 func mustBeInteger(what interface{}) *int {
114133
115 if isJsonNumber(what) {
134 if isJSONNumber(what) {
116135
117136 number := what.(json.Number)
118137
119 _, _, isValidInt32 := checkJsonNumber(number)
138 isInt := checkJSONInteger(number)
120139
121 if isValidInt32 {
140 if isInt {
122141
123142 int64Value, err := number.Int64()
124143 if err != nil {
127146
128147 int32Value := int(int64Value)
129148 return &int32Value
130
131 } else {
132 return nil
133149 }
134150
135151 }
137153 return nil
138154 }
139155
140 func mustBeNumber(what interface{}) *float64 {
156 func mustBeNumber(what interface{}) *big.Rat {
141157
142 if isJsonNumber(what) {
143
158 if isJSONNumber(what) {
144159 number := what.(json.Number)
145 float64Value, err := number.Float64()
146
147 if err == nil {
148 return &float64Value
149 } else {
150 return nil
160 float64Value, success := new(big.Rat).SetString(string(number))
161 if success {
162 return float64Value
151163 }
152
153164 }
154165
155166 return nil
156167
157 }
158
159 // formats a number so that it is displayed as the smallest string possible
160 func resultErrorFormatJsonNumber(n json.Number) string {
161
162 if int64Value, err := n.Int64(); err == nil {
163 return fmt.Sprintf("%d", int64Value)
164 }
165
166 float64Value, _ := n.Float64()
167
168 return fmt.Sprintf("%g", float64Value)
169 }
170
171 // formats a number so that it is displayed as the smallest string possible
172 func resultErrorFormatNumber(n float64) string {
173
174 if isFloat64AnInteger(n) {
175 return fmt.Sprintf("%d", int64(n))
176 }
177
178 return fmt.Sprintf("%g", n)
179168 }
180169
181170 func convertDocumentNode(val interface{}) interface{} {
2525 package gojsonschema
2626
2727 import (
28 "encoding/json"
29 "testing"
30
2831 "github.com/stretchr/testify/assert"
29 "math"
30 "testing"
3132 )
3233
33 func TestResultErrorFormatNumber(t *testing.T) {
34 func TestCheckJsonNumber(t *testing.T) {
35 var testCases = []struct {
36 isInt bool
37 value json.Number
38 }{
39 {true, "0"},
40 {true, "2147483647"},
41 {true, "-2147483648"},
42 {true, "9223372036854775807"},
43 {true, "-9223372036854775808"},
44 {true, "1.0e+2"},
45 {true, "1.0e+10"},
46 {true, "-1.0e+2"},
47 {true, "-1.0e+10"},
48 {false, "1.0e-2"},
49 {false, "number"},
50 {false, "123number"},
51 }
3452
35 assert.Equal(t, "1", resultErrorFormatNumber(1))
36 assert.Equal(t, "-1", resultErrorFormatNumber(-1))
37 assert.Equal(t, "0", resultErrorFormatNumber(0))
38 // unfortunately, can not be recognized as float
39 assert.Equal(t, "0", resultErrorFormatNumber(0.0))
40
41 assert.Equal(t, "1.001", resultErrorFormatNumber(1.001))
42 assert.Equal(t, "-1.001", resultErrorFormatNumber(-1.001))
43 assert.Equal(t, "0.0001", resultErrorFormatNumber(0.0001))
44
45 // casting math.MaxInt64 (1<<63 -1) to float back to int64
46 // becomes negative. obviousely because of bit missinterpretation.
47 // so simply test a slightly smaller "large" integer here
48 assert.Equal(t, "4.611686018427388e+18", resultErrorFormatNumber(1<<62))
49 // with negative int64 max works
50 assert.Equal(t, "-9.223372036854776e+18", resultErrorFormatNumber(math.MinInt64))
51 assert.Equal(t, "-4.611686018427388e+18", resultErrorFormatNumber(-1<<62))
52
53 assert.Equal(t, "10000000000", resultErrorFormatNumber(1e10))
54 assert.Equal(t, "-10000000000", resultErrorFormatNumber(-1e10))
55
56 assert.Equal(t, "1.000000000001", resultErrorFormatNumber(1.000000000001))
57 assert.Equal(t, "-1.000000000001", resultErrorFormatNumber(-1.000000000001))
58 assert.Equal(t, "1e-10", resultErrorFormatNumber(1e-10))
59 assert.Equal(t, "-1e-10", resultErrorFormatNumber(-1e-10))
60 assert.Equal(t, "4.6116860184273876e+07", resultErrorFormatNumber(4.611686018427387904e7))
61 assert.Equal(t, "-4.6116860184273876e+07", resultErrorFormatNumber(-4.611686018427387904e7))
53 for _, testCase := range testCases {
54 assert.Equal(t, testCase.isInt, checkJSONInteger(testCase.value))
55 assert.Equal(t, testCase.isInt, checkJSONInteger(testCase.value))
56 }
6257
6358 }
2626
2727 import (
2828 "encoding/json"
29 "math/big"
2930 "reflect"
3031 "regexp"
3132 "strconv"
3334 "unicode/utf8"
3435 )
3536
37 // Validate loads and validates a JSON schema
3638 func Validate(ls JSONLoader, ld JSONLoader) (*Result, error) {
37
38 var err error
39
4039 // load schema
41
4240 schema, err := NewSchema(ls)
4341 if err != nil {
4442 return nil, err
4543 }
46
47 // begine validation
48
4944 return schema.Validate(ld)
50
51 }
52
45 }
46
47 // Validate loads and validates a JSON document
5348 func (v *Schema) Validate(l JSONLoader) (*Result, error) {
54
55 // load document
56
5749 root, err := l.LoadJSON()
5850 if err != nil {
5951 return nil, err
6052 }
61
62 // begin validation
63
53 return v.validateDocument(root), nil
54 }
55
56 func (v *Schema) validateDocument(root interface{}) *Result {
6457 result := &Result{}
65 context := newJsonContext(STRING_CONTEXT_ROOT, nil)
58 context := NewJsonContext(STRING_CONTEXT_ROOT, nil)
6659 v.rootSchema.validateRecursive(v.rootSchema, root, result, context)
67
68 return result, nil
69
70 }
71
72 func (v *subSchema) subValidateWithContext(document interface{}, context *jsonContext) *Result {
60 return result
61 }
62
63 func (v *subSchema) subValidateWithContext(document interface{}, context *JsonContext) *Result {
7364 result := &Result{}
7465 v.validateRecursive(v, document, result, context)
7566 return result
7667 }
7768
7869 // Walker function to validate the json recursively against the subSchema
79 func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *jsonContext) {
70 func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) {
8071
8172 if internalLogEnabled {
8273 internalLog("validateRecursive %s", context.String())
8374 internalLog(" %v", currentNode)
75 }
76
77 // Handle true/false schema as early as possible as all other fields will be nil
78 if currentSubSchema.pass != nil {
79 if !*currentSubSchema.pass {
80 result.addInternalError(
81 new(FalseError),
82 context,
83 currentNode,
84 ErrorDetails{},
85 )
86 }
87 return
8488 }
8589
8690 // Handle referenced schemas, returns directly when a $ref is found
9296 // Check for null value
9397 if currentNode == nil {
9498 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_NULL) {
95 result.addError(
99 result.addInternalError(
96100 new(InvalidTypeError),
97101 context,
98102 currentNode,
109113
110114 } else { // Not a null value
111115
112 if isJsonNumber(currentNode) {
116 if isJSONNumber(currentNode) {
113117
114118 value := currentNode.(json.Number)
115119
116 _, isValidInt64, _ := checkJsonNumber(value)
117
118 validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isValidInt64 && currentSubSchema.types.Contains(TYPE_INTEGER))
120 isInt := checkJSONInteger(value)
121
122 validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isInt && currentSubSchema.types.Contains(TYPE_INTEGER))
119123
120124 if currentSubSchema.types.IsTyped() && !validType {
121125
122126 givenType := TYPE_INTEGER
123 if !isValidInt64 {
127 if !isInt {
124128 givenType = TYPE_NUMBER
125129 }
126130
127 result.addError(
131 result.addInternalError(
128132 new(InvalidTypeError),
129133 context,
130134 currentNode,
153157 case reflect.Slice:
154158
155159 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_ARRAY) {
156 result.addError(
160 result.addInternalError(
157161 new(InvalidTypeError),
158162 context,
159163 currentNode,
176180
177181 case reflect.Map:
178182 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_OBJECT) {
179 result.addError(
183 result.addInternalError(
180184 new(InvalidTypeError),
181185 context,
182186 currentNode,
201205 for _, pSchema := range currentSubSchema.propertiesChildren {
202206 nextNode, ok := castCurrentNode[pSchema.property]
203207 if ok {
204 subContext := newJsonContext(pSchema.property, context)
208 subContext := NewJsonContext(pSchema.property, context)
205209 v.validateRecursive(pSchema, nextNode, result, subContext)
206210 }
207211 }
211215 case reflect.Bool:
212216
213217 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_BOOLEAN) {
214 result.addError(
218 result.addInternalError(
215219 new(InvalidTypeError),
216220 context,
217221 currentNode,
233237 case reflect.String:
234238
235239 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_STRING) {
236 result.addError(
240 result.addInternalError(
237241 new(InvalidTypeError),
238242 context,
239243 currentNode,
262266 }
263267
264268 // Different kinds of validation there, subSchema / common / array / object / string...
265 func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *jsonContext) {
269 func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) {
266270
267271 if internalLogEnabled {
268272 internalLog("validateSchema %s", context.String())
286290 }
287291 if !validatedAnyOf {
288292
289 result.addError(new(NumberAnyOfError), context, currentNode, ErrorDetails{})
293 result.addInternalError(new(NumberAnyOfError), context, currentNode, ErrorDetails{})
290294
291295 if bestValidationResult != nil {
292296 // add error messages of closest matching subSchema as
312316
313317 if nbValidated != 1 {
314318
315 result.addError(new(NumberOneOfError), context, currentNode, ErrorDetails{})
319 result.addInternalError(new(NumberOneOfError), context, currentNode, ErrorDetails{})
316320
317321 if nbValidated == 0 {
318322 // add error messages of closest matching subSchema as
335339 }
336340
337341 if nbValidated != len(currentSubSchema.allOf) {
338 result.addError(new(NumberAllOfError), context, currentNode, ErrorDetails{})
342 result.addInternalError(new(NumberAllOfError), context, currentNode, ErrorDetails{})
339343 }
340344 }
341345
342346 if currentSubSchema.not != nil {
343347 validationResult := currentSubSchema.not.subValidateWithContext(currentNode, context)
344348 if validationResult.Valid() {
345 result.addError(new(NumberNotError), context, currentNode, ErrorDetails{})
349 result.addInternalError(new(NumberNotError), context, currentNode, ErrorDetails{})
346350 }
347351 }
348352
355359 case []string:
356360 for _, dependOnKey := range dependency {
357361 if _, dependencyResolved := currentNode.(map[string]interface{})[dependOnKey]; !dependencyResolved {
358 result.addError(
362 result.addInternalError(
359363 new(MissingDependencyError),
360364 context,
361365 currentNode,
366370
367371 case *subSchema:
368372 dependency.validateRecursive(dependency, currentNode, result, context)
369
370373 }
371374 }
372375 }
373376 }
374377 }
375378
379 if currentSubSchema._if != nil {
380 validationResultIf := currentSubSchema._if.subValidateWithContext(currentNode, context)
381 if currentSubSchema._then != nil && validationResultIf.Valid() {
382 validationResultThen := currentSubSchema._then.subValidateWithContext(currentNode, context)
383 if !validationResultThen.Valid() {
384 result.addInternalError(new(ConditionThenError), context, currentNode, ErrorDetails{})
385 result.mergeErrors(validationResultThen)
386 }
387 }
388 if currentSubSchema._else != nil && !validationResultIf.Valid() {
389 validationResultElse := currentSubSchema._else.subValidateWithContext(currentNode, context)
390 if !validationResultElse.Valid() {
391 result.addInternalError(new(ConditionElseError), context, currentNode, ErrorDetails{})
392 result.mergeErrors(validationResultElse)
393 }
394 }
395 }
396
376397 result.incrementScore()
377398 }
378399
379 func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) {
400 func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
380401
381402 if internalLogEnabled {
382403 internalLog("validateCommon %s", context.String())
383404 internalLog(" %v", value)
384405 }
385406
407 // const:
408 if currentSubSchema._const != nil {
409 vString, err := marshalWithoutNumber(value)
410 if err != nil {
411 result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err})
412 }
413 if *vString != *currentSubSchema._const {
414 result.addInternalError(new(ConstError),
415 context,
416 value,
417 ErrorDetails{
418 "allowed": *currentSubSchema._const,
419 },
420 )
421 }
422 }
423
386424 // enum:
387425 if len(currentSubSchema.enum) > 0 {
388 has, err := currentSubSchema.ContainsEnum(value)
426 vString, err := marshalWithoutNumber(value)
389427 if err != nil {
390 result.addError(new(InternalError), context, value, ErrorDetails{"error": err})
391 }
392 if !has {
393 result.addError(
428 result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err})
429 }
430 if !isStringInSlice(currentSubSchema.enum, *vString) {
431 result.addInternalError(
394432 new(EnumError),
395433 context,
396434 value,
404442 result.incrementScore()
405443 }
406444
407 func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *jsonContext) {
445 func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *JsonContext) {
408446
409447 if internalLogEnabled {
410448 internalLog("validateArray %s", context.String())
416454 // TODO explain
417455 if currentSubSchema.itemsChildrenIsSingleSchema {
418456 for i := range value {
419 subContext := newJsonContext(strconv.Itoa(i), context)
457 subContext := NewJsonContext(strconv.Itoa(i), context)
420458 validationResult := currentSubSchema.itemsChildren[0].subValidateWithContext(value[i], subContext)
421459 result.mergeErrors(validationResult)
422460 }
427465
428466 // while we have both schemas and values, check them against each other
429467 for i := 0; i != nbItems && i != nbValues; i++ {
430 subContext := newJsonContext(strconv.Itoa(i), context)
468 subContext := NewJsonContext(strconv.Itoa(i), context)
431469 validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext)
432470 result.mergeErrors(validationResult)
433471 }
439477 switch currentSubSchema.additionalItems.(type) {
440478 case bool:
441479 if !currentSubSchema.additionalItems.(bool) {
442 result.addError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{})
480 result.addInternalError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{})
443481 }
444482 case *subSchema:
445483 additionalItemSchema := currentSubSchema.additionalItems.(*subSchema)
446484 for i := nbItems; i != nbValues; i++ {
447 subContext := newJsonContext(strconv.Itoa(i), context)
485 subContext := NewJsonContext(strconv.Itoa(i), context)
448486 validationResult := additionalItemSchema.subValidateWithContext(value[i], subContext)
449487 result.mergeErrors(validationResult)
450488 }
456494 // minItems & maxItems
457495 if currentSubSchema.minItems != nil {
458496 if nbValues < int(*currentSubSchema.minItems) {
459 result.addError(
497 result.addInternalError(
460498 new(ArrayMinItemsError),
461499 context,
462500 value,
466504 }
467505 if currentSubSchema.maxItems != nil {
468506 if nbValues > int(*currentSubSchema.maxItems) {
469 result.addError(
507 result.addInternalError(
470508 new(ArrayMaxItemsError),
471509 context,
472510 value,
477515
478516 // uniqueItems:
479517 if currentSubSchema.uniqueItems {
480 var stringifiedItems []string
481 for _, v := range value {
482 vString, err := marshalToJsonString(v)
518 var stringifiedItems = make(map[string]int)
519 for j, v := range value {
520 vString, err := marshalWithoutNumber(v)
483521 if err != nil {
484 result.addError(new(InternalError), context, value, ErrorDetails{"err": err})
485 }
486 if isStringInSlice(stringifiedItems, *vString) {
487 result.addError(
522 result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err})
523 }
524 if i, ok := stringifiedItems[*vString]; ok {
525 result.addInternalError(
488526 new(ItemsMustBeUniqueError),
489527 context,
490528 value,
491 ErrorDetails{"type": TYPE_ARRAY},
529 ErrorDetails{"type": TYPE_ARRAY, "i": i, "j": j},
492530 )
493531 }
494 stringifiedItems = append(stringifiedItems, *vString)
532 stringifiedItems[*vString] = j
533 }
534 }
535
536 // contains:
537
538 if currentSubSchema.contains != nil {
539 validatedOne := false
540 var bestValidationResult *Result
541
542 for i, v := range value {
543 subContext := NewJsonContext(strconv.Itoa(i), context)
544
545 validationResult := currentSubSchema.contains.subValidateWithContext(v, subContext)
546 if validationResult.Valid() {
547 validatedOne = true
548 break
549 } else {
550 if bestValidationResult == nil || validationResult.score > bestValidationResult.score {
551 bestValidationResult = validationResult
552 }
553 }
554 }
555 if !validatedOne {
556 result.addInternalError(
557 new(ArrayContainsError),
558 context,
559 value,
560 ErrorDetails{},
561 )
562 if bestValidationResult != nil {
563 result.mergeErrors(bestValidationResult)
564 }
495565 }
496566 }
497567
498568 result.incrementScore()
499569 }
500570
501 func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *jsonContext) {
571 func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *JsonContext) {
502572
503573 if internalLogEnabled {
504574 internalLog("validateObject %s", context.String())
508578 // minProperties & maxProperties:
509579 if currentSubSchema.minProperties != nil {
510580 if len(value) < int(*currentSubSchema.minProperties) {
511 result.addError(
581 result.addInternalError(
512582 new(ArrayMinPropertiesError),
513583 context,
514584 value,
518588 }
519589 if currentSubSchema.maxProperties != nil {
520590 if len(value) > int(*currentSubSchema.maxProperties) {
521 result.addError(
591 result.addInternalError(
522592 new(ArrayMaxPropertiesError),
523593 context,
524594 value,
533603 if ok {
534604 result.incrementScore()
535605 } else {
536 result.addError(
606 result.addInternalError(
537607 new(RequiredError),
538608 context,
539609 value,
543613 }
544614
545615 // additionalProperty & patternProperty:
546 if currentSubSchema.additionalProperties != nil {
547
548 switch currentSubSchema.additionalProperties.(type) {
549 case bool:
550
551 if !currentSubSchema.additionalProperties.(bool) {
552
553 for pk := range value {
554
555 found := false
556 for _, spValue := range currentSubSchema.propertiesChildren {
557 if pk == spValue.property {
558 found = true
559 }
560 }
561
562 pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context)
563
564 if found {
565
566 if pp_has && !pp_match {
567 result.addError(
568 new(AdditionalPropertyNotAllowedError),
569 context,
570 value,
571 ErrorDetails{"property": pk},
572 )
573 }
574
575 } else {
576
577 if !pp_has || !pp_match {
578 result.addError(
579 new(AdditionalPropertyNotAllowedError),
580 context,
581 value,
582 ErrorDetails{"property": pk},
583 )
584 }
585
586 }
587 }
588 }
589
590 case *subSchema:
591
592 additionalPropertiesSchema := currentSubSchema.additionalProperties.(*subSchema)
593 for pk := range value {
594
595 found := false
596 for _, spValue := range currentSubSchema.propertiesChildren {
597 if pk == spValue.property {
598 found = true
599 }
600 }
601
602 pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context)
603
604 if found {
605
606 if pp_has && !pp_match {
607 validationResult := additionalPropertiesSchema.subValidateWithContext(value[pk], context)
608 result.mergeErrors(validationResult)
609 }
610
611 } else {
612
613 if !pp_has || !pp_match {
614 validationResult := additionalPropertiesSchema.subValidateWithContext(value[pk], context)
615 result.mergeErrors(validationResult)
616 }
617
618 }
619
620 }
621 }
622 } else {
623
616 for pk := range value {
617
618 // Check whether this property is described by "properties"
619 found := false
620 for _, spValue := range currentSubSchema.propertiesChildren {
621 if pk == spValue.property {
622 found = true
623 }
624 }
625
626 // Check whether this property is described by "patternProperties"
627 ppMatch := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context)
628
629 // If it is not described by neither "properties" nor "patternProperties" it must pass "additionalProperties"
630 if !found && !ppMatch {
631 switch ap := currentSubSchema.additionalProperties.(type) {
632 case bool:
633 // Handle the boolean case separately as it's cleaner to return a specific error than failing to pass the false schema
634 if !ap {
635 result.addInternalError(
636 new(AdditionalPropertyNotAllowedError),
637 context,
638 value[pk],
639 ErrorDetails{"property": pk},
640 )
641
642 }
643 case *subSchema:
644 validationResult := ap.subValidateWithContext(value[pk], NewJsonContext(pk, context))
645 result.mergeErrors(validationResult)
646 }
647 }
648 }
649
650 // propertyNames:
651 if currentSubSchema.propertyNames != nil {
624652 for pk := range value {
625
626 pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context)
627
628 if pp_has && !pp_match {
629
630 result.addError(
631 new(InvalidPropertyPatternError),
653 validationResult := currentSubSchema.propertyNames.subValidateWithContext(pk, context)
654 if !validationResult.Valid() {
655 result.addInternalError(new(InvalidPropertyNameError),
632656 context,
633 value,
634 ErrorDetails{
657 value, ErrorDetails{
635658 "property": pk,
636 "pattern": currentSubSchema.PatternPropertiesString(),
637 },
638 )
639 }
640
659 })
660 result.mergeErrors(validationResult)
661 }
641662 }
642663 }
643664
644665 result.incrementScore()
645666 }
646667
647 func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *jsonContext) (has bool, matched bool) {
668 func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *JsonContext) bool {
648669
649670 if internalLogEnabled {
650671 internalLog("validatePatternProperty %s", context.String())
651672 internalLog(" %s %v", key, value)
652673 }
653674
654 has = false
655
656 validatedkey := false
675 validated := false
657676
658677 for pk, pv := range currentSubSchema.patternProperties {
659678 if matches, _ := regexp.MatchString(pk, key); matches {
660 has = true
661 subContext := newJsonContext(key, context)
679 validated = true
680 subContext := NewJsonContext(key, context)
662681 validationResult := pv.subValidateWithContext(value, subContext)
663682 result.mergeErrors(validationResult)
664 if validationResult.Valid() {
665 validatedkey = true
666 }
667 }
668 }
669
670 if !validatedkey {
671 return has, false
683 }
684 }
685
686 if !validated {
687 return false
672688 }
673689
674690 result.incrementScore()
675
676 return has, true
677 }
678
679 func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) {
691 return true
692 }
693
694 func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
680695
681696 // Ignore JSON numbers
682 if isJsonNumber(value) {
697 if isJSONNumber(value) {
683698 return
684699 }
685700
698713 // minLength & maxLength:
699714 if currentSubSchema.minLength != nil {
700715 if utf8.RuneCount([]byte(stringValue)) < int(*currentSubSchema.minLength) {
701 result.addError(
716 result.addInternalError(
702717 new(StringLengthGTEError),
703718 context,
704719 value,
708723 }
709724 if currentSubSchema.maxLength != nil {
710725 if utf8.RuneCount([]byte(stringValue)) > int(*currentSubSchema.maxLength) {
711 result.addError(
726 result.addInternalError(
712727 new(StringLengthLTEError),
713728 context,
714729 value,
720735 // pattern:
721736 if currentSubSchema.pattern != nil {
722737 if !currentSubSchema.pattern.MatchString(stringValue) {
723 result.addError(
738 result.addInternalError(
724739 new(DoesNotMatchPatternError),
725740 context,
726741 value,
733748 // format
734749 if currentSubSchema.format != "" {
735750 if !FormatCheckers.IsFormat(currentSubSchema.format, stringValue) {
736 result.addError(
751 result.addInternalError(
737752 new(DoesNotMatchFormatError),
738753 context,
739754 value,
745760 result.incrementScore()
746761 }
747762
748 func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) {
763 func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
749764
750765 // Ignore non numbers
751 if !isJsonNumber(value) {
766 if !isJSONNumber(value) {
752767 return
753768 }
754769
758773 }
759774
760775 number := value.(json.Number)
761 float64Value, _ := number.Float64()
776 float64Value, _ := new(big.Rat).SetString(string(number))
762777
763778 // multipleOf:
764779 if currentSubSchema.multipleOf != nil {
765
766 if !isFloat64AnInteger(float64Value / *currentSubSchema.multipleOf) {
767 result.addError(
780 if q := new(big.Rat).Quo(float64Value, currentSubSchema.multipleOf); !q.IsInt() {
781 result.addInternalError(
768782 new(MultipleOfError),
769783 context,
770 resultErrorFormatJsonNumber(number),
771 ErrorDetails{"multiple": *currentSubSchema.multipleOf},
784 number,
785 ErrorDetails{
786 "multiple": new(big.Float).SetRat(currentSubSchema.multipleOf),
787 },
772788 )
773789 }
774790 }
775791
776792 //maximum & exclusiveMaximum:
777793 if currentSubSchema.maximum != nil {
778 if currentSubSchema.exclusiveMaximum {
779 if float64Value >= *currentSubSchema.maximum {
780 result.addError(
781 new(NumberLTError),
782 context,
783 resultErrorFormatJsonNumber(number),
784 ErrorDetails{
785 "max": resultErrorFormatNumber(*currentSubSchema.maximum),
786 },
787 )
788 }
789 } else {
790 if float64Value > *currentSubSchema.maximum {
791 result.addError(
792 new(NumberLTEError),
793 context,
794 resultErrorFormatJsonNumber(number),
795 ErrorDetails{
796 "max": resultErrorFormatNumber(*currentSubSchema.maximum),
797 },
798 )
799 }
794 if float64Value.Cmp(currentSubSchema.maximum) == 1 {
795 result.addInternalError(
796 new(NumberLTEError),
797 context,
798 number,
799 ErrorDetails{
800 "max": new(big.Float).SetRat(currentSubSchema.maximum),
801 },
802 )
803 }
804 }
805 if currentSubSchema.exclusiveMaximum != nil {
806 if float64Value.Cmp(currentSubSchema.exclusiveMaximum) >= 0 {
807 result.addInternalError(
808 new(NumberLTError),
809 context,
810 number,
811 ErrorDetails{
812 "max": new(big.Float).SetRat(currentSubSchema.exclusiveMaximum),
813 },
814 )
800815 }
801816 }
802817
803818 //minimum & exclusiveMinimum:
804819 if currentSubSchema.minimum != nil {
805 if currentSubSchema.exclusiveMinimum {
806 if float64Value <= *currentSubSchema.minimum {
807 result.addError(
808 new(NumberGTError),
809 context,
810 resultErrorFormatJsonNumber(number),
811 ErrorDetails{
812 "min": resultErrorFormatNumber(*currentSubSchema.minimum),
813 },
814 )
815 }
816 } else {
817 if float64Value < *currentSubSchema.minimum {
818 result.addError(
819 new(NumberGTEError),
820 context,
821 resultErrorFormatJsonNumber(number),
822 ErrorDetails{
823 "min": resultErrorFormatNumber(*currentSubSchema.minimum),
824 },
825 )
826 }
820 if float64Value.Cmp(currentSubSchema.minimum) == -1 {
821 result.addInternalError(
822 new(NumberGTEError),
823 context,
824 number,
825 ErrorDetails{
826 "min": new(big.Float).SetRat(currentSubSchema.minimum),
827 },
828 )
829 }
830 }
831 if currentSubSchema.exclusiveMinimum != nil {
832 if float64Value.Cmp(currentSubSchema.exclusiveMinimum) <= 0 {
833 result.addInternalError(
834 new(NumberGTError),
835 context,
836 number,
837 ErrorDetails{
838 "min": new(big.Float).SetRat(currentSubSchema.exclusiveMinimum),
839 },
840 )
841 }
842 }
843
844 // format
845 if currentSubSchema.format != "" {
846 if !FormatCheckers.IsFormat(currentSubSchema.format, float64Value) {
847 result.addInternalError(
848 new(DoesNotMatchFormatError),
849 context,
850 value,
851 ErrorDetails{"format": currentSubSchema.format},
852 )
827853 }
828854 }
829855