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
4# gojsonschema
5
6## Description
7
8An implementation of JSON Schema for the Go programming language. Supports draft-04, draft-06 and draft-07.
9
10References :
11
12* http://json-schema.org
13* http://json-schema.org/latest/json-schema-core.html
14* http://json-schema.org/latest/json-schema-validation.html
15
16## Installation
17
18```
19go get github.com/xeipuuv/gojsonschema
20```
21
22Dependencies :
23* [github.com/xeipuuv/gojsonpointer](https://github.com/xeipuuv/gojsonpointer)
24* [github.com/xeipuuv/gojsonreference](https://github.com/xeipuuv/gojsonreference)
25* [github.com/stretchr/testify/assert](https://github.com/stretchr/testify#assert-package)
26
27## Usage
28
29### Example
30
31```go
32
33package main
34
35import (
36 "fmt"
37 "github.com/xeipuuv/gojsonschema"
38)
39
40func main() {
41
42 schemaLoader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json")
43 documentLoader := gojsonschema.NewReferenceLoader("file:///home/me/document.json")
44
45 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
46 if err != nil {
47 panic(err.Error())
48 }
49
50 if result.Valid() {
51 fmt.Printf("The document is valid\n")
52 } else {
53 fmt.Printf("The document is not valid. see errors :\n")
54 for _, desc := range result.Errors() {
55 fmt.Printf("- %s\n", desc)
56 }
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## Working with Errors
153
154The library handles string error codes which you can customize by creating your own gojsonschema.locale and setting it
155```go
156gojsonschema.Locale = YourCustomLocale{}
157```
158
159However, each error contains additional contextual information.
160
161Newer versions of `gojsonschema` may have new additional errors, so code that uses a custom locale will need to be updated when this happens.
162
163**err.Type()**: *string* Returns the "type" of error that occurred. Note you can also type check. See below
164
165Note: An error of RequiredType has an err.Type() return value of "required"
166
167 "required": RequiredError
168 "invalid_type": InvalidTypeError
169 "number_any_of": NumberAnyOfError
170 "number_one_of": NumberOneOfError
171 "number_all_of": NumberAllOfError
172 "number_not": NumberNotError
173 "missing_dependency": MissingDependencyError
174 "internal": InternalError
175 "const": ConstEror
176 "enum": EnumError
177 "array_no_additional_items": ArrayNoAdditionalItemsError
178 "array_min_items": ArrayMinItemsError
179 "array_max_items": ArrayMaxItemsError
180 "unique": ItemsMustBeUniqueError
181 "contains" : ArrayContainsError
182 "array_min_properties": ArrayMinPropertiesError
183 "array_max_properties": ArrayMaxPropertiesError
184 "additional_property_not_allowed": AdditionalPropertyNotAllowedError
185 "invalid_property_pattern": InvalidPropertyPatternError
186 "invalid_property_name": InvalidPropertyNameError
187 "string_gte": StringLengthGTEError
188 "string_lte": StringLengthLTEError
189 "pattern": DoesNotMatchPatternError
190 "multiple_of": MultipleOfError
191 "number_gte": NumberGTEError
192 "number_gt": NumberGTError
193 "number_lte": NumberLTEError
194 "number_lt": NumberLTError
195
196**err.Value()**: *interface{}* Returns the value given
197
198**err.Context()**: *gojsonschema.JsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName
199
200**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.
201
202**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.
203
204**err.DescriptionFormat()**: *string* The error description format. This is relevant if you are adding custom validation errors afterwards to the result.
205
206**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()*
207
208Note 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.
209```
210{{.field}} must be greater than or equal to {{.min}}
211```
212
213The library allows you to specify custom template functions, should you require more complex error message handling.
214```go
215gojsonschema.ErrorTemplateFuncs = map[string]interface{}{
216 "allcaps": func(s string) string {
217 return strings.ToUpper(s)
218 },
219}
220```
221
222Given the above definition, you can use the custom function `"allcaps"` in your localization templates:
223```
224{{allcaps .field}} must be greater than or equal to {{.min}}
225```
226
227The above error message would then be rendered with the `field` value in capital letters. For example:
228```
229"PASSWORD must be greater than or equal to 8"
230```
231
232Learn 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.
233
234## Formats
235JSON 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:
236````json
237{"type": "string", "format": "email"}
238````
239Available formats: date-time, hostname, email, ipv4, ipv6, uri, uri-reference, uuid, regex. Some of the new formats in draft-06 and draft-07 are not yet implemented.
240
241For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this:
242
243```go
244// Define the format checker
245type RoleFormatChecker struct {}
246
247// Ensure it meets the gojsonschema.FormatChecker interface
248func (f RoleFormatChecker) IsFormat(input interface{}) bool {
249
250 asString, ok := input.(string)
251 if ok == false {
252 return false
253 }
254
255 return strings.HasPrefix("ROLE_", asString)
256}
257
258// Add it to the library
259gojsonschema.FormatCheckers.Add("role", RoleFormatChecker{})
260````
261
262Now to use in your json schema:
263````json
264{"type": "string", "format": "role"}
265````
266
267Another example would be to check if the provided integer matches an id on database:
268
269JSON schema:
270```json
271{"type": "integer", "format": "ValidUserId"}
272```
273
274```go
275// Define the format checker
276type ValidUserIdFormatChecker struct {}
277
278// Ensure it meets the gojsonschema.FormatChecker interface
279func (f ValidUserIdFormatChecker) IsFormat(input interface{}) bool {
280
281 asFloat64, ok := input.(float64) // Numbers are always float64 here
282 if ok == false {
283 return false
284 }
285
286 // XXX
287 // do the magic on the database looking for the int(asFloat64)
288
289 return true
290}
291
292// Add it to the library
293gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{})
294````
295
296## Additional custom validation
297After the validation has run and you have the results, you may add additional
298errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead
299of having to add special exceptions for your own errors. Below is an example.
300
301```go
302type AnswerInvalidError struct {
303 gojsonschema.ResultErrorFields
304}
305
306func newAnswerInvalidError(context *gojsonschema.JsonContext, value interface{}, details gojsonschema.ErrorDetails) *AnswerInvalidError {
307 err := AnswerInvalidError{}
308 err.SetContext(context)
309 err.SetType("custom_invalid_error")
310 // it is important to use SetDescriptionFormat() as this is used to call SetDescription() after it has been parsed
311 // using the description of err will be overridden by this.
312 err.SetDescriptionFormat("Answer to the Ultimate Question of Life, the Universe, and Everything is {{.answer}}")
313 err.SetValue(value)
314 err.SetDetails(details)
315
316 return &err
317}
318
319func main() {
320 // ...
321 schema, err := gojsonschema.NewSchema(schemaLoader)
322 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
323
324 if true { // some validation
325 jsonContext := gojsonschema.NewJsonContext("question", nil)
326 errDetail := gojsonschema.ErrorDetails{
327 "answer": 42,
328 }
329 result.AddError(
330 newAnswerInvalidError(
331 gojsonschema.NewJsonContext("answer", jsonContext),
332 52,
333 errDetail,
334 ),
335 errDetail,
336 )
337 }
338
339 return result, err
340
341}
342```
343
344This is especially useful if you want to add validation beyond what the
345json schema drafts can provide such business specific logic.
346
347## Uses
348
349gojsonschema uses the following test suite :
350
351https://github.com/json-schema/JSON-Schema-Test-Suite
352