Merge branch 'master' of https://github.com/xeipuuv/gojsonschema
Conflicts:
README.md
diff --git a/README.md b/README.md
index bb571f4..123e673 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
An implementation of JSON Schema, based on IETF's draft v4 - Go language
-References:
+References :
* http://json-schema.org
* http://json-schema.org/latest/json-schema-core.html
@@ -18,9 +18,14 @@
go get github.com/xeipuuv/gojsonschema
```
+Dependencies :
+* https://github.com/xeipuuv/gojsonpointer
+* https://github.com/xeipuuv/gojsonreference
+* https://github.com/stretchr/testify/assert
+
## Usage
-### A full working example
+### Example
```go
@@ -28,39 +33,26 @@
import (
"fmt"
- gjs "github.com/xeipuuv/gojsonschema"
+ "github.com/xeipuuv/gojsonschema"
)
func main() {
- schema, err := gjs.NewSchema("file:///home/me/schema.json")
- if err != nil {
- panic(err.Error())
- }
+ schemaLoader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json")
+ documentLoader := gojsonschema.NewReferenceLoader("file:///home/me/document.json")
- document, err := gjs.GetFile("/home/me/document.json")
- if err != nil {
- panic(err.Error())
- }
-
- result, err := schema.Validate(document)
+ result, err := gojsonschema.Validate(schemaLoader, documentLoader)
if err != nil {
panic(err.Error())
}
if result.Valid() {
-
fmt.Printf("The document is valid\n")
-
} else {
-
fmt.Printf("The document is not valid. see errors :\n")
-
- // display validation errors
for _, desc := range result.Errors() {
fmt.Printf("- %s\n", desc)
}
-
}
}
@@ -68,108 +60,72 @@
```
-#### Loading a schema
+#### Loaders
-Schemas can be loaded remotely from a HTTP URL :
+There are various ways to load your JSON data.
+In order to load your schemas and documents,
+first declare an appropriate loader :
+
+* Web / HTTP, using a reference :
```go
- schema, err := gjs.NewSchema("http://myhost/schema.json")
+ loader, err := gojsonschema.NewReferenceLoader("http://www.some_host.com/schema.json")
```
-From a local file, using the file URI scheme:
+* Local file, using a reference :
```go
- schema, err := gjs.NewSchema("file:///home/me/schema.json")
+ loader, err := gojsonschema.NewReferenceLoader("file:///home/me/schema.json")
```
+References use the URI scheme, the prefix (file://) and a full path to the file are required.
-You may also load the schema from within your code, using a map[string]interface{} variable or a JSON string.
-
-Note that schemas loaded from non-HTTP are subject to limitations, they need to be standalone schemas;
-That means references to local files and/or remote files within these schemas will not work.
+* Custom Go types :
```go
- m := map[string]interface{}{
- "type": "string"}
-
- schema, err := gjs.NewSchema(m)
+ m := map[string]interface{}{"type": "string"}
+ loader, err := gojsonschema.NewGoLoader(m)
```
-Or
+* JSON strings :
```go
- schema, err := gjs.NewSchema(`{"type": "string"}`)
-```
-
-#### Loading a JSON
-
-The library virtually accepts any form of JSON since it uses reflection to validate against the schema.
-
-You may use and combine go types like :
-
-* string (JSON string)
-* bool (JSON boolean)
-* float64 (JSON number)
-* nil (JSON null)
-* slice (JSON array)
-* map[string]interface{} (JSON object)
-
-You can declare your JSON from within your code, using a map / interface{} :
-
-```go
- document := map[string]interface{}{
- "name": "john"}
-```
-
-Or a JSON string:
-
-```go
- document := `{"name": "john"}`
-```
-
-Helper functions are also available to load from a HTTP URL :
-
-```go
- document, err := gjs.GetHTTP("http://host/data.json")
-```
-
-Or a local file :
-
-```go
- document, err := gjs.GetFile("/home/me/data.json")
+ loader, err := gojsonschema.NewStringLoader(`{"type": "string"}`)
```
#### Validation
-Once the schema and the JSON to validate are loaded, validation phase becomes easy :
+Once the loaders are set, validation is easy :
```go
- result, err := schema.Validate(document)
+ result, err := gojsonschema.Validate(schemaLoader, documentLoader)
```
-Check the result with:
+Alternatively, you might want to load a schema only once and process to multiple validations :
+
+```go
+ schema, err := gojsonschema.NewSchema(schemaLoader)
+ ...
+ result1, err := schema.Validate(documentLoader1)
+ ...
+ result2, err := schema.Validate(documentLoader2)
+ ...
+ // etc ...
+```
+
+To check the result :
```go
if result.Valid() {
- // Your Json is valid
+ fmt.Printf("The document is valid\n")
+ } else {
+ fmt.Printf("The document is not valid. see errors :\n")
+ for _, desc := range result.Errors() {
+ fmt.Printf("- %s\n", desc)
+ }
}
```
-If not valid, you can loop through the error messages returned by the validation phase :
-
-```go
- for _, desc := range result.Errors() {
- fmt.Printf("Error: %s\n", desc)
- }
-```
-
-## Dependencies
-https://github.com/xeipuuv/gojsonpointer
-
-https://github.com/xeipuuv/gojsonreference
-
-https://github.com/stretchr/testify/assert
-
## Uses
gojsonschema uses the following test suite :
diff --git a/getJson.go b/getJson.go
deleted file mode 100644
index 6a54788..0000000
--- a/getJson.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// author xeipuuv
-// author-github https://github.com/xeipuuv
-// author-mail xeipuuv@gmail.com
-//
-// repository-name gojsonschema
-// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
-//
-// description Helper functions to load JSON files.
-//
-// created 26-02-2013
-
-package gojsonschema
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "net/http"
-)
-
-// Helper function to read a JSON from a HTTP request
-// Must return HTTP Status 200 OK
-func GetHTTP(url string) (interface{}, error) {
-
- resp, err := http.Get(url)
- if err != nil {
- return nil, err
- }
-
- if resp.StatusCode != http.StatusOK {
- return nil, errors.New(fmt.Sprintf(ERROR_MESSAGE_GET_HTTP_BAD_STATUS, resp.Status))
- }
-
- bodyBuff, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
-
- var document interface{}
- err = json.Unmarshal(bodyBuff, &document)
- if err != nil {
- return nil, err
- }
-
- return document, nil
-}
-
-// Helper function to read a JSON from a path
-func GetFile(path string) (interface{}, error) {
-
- bodyBuff, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, err
- }
-
- var document interface{}
- err = json.Unmarshal(bodyBuff, &document)
- if err != nil {
- return nil, err
- }
-
- return document, nil
-}
diff --git a/internalLog.go b/internalLog.go
index 2459fef..ee14ee6 100644
--- a/internalLog.go
+++ b/internalLog.go
@@ -30,7 +30,7 @@
"log"
)
-const internalLogEnabled = false
+const internalLogEnabled = true
func internalLog(format string, v ...interface{}) {
diff --git a/jsonLoader.go b/jsonLoader.go
new file mode 100644
index 0000000..2af72df
--- /dev/null
+++ b/jsonLoader.go
@@ -0,0 +1,280 @@
+// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// author xeipuuv
+// author-github https://github.com/xeipuuv
+// author-mail xeipuuv@gmail.com
+//
+// repository-name gojsonschema
+// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
+//
+// description Different strategies to load JSON files.
+// Includes References (file and HTTP), JSON strings and Go types.
+//
+// created 01-02-2015
+
+package gojsonschema
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/xeipuuv/gojsonreference"
+ "io/ioutil"
+ "net/http"
+ "strings"
+)
+
+// JSON loader interface
+
+type jsonLoader interface {
+ jsonSource() interface{}
+ loadJSON() (interface{}, error)
+ loadSchema() (*Schema, error)
+}
+
+// JSON Reference loader
+// references are used to load JSONs from files and HTTP
+
+type jsonReferenceLoader struct {
+ source string
+}
+
+func (l *jsonReferenceLoader) jsonSource() interface{} {
+ return l.source
+}
+
+func NewReferenceLoader(source string) *jsonReferenceLoader {
+ return &jsonReferenceLoader{source: source}
+}
+
+func (l *jsonReferenceLoader) loadJSON() (interface{}, error) {
+
+ var err error
+
+ reference, err := gojsonreference.NewJsonReference(l.jsonSource().(string))
+ if err != nil {
+ return nil, err
+ }
+
+ refToUrl := reference
+ refToUrl.GetUrl().Fragment = ""
+
+ var document interface{}
+
+ if reference.HasFileScheme {
+
+ filename := strings.Replace(refToUrl.String(), "file://", "", -1)
+ document, err = l.loadFromFile(filename)
+ if err != nil {
+ return nil, err
+ }
+
+ } else {
+
+ document, err = l.loadFromHTTP(refToUrl.String())
+ if err != nil {
+ return nil, err
+ }
+
+ }
+
+ return document, nil
+
+}
+
+func (l *jsonReferenceLoader) loadSchema() (*Schema, error) {
+
+ var err error
+
+ d := Schema{}
+ d.pool = newSchemaPool()
+ d.referencePool = newSchemaReferencePool()
+
+ d.documentReference, err = gojsonreference.NewJsonReference(l.jsonSource().(string))
+ if err != nil {
+ return nil, err
+ }
+
+ spd, err := d.pool.GetDocument(d.documentReference)
+ if err != nil {
+ return nil, err
+ }
+
+ err = d.parse(spd.Document)
+ if err != nil {
+ return nil, err
+ }
+
+ return &d, nil
+
+}
+
+func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
+
+ resp, err := http.Get(address)
+ if err != nil {
+ return nil, err
+ }
+
+ // must return HTTP Status 200 OK
+ if resp.StatusCode != http.StatusOK {
+ return nil, errors.New(fmt.Sprintf(ERROR_MESSAGE_GET_HTTP_BAD_STATUS, resp.Status))
+ }
+
+ bodyBuff, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var document interface{}
+ err = json.Unmarshal(bodyBuff, &document)
+ if err != nil {
+ return nil, err
+ }
+
+ return document, nil
+}
+
+func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
+
+ bodyBuff, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+
+ var document interface{}
+ err = json.Unmarshal(bodyBuff, &document)
+ if err != nil {
+ return nil, err
+ }
+
+ return document, nil
+}
+
+// JSON string loader
+
+type jsonStringLoader struct {
+ source string
+}
+
+func (l *jsonStringLoader) jsonSource() interface{} {
+ return l.source
+}
+
+func NewStringLoader(source string) *jsonStringLoader {
+ return &jsonStringLoader{source: source}
+}
+
+func (l *jsonStringLoader) loadJSON() (interface{}, error) {
+
+ var document interface{}
+
+ err := json.Unmarshal([]byte(l.jsonSource().(string)), &document)
+ if err != nil {
+ return nil, err
+ }
+
+ return document, nil
+
+}
+
+func (l *jsonStringLoader) loadSchema() (*Schema, error) {
+
+ var err error
+
+ document, err := l.loadJSON()
+ if err != nil {
+ return nil, err
+ }
+
+ d := Schema{}
+ d.pool = newSchemaPool()
+ d.referencePool = newSchemaReferencePool()
+ d.documentReference, err = gojsonreference.NewJsonReference("#")
+ d.pool.SetStandaloneDocument(document)
+ if err != nil {
+ return nil, err
+ }
+
+ err = d.parse(document)
+ if err != nil {
+ return nil, err
+ }
+
+ return &d, nil
+
+}
+
+// JSON Go (types) loader
+// used to load JSONs from the code as maps, interface{}, structs ...
+
+type jsonGoLoader struct {
+ source interface{}
+}
+
+func (l *jsonGoLoader) jsonSource() interface{} {
+ return l.source
+}
+
+func NewGoLoader(source interface{}) *jsonGoLoader {
+ return &jsonGoLoader{source: source}
+}
+
+func (l *jsonGoLoader) loadJSON() (interface{}, error) {
+
+ // convert it to a compliant JSON first to avoid types "mismatches"
+
+ jsonBytes, err := json.Marshal(l.jsonSource())
+ if err != nil {
+ return nil, err
+ }
+
+ var document interface{}
+
+ err = json.Unmarshal(jsonBytes, &document)
+ if err != nil {
+ return nil, err
+ }
+
+ return document, nil
+
+}
+
+func (l *jsonGoLoader) loadSchema() (*Schema, error) {
+
+ var err error
+
+ document, err := l.loadJSON()
+ if err != nil {
+ return nil, err
+ }
+
+ d := Schema{}
+ d.pool = newSchemaPool()
+ d.referencePool = newSchemaReferencePool()
+ d.documentReference, err = gojsonreference.NewJsonReference("#")
+ d.pool.SetStandaloneDocument(document)
+ if err != nil {
+ return nil, err
+ }
+
+ err = d.parse(document)
+ if err != nil {
+ return nil, err
+ }
+
+ return &d, nil
+
+}
diff --git a/schema.go b/schema.go
index 8777a3d..1d4e9da 100644
--- a/schema.go
+++ b/schema.go
@@ -27,7 +27,7 @@
package gojsonschema
import (
- "encoding/json"
+ // "encoding/json"
"errors"
"fmt"
"github.com/xeipuuv/gojsonreference"
@@ -35,104 +35,8 @@
"regexp"
)
-func NewSchema(document interface{}) (*Schema, error) {
-
- internalLog("New Schema")
-
- switch document.(type) {
-
- // document is a string:
- // Could be a JSON string or a JSON reference string ( file or HTTP scheme )
- // Use inferring to determine proper way to load this document
-
- case string:
-
- internalLog(" From string argument")
-
- var m map[string]interface{}
- err := json.Unmarshal([]byte(document.(string)), &m)
- if err != nil {
- internalLog(" Inferring JSON reference %s", document.(string))
- return newSchemaDocumentFromReference(document.(string))
- } else {
- internalLog(" Inferring JSON string")
- return newSchemaDocumentFromMap(m)
- }
-
- // document is a Go Map
-
- case map[string]interface{}:
-
- internalLog(" From map argument")
-
- // convert the map to a compliant JSON map
-
- jsonBytes, err := json.Marshal(document)
- if err != nil {
- return nil, err
- }
-
- var m map[string]interface{}
- err = json.Unmarshal(jsonBytes, &m)
- if err != nil {
- return nil, err
- }
-
- return newSchemaDocumentFromMap(m)
-
- default:
-
- return nil, errors.New(ERROR_MESSAGE_NEW_SCHEMA_DOCUMENT_INVALID_ARGUMENT)
-
- }
-
- return nil, nil
-}
-
-func newSchemaDocumentFromMap(document map[string]interface{}) (*Schema, error) {
-
- d := Schema{}
- d.pool = newSchemaPool()
- d.referencePool = newSchemaReferencePool()
-
- var err error
-
- d.documentReference, err = gojsonreference.NewJsonReference("#")
- d.pool.SetStandaloneDocument(document)
- if err != nil {
- return nil, err
- }
-
- err = d.parse(document)
- if err != nil {
- return nil, err
- }
-
- return &d, nil
-
-}
-
-func newSchemaDocumentFromReference(document string) (*Schema, error) {
-
- var err error
-
- d := Schema{}
- d.pool = newSchemaPool()
- d.referencePool = newSchemaReferencePool()
-
- d.documentReference, err = gojsonreference.NewJsonReference(document)
- spd, err := d.pool.GetDocument(d.documentReference)
- if err != nil {
- return nil, err
- }
-
- err = d.parse(spd.Document)
- if err != nil {
- return nil, err
- }
-
- return &d, nil
-
+func NewSchema(l jsonLoader) (*Schema, error) {
+ return l.loadSchema()
}
type Schema struct {
diff --git a/schemaPool.go b/schemaPool.go
index dab2620..67e5fb8 100644
--- a/schemaPool.go
+++ b/schemaPool.go
@@ -29,8 +29,6 @@
import (
"errors"
"fmt"
- "strings"
-
"github.com/xeipuuv/gojsonreference"
)
@@ -88,31 +86,10 @@
return spd, nil
}
- // Load the document
-
- var document interface{}
-
- if reference.HasFileScheme {
-
- internalLog(" From file")
-
- // Load from file
- filename := strings.Replace(refToUrl.String(), "file://", "", -1)
- document, err = GetFile(filename)
- if err != nil {
- return nil, err
- }
-
- } else {
-
- internalLog(" From HTTP")
-
- // Load from HTTP
- document, err = GetHTTP(refToUrl.String())
- if err != nil {
- return nil, err
- }
-
+ jsonReferenceLoader := NewReferenceLoader(reference.String())
+ document, err := jsonReferenceLoader.loadJSON()
+ if err != nil {
+ return nil, err
}
spd = &schemaPoolDocument{Document: document}
diff --git a/schema_test.go b/schema_test.go
index 3866108..01ff133 100644
--- a/schema_test.go
+++ b/schema_test.go
@@ -314,27 +314,18 @@
fmt.Printf("Test (%d) | %s :: %s\n", testJsonIndex, testJson["phase"], testJson["test"])
- // get schema
- schemaDocument, err := NewSchema("file://" + testwd + "/" + testJson["schema"])
- if err != nil {
- t.Errorf("Cound not parse schema : %s\n", err.Error())
- }
-
- // get data
- dataDocument, err := GetFile(testwd + "/" + testJson["data"])
- if err != nil {
- t.Errorf("Could not get test data : %s\n", err.Error())
- }
+ schemaLoader := NewReferenceLoader("file://" + testwd + "/" + testJson["schema"])
+ documentLoader := NewReferenceLoader("file://" + testwd + "/" + testJson["data"])
// validate
- validationResult, err := schemaDocument.Validate(dataDocument)
+ result, err := Validate(schemaLoader, documentLoader)
if err != nil {
t.Errorf("Error (%s)\n", err.Error())
}
- givenValid := validationResult.Valid()
+ givenValid := result.Valid()
if displayErrorMessages {
- for vErrI, vErr := range validationResult.Errors() {
+ for vErrI, vErr := range result.Errors() {
fmt.Printf(" Error (%d) | %s\n", vErrI, vErr)
}
}
diff --git a/validation.go b/validation.go
index 2d1dd6d..5dde0bf 100644
--- a/validation.go
+++ b/validation.go
@@ -26,7 +26,6 @@
package gojsonschema
import (
- "encoding/json"
"fmt"
"reflect"
"regexp"
@@ -35,56 +34,30 @@
"unicode/utf8"
)
-func (v *Schema) Validate(document interface{}) (*Result, error) {
+func Validate(ls jsonLoader, ld jsonLoader) (*Result, error) {
- internalLog("Validate")
+ var err error
- var root interface{}
+ // load schema
- switch document.(type) {
+ schema, err := NewSchema(ls)
+ if err != nil {
+ return nil, err
+ }
- // document is a JSON string or a single JSON string element
+ // begine validation
- case string:
+ return schema.Validate(ld)
- trimmedString := strings.TrimSpace(document.(string))
+}
- // is a JSON string
- if strings.HasPrefix(trimmedString, "{") || strings.HasPrefix(trimmedString, "[") {
+func (v *Schema) Validate(l jsonLoader) (*Result, error) {
- internalLog(" From JSON string")
+ // load document
- err := json.Unmarshal([]byte(document.(string)), &root)
- if err != nil {
- return nil, err
- }
-
- } else { // is a single JSON string element
-
- internalLog(" From JSON string element")
-
- root = document.(string)
-
- }
-
- // otherwise
-
- default:
-
- internalLog(" From JSON object")
-
- // Turn it into a compliant JSON
-
- jsonBytes, err := json.Marshal(document)
- if err != nil {
- return nil, err
- }
-
- err = json.Unmarshal(jsonBytes, &root)
- if err != nil {
- return nil, err
- }
-
+ root, err := l.loadJSON()
+ if err != nil {
+ return nil, err
}
// begin validation