Implement id parsing in conjuction with $ref fixes
diff --git a/schema.go b/schema.go
index 2cac71e..55cb1cf 100644
--- a/schema.go
+++ b/schema.go
@@ -27,7 +27,6 @@
package gojsonschema
import (
- // "encoding/json"
"errors"
"reflect"
"regexp"
@@ -56,10 +55,11 @@
d.documentReference = ref
d.referencePool = newSchemaReferencePool()
+ var spd *schemaPoolDocument
var doc interface{}
if ref.String() != "" {
// Get document from schema pool
- spd, err := d.pool.GetDocument(d.documentReference)
+ spd, err = d.pool.GetDocument(d.documentReference)
if err != nil {
return nil, err
}
@@ -70,8 +70,8 @@
if err != nil {
return nil, err
}
- d.pool.SetStandaloneDocument(doc)
}
+ d.pool.SetStandaloneDocument(doc)
err = d.parse(doc)
if err != nil {
@@ -113,12 +113,48 @@
},
))
}
+ if currentSchema.parent == nil {
+ currentSchema.ref = &d.documentReference
+ currentSchema.id = &d.documentReference
+ }
+
+ if currentSchema.id == nil && currentSchema.parent != nil {
+ currentSchema.id = currentSchema.parent.id
+ }
m := documentNode.(map[string]interface{})
- if currentSchema == d.rootSchema {
- currentSchema.ref = &d.documentReference
+ // id
+ if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": TYPE_STRING,
+ "given": KEY_ID,
+ },
+ ))
}
+ if k, ok := m[KEY_ID].(string); ok {
+ jsonReference, err := gojsonreference.NewJsonReference(k)
+ if err != nil {
+ return err
+ }
+ if currentSchema == d.rootSchema {
+ currentSchema.id = &jsonReference
+ } else {
+ ref, err := currentSchema.parent.id.Inherits(jsonReference)
+ if err != nil {
+ return err
+ }
+ currentSchema.id = ref
+ }
+ }
+
+ // Add schema to document cache. The same id is passed down to subsequent
+ // subschemas, but as only the first and top one is used it will always reference
+ // the correct schema. Doing it once here prevents having
+ // to do this same step at every corner case.
+ d.referencePool.Add(currentSchema.id.String(), currentSchema)
// $subSchema
if existsMapKey(m, KEY_SCHEMA) {
@@ -159,19 +195,17 @@
if jsonReference.HasFullUrl {
currentSchema.ref = &jsonReference
} else {
- inheritedReference, err := currentSchema.ref.Inherits(jsonReference)
+ inheritedReference, err := currentSchema.id.Inherits(jsonReference)
if err != nil {
return err
}
-
currentSchema.ref = inheritedReference
}
-
- if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok {
+ if sch, ok := d.referencePool.Get(currentSchema.ref.String()); ok {
currentSchema.refSchema = sch
-
} else {
- err := d.parseReference(documentNode, currentSchema, k)
+ err := d.parseReference(documentNode, currentSchema)
+
if err != nil {
return err
}
@@ -186,11 +220,23 @@
currentSchema.definitions = make(map[string]*subSchema)
for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) {
if isKind(dv, reflect.Map) {
- newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref}
- currentSchema.definitions[dk] = newSchema
- err := d.parseSchema(dv, newSchema)
+
+ ref, err := gojsonreference.NewJsonReference("#/" + KEY_DEFINITIONS + "/" + dk)
if err != nil {
- return errors.New(err.Error())
+ return err
+ }
+
+ newSchemaID, err := currentSchema.id.Inherits(ref)
+ if err != nil {
+ return err
+ }
+ newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, id: newSchemaID}
+ currentSchema.definitions[dk] = newSchema
+
+ err = d.parseSchema(dv, newSchema)
+
+ if err != nil {
+ return err
}
} else {
return errors.New(formatErrorDescription(
@@ -214,20 +260,6 @@
}
- // id
- if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) {
- return errors.New(formatErrorDescription(
- Locale.InvalidType(),
- ErrorDetails{
- "expected": TYPE_STRING,
- "given": KEY_ID,
- },
- ))
- }
- if k, ok := m[KEY_ID].(string); ok {
- currentSchema.id = &k
- }
-
// title
if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) {
return errors.New(formatErrorDescription(
@@ -798,26 +830,32 @@
return nil
}
-func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) error {
- var refdDocumentNode interface{}
+func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema) error {
+ var (
+ refdDocumentNode interface{}
+ dsp *schemaPoolDocument
+ err error
+ )
jsonPointer := currentSchema.ref.GetPointer()
standaloneDocument := d.pool.GetStandaloneDocument()
- if standaloneDocument != nil {
+ newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
- var err error
+ if currentSchema.ref.HasFragmentOnly {
refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument)
if err != nil {
return err
}
} else {
- dsp, err := d.pool.GetDocument(*currentSchema.ref)
+ dsp, err = d.pool.GetDocument(*currentSchema.ref)
if err != nil {
return err
}
+ newSchema.id = currentSchema.ref
refdDocumentNode, _, err = jsonPointer.Get(dsp.Document)
+
if err != nil {
return err
}
@@ -833,10 +871,8 @@
// returns the loaded referenced subSchema for the caller to update its current subSchema
newSchemaDocument := refdDocumentNode.(map[string]interface{})
- newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
- d.referencePool.Add(currentSchema.ref.String()+reference, newSchema)
- err := d.parseSchema(newSchemaDocument, newSchema)
+ err = d.parseSchema(newSchemaDocument, newSchema)
if err != nil {
return err
}
diff --git a/schemaPool.go b/schemaPool.go
index f2ad641..ff9715f 100644
--- a/schemaPool.go
+++ b/schemaPool.go
@@ -62,12 +62,16 @@
func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*schemaPoolDocument, error) {
+ var (
+ spd *schemaPoolDocument
+ ok bool
+ err error
+ )
+
if internalLogEnabled {
internalLog("Get Document ( %s )", reference.String())
}
- var err error
-
// It is not possible to load anything that is not canonical...
if !reference.IsCanonical() {
return nil, errors.New(formatErrorDescription(
@@ -75,20 +79,10 @@
ErrorDetails{"reference": reference},
))
}
-
refToUrl := reference
refToUrl.GetUrl().Fragment = ""
- var spd *schemaPoolDocument
-
- // Try to find the requested document in the pool
- for k := range p.schemaPoolDocuments {
- if k == refToUrl.String() {
- spd = p.schemaPoolDocuments[k]
- }
- }
-
- if spd != nil {
+ if spd, ok = p.schemaPoolDocuments[refToUrl.String()]; ok {
if internalLogEnabled {
internalLog(" From pool")
}
diff --git a/schemaReferencePool.go b/schemaReferencePool.go
index 294e36a..6e5e1b5 100644
--- a/schemaReferencePool.go
+++ b/schemaReferencePool.go
@@ -62,6 +62,7 @@
if internalLogEnabled {
internalLog(fmt.Sprintf("Add Schema Reference %s to pool", ref))
}
-
- p.documents[ref] = sch
+ if _, ok := p.documents[ref]; !ok {
+ p.documents[ref] = sch
+ }
}
diff --git a/schema_test.go b/schema_test.go
index 453dfb8..9f47e4b 100644
--- a/schema_test.go
+++ b/schema_test.go
@@ -360,12 +360,10 @@
{"phase": "format validation", "test": "uri format is invalid", "schema": "format/schema_6.json", "data": "format/data_13.json", "valid": "false", "errors": "format"},
{"phase": "format validation", "test": "number format is valid", "schema": "format/schema_7.json", "data": "format/data_29.json", "valid": "true"},
{"phase": "format validation", "test": "number format is valid", "schema": "format/schema_7.json", "data": "format/data_30.json", "valid": "false", "errors": "format"},
+ {"phase": "change resolution scope", "test": "changed scope ref valid", "schema": "refRemote/schema_3.json", "data": "refRemote/data_30.json", "valid": "true"},
+ {"phase": "change resolution scope", "test": "changed scope ref invalid", "schema": "refRemote/schema_3.json", "data": "refRemote/data_31.json", "valid": "false", "errors": "invalid_type"},
}
- //TODO Pass failed tests : id(s) as scope for references is not implemented yet
- //map[string]string{"phase": "change resolution scope", "test": "changed scope ref valid", "schema": "refRemote/schema_3.json", "data": "refRemote/data_30.json", "valid": "true"},
- //map[string]string{"phase": "change resolution scope", "test": "changed scope ref invalid", "schema": "refRemote/schema_3.json", "data": "refRemote/data_31.json", "valid": "false"}}
-
// Setup a small http server on localhost:1234 for testing purposes
wd, err := os.Getwd()
@@ -416,6 +414,9 @@
expectedValid, _ := strconv.ParseBool(testJson["valid"])
if givenValid != expectedValid {
t.Errorf("Test failed : %s :: %s, expects %t, given %t\n", testJson["phase"], testJson["test"], expectedValid, givenValid)
+ for _, e := range result.Errors() {
+ fmt.Println("Error: " + e.Type())
+ }
}
if !givenValid && testJson["errors"] != "" {
diff --git a/subSchema.go b/subSchema.go
index 9ddbb5f..9961d92 100644
--- a/subSchema.go
+++ b/subSchema.go
@@ -36,7 +36,7 @@
const (
KEY_SCHEMA = "$subSchema"
- KEY_ID = "$id"
+ KEY_ID = "id"
KEY_REF = "$ref"
KEY_TITLE = "title"
KEY_DESCRIPTION = "description"
@@ -73,7 +73,7 @@
type subSchema struct {
// basic subSchema meta properties
- id *string
+ id *gojsonreference.JsonReference
title *string
description *string