• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..28-Jan-2021-

README.mdH A D28-Jan-202115.1 KiB467347

draft.goH A D28-Jan-202110.3 KiB12691

errors.goH A D28-Jan-20219 KiB365246

format_checkers.goH A D28-Jan-20219.7 KiB369219

go.modH A D28-Jan-2021240 86

internalLog.goH A D28-Jan-20211.1 KiB388

jsonContext.goH A D28-Jan-20212 KiB7434

jsonLoader.goH A D28-Jan-20219.5 KiB387242

locales.goH A D28-Jan-202116.7 KiB473227

result.goH A D28-Jan-20217 KiB221120

schema.goH A D28-Jan-202129.1 KiB1,088929

schemaLoader.goH A D28-Jan-20214.5 KiB207140

schemaPool.goH A D28-Jan-20216.2 KiB216136

schemaReferencePool.goH A D28-Jan-20211.7 KiB6932

schemaType.goH A D28-Jan-20212.1 KiB8439

subSchema.goH A D28-Jan-20214.1 KiB15093

types.goH A D28-Jan-20211.6 KiB6329

utils.goH A D28-Jan-20214.1 KiB198117

validation.goH A D28-Jan-202122.7 KiB859663

README.md

1[![GoDoc](https://godoc.org/github.com/xeipuuv/gojsonschema?status.svg)](https://godoc.org/github.com/xeipuuv/gojsonschema)
2[![Build Status](https://travis-ci.org/xeipuuv/gojsonschema.svg)](https://travis-ci.org/xeipuuv/gojsonschema)
3[![Go Report Card](https://goreportcard.com/badge/github.com/xeipuuv/gojsonschema)](https://goreportcard.com/report/github.com/xeipuuv/gojsonschema)
4
5# gojsonschema
6
7## Description
8
9An implementation of JSON Schema for the Go  programming language. Supports draft-04, draft-06 and draft-07.
10
11References :
12
13* http://json-schema.org
14* http://json-schema.org/latest/json-schema-core.html
15* http://json-schema.org/latest/json-schema-validation.html
16
17## Installation
18
19```
20go get github.com/xeipuuv/gojsonschema
21```
22
23Dependencies :
24* [github.com/xeipuuv/gojsonpointer](https://github.com/xeipuuv/gojsonpointer)
25* [github.com/xeipuuv/gojsonreference](https://github.com/xeipuuv/gojsonreference)
26* [github.com/stretchr/testify/assert](https://github.com/stretchr/testify#assert-package)
27
28## Usage
29
30### Example
31
32```go
33
34package main
35
36import (
37    "fmt"
38    "github.com/xeipuuv/gojsonschema"
39)
40
41func main() {
42
43    schemaLoader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json")
44    documentLoader := gojsonschema.NewReferenceLoader("file:///home/me/document.json")
45
46    result, err := gojsonschema.Validate(schemaLoader, documentLoader)
47    if err != nil {
48        panic(err.Error())
49    }
50
51    if result.Valid() {
52        fmt.Printf("The document is valid\n")
53    } else {
54        fmt.Printf("The document is not valid. see errors :\n")
55        for _, desc := range result.Errors() {
56            fmt.Printf("- %s\n", desc)
57        }
58    }
59}
60
61
62```
63
64#### Loaders
65
66There are various ways to load your JSON data.
67In order to load your schemas and documents,
68first declare an appropriate loader :
69
70* Web / HTTP, using a reference :
71
72```go
73loader := gojsonschema.NewReferenceLoader("http://www.some_host.com/schema.json")
74```
75
76* Local file, using a reference :
77
78```go
79loader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json")
80```
81
82References use the URI scheme, the prefix (file://) and a full path to the file are required.
83
84* JSON strings :
85
86```go
87loader := gojsonschema.NewStringLoader(`{"type": "string"}`)
88```
89
90* Custom Go types :
91
92```go
93m := map[string]interface{}{"type": "string"}
94loader := gojsonschema.NewGoLoader(m)
95```
96
97And
98
99```go
100type Root struct {
101	Users []User `json:"users"`
102}
103
104type User struct {
105	Name string `json:"name"`
106}
107
108...
109
110data := Root{}
111data.Users = append(data.Users, User{"John"})
112data.Users = append(data.Users, User{"Sophia"})
113data.Users = append(data.Users, User{"Bill"})
114
115loader := gojsonschema.NewGoLoader(data)
116```
117
118#### Validation
119
120Once the loaders are set, validation is easy :
121
122```go
123result, err := gojsonschema.Validate(schemaLoader, documentLoader)
124```
125
126Alternatively, you might want to load a schema only once and process to multiple validations :
127
128```go
129schema, err := gojsonschema.NewSchema(schemaLoader)
130...
131result1, err := schema.Validate(documentLoader1)
132...
133result2, err := schema.Validate(documentLoader2)
134...
135// etc ...
136```
137
138To check the result :
139
140```go
141    if result.Valid() {
142    	fmt.Printf("The document is valid\n")
143    } else {
144        fmt.Printf("The document is not valid. see errors :\n")
145        for _, err := range result.Errors() {
146        	// Err implements the ResultError interface
147            fmt.Printf("- %s\n", err)
148        }
149    }
150```
151
152
153## Loading local schemas
154
155By 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`.
156
157```go
158	sl := gojsonschema.NewSchemaLoader()
159	loader1 := gojsonschema.NewStringLoader(`{ "type" : "string" }`)
160	err := sl.AddSchema("http://some_host.com/string.json", loader1)
161```
162
163Alternatively if your schema already has an `$id` you can use the `AddSchemas` function
164```go
165	loader2 := gojsonschema.NewStringLoader(`{
166			"$id" : "http://some_host.com/maxlength.json",
167			"maxLength" : 5
168		}`)
169	err = sl.AddSchemas(loader2)
170```
171
172The main schema should be passed to the `Compile` function. This main schema can then directly reference the added schemas without needing to download them.
173```go
174	loader3 := gojsonschema.NewStringLoader(`{
175		"$id" : "http://some_host.com/main.json",
176		"allOf" : [
177			{ "$ref" : "http://some_host.com/string.json" },
178			{ "$ref" : "http://some_host.com/maxlength.json" }
179		]
180	}`)
181
182	schema, err := sl.Compile(loader3)
183
184	documentLoader := gojsonschema.NewStringLoader(`"hello world"`)
185
186	result, err := schema.Validate(documentLoader)
187```
188
189It's also possible to pass a `ReferenceLoader` to the `Compile` function that references a loaded schema.
190
191```go
192err = sl.AddSchemas(loader3)
193schema, err := sl.Compile(gojsonschema.NewReferenceLoader("http://some_host.com/main.json"))
194```
195
196Schemas added by `AddSchema` and `AddSchemas` are only validated when the entire schema is compiled, unless meta-schema validation is used.
197
198## Using a specific draft
199By 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.
200
201Autodectection can be turned off with the `AutoDetect` property. Specific draft versions can be specified with the `Draft` property.
202
203```go
204sl := gojsonschema.NewSchemaLoader()
205sl.Draft = gojsonschema.Draft7
206sl.AutoDetect = false
207```
208
209If 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.
210
211## Meta-schema validation
212Schemas that are added using the `AddSchema`, `AddSchemas` and `Compile` can be validated against their meta-schema by setting the `Validate` property.
213
214The 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.
215
216```go
217sl := gojsonschema.NewSchemaLoader()
218sl.Validate = true
219err := sl.AddSchemas(gojsonschema.NewStringLoader(`{
220     $id" : "http://some_host.com/invalid.json",
221    "$schema": "http://json-schema.org/draft-07/schema#",
222    "multipleOf" : true
223}`))
224 ```
225```
226 ```
227
228Errors returned by meta-schema validation are more readable and contain more information, which helps significantly if you are developing a schema.
229
230Meta-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.
231
232
233## Working with Errors
234
235The library handles string error codes which you can customize by creating your own gojsonschema.locale and setting it
236```go
237gojsonschema.Locale = YourCustomLocale{}
238```
239
240However, each error contains additional contextual information.
241
242Newer versions of `gojsonschema` may have new additional errors, so code that uses a custom locale will need to be updated when this happens.
243
244**err.Type()**: *string* Returns the "type" of error that occurred. Note you can also type check. See below
245
246Note: An error of RequiredType has an err.Type() return value of "required"
247
248    "required": RequiredError
249    "invalid_type": InvalidTypeError
250    "number_any_of": NumberAnyOfError
251    "number_one_of": NumberOneOfError
252    "number_all_of": NumberAllOfError
253    "number_not": NumberNotError
254    "missing_dependency": MissingDependencyError
255    "internal": InternalError
256    "const": ConstEror
257    "enum": EnumError
258    "array_no_additional_items": ArrayNoAdditionalItemsError
259    "array_min_items": ArrayMinItemsError
260    "array_max_items": ArrayMaxItemsError
261    "unique": ItemsMustBeUniqueError
262    "contains" : ArrayContainsError
263    "array_min_properties": ArrayMinPropertiesError
264    "array_max_properties": ArrayMaxPropertiesError
265    "additional_property_not_allowed": AdditionalPropertyNotAllowedError
266    "invalid_property_pattern": InvalidPropertyPatternError
267    "invalid_property_name":  InvalidPropertyNameError
268    "string_gte": StringLengthGTEError
269    "string_lte": StringLengthLTEError
270    "pattern": DoesNotMatchPatternError
271    "multiple_of": MultipleOfError
272    "number_gte": NumberGTEError
273    "number_gt": NumberGTError
274    "number_lte": NumberLTEError
275    "number_lt": NumberLTError
276    "condition_then" : ConditionThenError
277    "condition_else" : ConditionElseError
278
279**err.Value()**: *interface{}* Returns the value given
280
281**err.Context()**: *gojsonschema.JsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName
282
283**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.
284
285**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.
286
287**err.DescriptionFormat()**: *string* The error description format. This is relevant if you are adding custom validation errors afterwards to the result.
288
289**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()*
290
291Note 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.
292```
293{{.field}} must be greater than or equal to {{.min}}
294```
295
296The library allows you to specify custom template functions, should you require more complex error message handling.
297```go
298gojsonschema.ErrorTemplateFuncs = map[string]interface{}{
299	"allcaps": func(s string) string {
300		return strings.ToUpper(s)
301	},
302}
303```
304
305Given the above definition, you can use the custom function `"allcaps"` in your localization templates:
306```
307{{allcaps .field}} must be greater than or equal to {{.min}}
308```
309
310The above error message would then be rendered with the `field` value in capital letters. For example:
311```
312"PASSWORD must be greater than or equal to 8"
313```
314
315Learn 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.
316
317## Formats
318JSON 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:
319
320````json
321{"type": "string", "format": "email"}
322````
323
324Not all formats defined in draft-07 are available. Implemented formats are:
325
326* `date`
327* `time`
328* `date-time`
329* `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.
330* `email`. Go's email parser deviates slightly from [RFC5322](https://tools.ietf.org/html/rfc5322). Includes unicode support.
331* `idn-email`. Same caveat as `email`.
332* `ipv4`
333* `ipv6`
334* `uri`. Includes unicode support.
335* `uri-reference`. Includes unicode support.
336* `iri`
337* `iri-reference`
338* `uri-template`
339* `uuid`
340* `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.
341* `json-pointer`
342* `relative-json-pointer`
343
344`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
345unicode enabled formats for the sake of interoperability as other implementations might not support unicode in the regular formats.
346
347The validation code for `uri`, `idn-email` and their relatives use mostly standard library code.
348
349For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this:
350
351```go
352// Define the format checker
353type RoleFormatChecker struct {}
354
355// Ensure it meets the gojsonschema.FormatChecker interface
356func (f RoleFormatChecker) IsFormat(input interface{}) bool {
357
358    asString, ok := input.(string)
359    if ok == false {
360        return false
361    }
362
363    return strings.HasPrefix("ROLE_", asString)
364}
365
366// Add it to the library
367gojsonschema.FormatCheckers.Add("role", RoleFormatChecker{})
368````
369
370Now to use in your json schema:
371````json
372{"type": "string", "format": "role"}
373````
374
375Another example would be to check if the provided integer matches an id on database:
376
377JSON schema:
378```json
379{"type": "integer", "format": "ValidUserId"}
380```
381
382```go
383// Define the format checker
384type ValidUserIdFormatChecker struct {}
385
386// Ensure it meets the gojsonschema.FormatChecker interface
387func (f ValidUserIdFormatChecker) IsFormat(input interface{}) bool {
388
389    asFloat64, ok := input.(float64) // Numbers are always float64 here
390    if ok == false {
391        return false
392    }
393
394    // XXX
395    // do the magic on the database looking for the int(asFloat64)
396
397    return true
398}
399
400// Add it to the library
401gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{})
402````
403
404Formats can also be removed, for example if you want to override one of the formats that is defined by default.
405
406```go
407gojsonschema.FormatCheckers.Remove("hostname")
408```
409
410
411## Additional custom validation
412After the validation has run and you have the results, you may add additional
413errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead
414of having to add special exceptions for your own errors. Below is an example.
415
416```go
417type AnswerInvalidError struct {
418    gojsonschema.ResultErrorFields
419}
420
421func newAnswerInvalidError(context *gojsonschema.JsonContext, value interface{}, details gojsonschema.ErrorDetails) *AnswerInvalidError {
422    err := AnswerInvalidError{}
423    err.SetContext(context)
424    err.SetType("custom_invalid_error")
425    // it is important to use SetDescriptionFormat() as this is used to call SetDescription() after it has been parsed
426    // using the description of err will be overridden by this.
427    err.SetDescriptionFormat("Answer to the Ultimate Question of Life, the Universe, and Everything is {{.answer}}")
428    err.SetValue(value)
429    err.SetDetails(details)
430
431    return &err
432}
433
434func main() {
435    // ...
436    schema, err := gojsonschema.NewSchema(schemaLoader)
437    result, err := gojsonschema.Validate(schemaLoader, documentLoader)
438
439    if true { // some validation
440        jsonContext := gojsonschema.NewJsonContext("question", nil)
441        errDetail := gojsonschema.ErrorDetails{
442            "answer": 42,
443        }
444        result.AddError(
445            newAnswerInvalidError(
446                gojsonschema.NewJsonContext("answer", jsonContext),
447                52,
448                errDetail,
449            ),
450            errDetail,
451        )
452    }
453
454    return result, err
455
456}
457```
458
459This is especially useful if you want to add validation beyond what the
460json schema drafts can provide such business specific logic.
461
462## Uses
463
464gojsonschema uses the following test suite :
465
466https://github.com/json-schema/JSON-Schema-Test-Suite
467