blob: 8602672e9e40c5db7ca6039ffc993fb37899e102 [file] [log] [blame]
// 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 Defines Schema, the main entry to every subSchema.
// Contains the parsing logic and error checking.
//
// created 26-02-2013
package gojsonschema
import (
"errors"
"math/big"
"reflect"
"regexp"
"text/template"
"github.com/xeipuuv/gojsonreference"
)
var (
// Locale is the default locale to use
// Library users can overwrite with their own implementation
Locale locale = DefaultLocale{}
// ErrorTemplateFuncs allows you to define custom template funcs for use in localization.
ErrorTemplateFuncs template.FuncMap
)
func NewSchema(l JSONLoader) (*Schema, error) {
ref, err := l.JsonReference()
if err != nil {
return nil, err
}
d := Schema{}
d.pool = newSchemaPool(l.LoaderFactory())
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)
if err != nil {
return nil, err
}
doc = spd.Document
// Deal with fragment pointers
jsonPointer := ref.GetPointer()
doc, _, err = jsonPointer.Get(doc)
if err != nil {
return nil, err
}
} else {
// Load JSON directly
doc, err = l.LoadJSON()
if err != nil {
return nil, err
}
}
d.pool.SetStandaloneDocument(doc)
err = d.parse(doc)
if err != nil {
return nil, err
}
return &d, nil
}
type Schema struct {
documentReference gojsonreference.JsonReference
rootSchema *subSchema
pool *schemaPool
referencePool *schemaReferencePool
}
func (d *Schema) parse(document interface{}) error {
d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY}
return d.parseSchema(document, d.rootSchema)
}
func (d *Schema) SetRootSchemaName(name string) {
d.rootSchema.property = name
}
// Parses a subSchema
//
// Pretty long function ( sorry :) )... but pretty straight forward, repetitive and boring
// Not much magic involved here, most of the job is to validate the key names and their values,
// then the values are copied into subSchema struct
//
func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error {
if !isKind(documentNode, reflect.Map) {
return errors.New(formatErrorDescription(
Locale.ParseError(),
ErrorDetails{
"expected": STRING_SCHEMA,
},
))
}
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{})
// 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) {
if !isKind(m[KEY_SCHEMA], reflect.String) {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_STRING,
"given": KEY_SCHEMA,
},
))
}
schemaRef := m[KEY_SCHEMA].(string)
schemaReference, err := gojsonreference.NewJsonReference(schemaRef)
currentSchema.subSchema = &schemaReference
if err != nil {
return err
}
}
// $ref
if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_STRING,
"given": KEY_REF,
},
))
}
if k, ok := m[KEY_REF].(string); ok {
jsonReference, err := gojsonreference.NewJsonReference(k)
if err != nil {
return err
}
if jsonReference.HasFullUrl {
currentSchema.ref = &jsonReference
} else {
inheritedReference, err := currentSchema.id.Inherits(jsonReference)
if err != nil {
return err
}
currentSchema.ref = inheritedReference
}
if sch, ok := d.referencePool.Get(currentSchema.ref.String()); ok {
currentSchema.refSchema = sch
} else {
err := d.parseReference(documentNode, currentSchema)
if err != nil {
return err
}
return nil
}
}
// definitions
if existsMapKey(m, KEY_DEFINITIONS) {
if isKind(m[KEY_DEFINITIONS], reflect.Map) {
currentSchema.definitions = make(map[string]*subSchema)
for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) {
if isKind(dv, reflect.Map) {
ref, err := gojsonreference.NewJsonReference("#/" + KEY_DEFINITIONS + "/" + dk)
if err != nil {
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(
Locale.InvalidType(),
ErrorDetails{
"expected": STRING_ARRAY_OF_SCHEMAS,
"given": KEY_DEFINITIONS,
},
))
}
}
} else {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": STRING_ARRAY_OF_SCHEMAS,
"given": KEY_DEFINITIONS,
},
))
}
}
// title
if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_STRING,
"given": KEY_TITLE,
},
))
}
if k, ok := m[KEY_TITLE].(string); ok {
currentSchema.title = &k
}
// description
if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_STRING,
"given": KEY_DESCRIPTION,
},
))
}
if k, ok := m[KEY_DESCRIPTION].(string); ok {
currentSchema.description = &k
}
// type
if existsMapKey(m, KEY_TYPE) {
if isKind(m[KEY_TYPE], reflect.String) {
if k, ok := m[KEY_TYPE].(string); ok {
err := currentSchema.types.Add(k)
if err != nil {
return err
}
}
} else {
if isKind(m[KEY_TYPE], reflect.Slice) {
arrayOfTypes := m[KEY_TYPE].([]interface{})
for _, typeInArray := range arrayOfTypes {
if reflect.ValueOf(typeInArray).Kind() != reflect.String {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS,
"given": KEY_TYPE,
},
))
} else {
currentSchema.types.Add(typeInArray.(string))
}
}
} else {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS,
"given": KEY_TYPE,
},
))
}
}
}
// properties
if existsMapKey(m, KEY_PROPERTIES) {
err := d.parseProperties(m[KEY_PROPERTIES], currentSchema)
if err != nil {
return err
}
}
// additionalProperties
if existsMapKey(m, KEY_ADDITIONAL_PROPERTIES) {
if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Bool) {
currentSchema.additionalProperties = m[KEY_ADDITIONAL_PROPERTIES].(bool)
} else if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Map) {
newSchema := &subSchema{property: KEY_ADDITIONAL_PROPERTIES, parent: currentSchema, ref: currentSchema.ref}
currentSchema.additionalProperties = newSchema
err := d.parseSchema(m[KEY_ADDITIONAL_PROPERTIES], newSchema)
if err != nil {
return errors.New(err.Error())
}
} else {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA,
"given": KEY_ADDITIONAL_PROPERTIES,
},
))
}
}
// patternProperties
if existsMapKey(m, KEY_PATTERN_PROPERTIES) {
if isKind(m[KEY_PATTERN_PROPERTIES], reflect.Map) {
patternPropertiesMap := m[KEY_PATTERN_PROPERTIES].(map[string]interface{})
if len(patternPropertiesMap) > 0 {
currentSchema.patternProperties = make(map[string]*subSchema)
for k, v := range patternPropertiesMap {
_, err := regexp.MatchString(k, "")
if err != nil {
return errors.New(formatErrorDescription(
Locale.RegexPattern(),
ErrorDetails{"pattern": k},
))
}
newSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref}
err = d.parseSchema(v, newSchema)
if err != nil {
return errors.New(err.Error())
}
currentSchema.patternProperties[k] = newSchema
}
}
} else {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": STRING_SCHEMA,
"given": KEY_PATTERN_PROPERTIES,
},
))
}
}
// dependencies
if existsMapKey(m, KEY_DEPENDENCIES) {
err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema)
if err != nil {
return err
}
}
// items
if existsMapKey(m, KEY_ITEMS) {
if isKind(m[KEY_ITEMS], reflect.Slice) {
for _, itemElement := range m[KEY_ITEMS].([]interface{}) {
if isKind(itemElement, reflect.Map) {
newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
newSchema.ref = currentSchema.ref
currentSchema.AddItemsChild(newSchema)
err := d.parseSchema(itemElement, newSchema)
if err != nil {
return err
}
} else {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS,
"given": KEY_ITEMS,
},
))
}
currentSchema.itemsChildrenIsSingleSchema = false
}
} else if isKind(m[KEY_ITEMS], reflect.Map) {
newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
newSchema.ref = currentSchema.ref
currentSchema.AddItemsChild(newSchema)
err := d.parseSchema(m[KEY_ITEMS], newSchema)
if err != nil {
return err
}
currentSchema.itemsChildrenIsSingleSchema = true
} else {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS,
"given": KEY_ITEMS,
},
))
}
}
// additionalItems
if existsMapKey(m, KEY_ADDITIONAL_ITEMS) {
if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Bool) {
currentSchema.additionalItems = m[KEY_ADDITIONAL_ITEMS].(bool)
} else if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Map) {
newSchema := &subSchema{property: KEY_ADDITIONAL_ITEMS, parent: currentSchema, ref: currentSchema.ref}
currentSchema.additionalItems = newSchema
err := d.parseSchema(m[KEY_ADDITIONAL_ITEMS], newSchema)
if err != nil {
return errors.New(err.Error())
}
} else {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA,
"given": KEY_ADDITIONAL_ITEMS,
},
))
}
}
// validation : number / integer
if existsMapKey(m, KEY_MULTIPLE_OF) {
multipleOfValue := mustBeNumber(m[KEY_MULTIPLE_OF])
if multipleOfValue == nil {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": STRING_NUMBER,
"given": KEY_MULTIPLE_OF,
},
))
}
if multipleOfValue.Cmp(big.NewFloat(0)) <= 0 {
return errors.New(formatErrorDescription(
Locale.GreaterThanZero(),
ErrorDetails{"number": KEY_MULTIPLE_OF},
))
}
currentSchema.multipleOf = multipleOfValue
}
if existsMapKey(m, KEY_MINIMUM) {
minimumValue := mustBeNumber(m[KEY_MINIMUM])
if minimumValue == nil {
return errors.New(formatErrorDescription(
Locale.MustBeOfA(),
ErrorDetails{"x": KEY_MINIMUM, "y": STRING_NUMBER},
))
}
currentSchema.minimum = minimumValue
}
if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) {
if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
if currentSchema.minimum == nil {
return errors.New(formatErrorDescription(
Locale.CannotBeUsedWithout(),
ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM},
))
}
exclusiveMinimumValue := m[KEY_EXCLUSIVE_MINIMUM].(bool)
currentSchema.exclusiveMinimum = exclusiveMinimumValue
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfA(),
ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": TYPE_BOOLEAN},
))
}
}
if existsMapKey(m, KEY_MAXIMUM) {
maximumValue := mustBeNumber(m[KEY_MAXIMUM])
if maximumValue == nil {
return errors.New(formatErrorDescription(
Locale.MustBeOfA(),
ErrorDetails{"x": KEY_MAXIMUM, "y": STRING_NUMBER},
))
}
currentSchema.maximum = maximumValue
}
if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) {
if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
if currentSchema.maximum == nil {
return errors.New(formatErrorDescription(
Locale.CannotBeUsedWithout(),
ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM},
))
}
exclusiveMaximumValue := m[KEY_EXCLUSIVE_MAXIMUM].(bool)
currentSchema.exclusiveMaximum = exclusiveMaximumValue
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfA(),
ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": STRING_NUMBER},
))
}
}
if currentSchema.minimum != nil && currentSchema.maximum != nil {
if currentSchema.minimum.Cmp(currentSchema.maximum) == 1 {
return errors.New(formatErrorDescription(
Locale.CannotBeGT(),
ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM},
))
}
}
// validation : string
if existsMapKey(m, KEY_MIN_LENGTH) {
minLengthIntegerValue := mustBeInteger(m[KEY_MIN_LENGTH])
if minLengthIntegerValue == nil {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_MIN_LENGTH, "y": TYPE_INTEGER},
))
}
if *minLengthIntegerValue < 0 {
return errors.New(formatErrorDescription(
Locale.MustBeGTEZero(),
ErrorDetails{"key": KEY_MIN_LENGTH},
))
}
currentSchema.minLength = minLengthIntegerValue
}
if existsMapKey(m, KEY_MAX_LENGTH) {
maxLengthIntegerValue := mustBeInteger(m[KEY_MAX_LENGTH])
if maxLengthIntegerValue == nil {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_MAX_LENGTH, "y": TYPE_INTEGER},
))
}
if *maxLengthIntegerValue < 0 {
return errors.New(formatErrorDescription(
Locale.MustBeGTEZero(),
ErrorDetails{"key": KEY_MAX_LENGTH},
))
}
currentSchema.maxLength = maxLengthIntegerValue
}
if currentSchema.minLength != nil && currentSchema.maxLength != nil {
if *currentSchema.minLength > *currentSchema.maxLength {
return errors.New(formatErrorDescription(
Locale.CannotBeGT(),
ErrorDetails{"x": KEY_MIN_LENGTH, "y": KEY_MAX_LENGTH},
))
}
}
if existsMapKey(m, KEY_PATTERN) {
if isKind(m[KEY_PATTERN], reflect.String) {
regexpObject, err := regexp.Compile(m[KEY_PATTERN].(string))
if err != nil {
return errors.New(formatErrorDescription(
Locale.MustBeValidRegex(),
ErrorDetails{"key": KEY_PATTERN},
))
}
currentSchema.pattern = regexpObject
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfA(),
ErrorDetails{"x": KEY_PATTERN, "y": TYPE_STRING},
))
}
}
if existsMapKey(m, KEY_FORMAT) {
formatString, ok := m[KEY_FORMAT].(string)
if ok && FormatCheckers.Has(formatString) {
currentSchema.format = formatString
}
}
// validation : object
if existsMapKey(m, KEY_MIN_PROPERTIES) {
minPropertiesIntegerValue := mustBeInteger(m[KEY_MIN_PROPERTIES])
if minPropertiesIntegerValue == nil {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_MIN_PROPERTIES, "y": TYPE_INTEGER},
))
}
if *minPropertiesIntegerValue < 0 {
return errors.New(formatErrorDescription(
Locale.MustBeGTEZero(),
ErrorDetails{"key": KEY_MIN_PROPERTIES},
))
}
currentSchema.minProperties = minPropertiesIntegerValue
}
if existsMapKey(m, KEY_MAX_PROPERTIES) {
maxPropertiesIntegerValue := mustBeInteger(m[KEY_MAX_PROPERTIES])
if maxPropertiesIntegerValue == nil {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_MAX_PROPERTIES, "y": TYPE_INTEGER},
))
}
if *maxPropertiesIntegerValue < 0 {
return errors.New(formatErrorDescription(
Locale.MustBeGTEZero(),
ErrorDetails{"key": KEY_MAX_PROPERTIES},
))
}
currentSchema.maxProperties = maxPropertiesIntegerValue
}
if currentSchema.minProperties != nil && currentSchema.maxProperties != nil {
if *currentSchema.minProperties > *currentSchema.maxProperties {
return errors.New(formatErrorDescription(
Locale.KeyCannotBeGreaterThan(),
ErrorDetails{"key": KEY_MIN_PROPERTIES, "y": KEY_MAX_PROPERTIES},
))
}
}
if existsMapKey(m, KEY_REQUIRED) {
if isKind(m[KEY_REQUIRED], reflect.Slice) {
requiredValues := m[KEY_REQUIRED].([]interface{})
for _, requiredValue := range requiredValues {
if isKind(requiredValue, reflect.String) {
err := currentSchema.AddRequired(requiredValue.(string))
if err != nil {
return err
}
} else {
return errors.New(formatErrorDescription(
Locale.KeyItemsMustBeOfType(),
ErrorDetails{"key": KEY_REQUIRED, "type": TYPE_STRING},
))
}
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_REQUIRED, "y": TYPE_ARRAY},
))
}
}
// validation : array
if existsMapKey(m, KEY_MIN_ITEMS) {
minItemsIntegerValue := mustBeInteger(m[KEY_MIN_ITEMS])
if minItemsIntegerValue == nil {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_MIN_ITEMS, "y": TYPE_INTEGER},
))
}
if *minItemsIntegerValue < 0 {
return errors.New(formatErrorDescription(
Locale.MustBeGTEZero(),
ErrorDetails{"key": KEY_MIN_ITEMS},
))
}
currentSchema.minItems = minItemsIntegerValue
}
if existsMapKey(m, KEY_MAX_ITEMS) {
maxItemsIntegerValue := mustBeInteger(m[KEY_MAX_ITEMS])
if maxItemsIntegerValue == nil {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_MAX_ITEMS, "y": TYPE_INTEGER},
))
}
if *maxItemsIntegerValue < 0 {
return errors.New(formatErrorDescription(
Locale.MustBeGTEZero(),
ErrorDetails{"key": KEY_MAX_ITEMS},
))
}
currentSchema.maxItems = maxItemsIntegerValue
}
if existsMapKey(m, KEY_UNIQUE_ITEMS) {
if isKind(m[KEY_UNIQUE_ITEMS], reflect.Bool) {
currentSchema.uniqueItems = m[KEY_UNIQUE_ITEMS].(bool)
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfA(),
ErrorDetails{"x": KEY_UNIQUE_ITEMS, "y": TYPE_BOOLEAN},
))
}
}
// validation : all
if existsMapKey(m, KEY_ENUM) {
if isKind(m[KEY_ENUM], reflect.Slice) {
for _, v := range m[KEY_ENUM].([]interface{}) {
err := currentSchema.AddEnum(v)
if err != nil {
return err
}
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_ENUM, "y": TYPE_ARRAY},
))
}
}
// validation : subSchema
if existsMapKey(m, KEY_ONE_OF) {
if isKind(m[KEY_ONE_OF], reflect.Slice) {
for _, v := range m[KEY_ONE_OF].([]interface{}) {
newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref}
currentSchema.AddOneOf(newSchema)
err := d.parseSchema(v, newSchema)
if err != nil {
return err
}
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_ONE_OF, "y": TYPE_ARRAY},
))
}
}
if existsMapKey(m, KEY_ANY_OF) {
if isKind(m[KEY_ANY_OF], reflect.Slice) {
for _, v := range m[KEY_ANY_OF].([]interface{}) {
newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref}
currentSchema.AddAnyOf(newSchema)
err := d.parseSchema(v, newSchema)
if err != nil {
return err
}
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY},
))
}
}
if existsMapKey(m, KEY_ALL_OF) {
if isKind(m[KEY_ALL_OF], reflect.Slice) {
for _, v := range m[KEY_ALL_OF].([]interface{}) {
newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref}
currentSchema.AddAllOf(newSchema)
err := d.parseSchema(v, newSchema)
if err != nil {
return err
}
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY},
))
}
}
if existsMapKey(m, KEY_NOT) {
if isKind(m[KEY_NOT], reflect.Map) {
newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref}
currentSchema.SetNot(newSchema)
err := d.parseSchema(m[KEY_NOT], newSchema)
if err != nil {
return err
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_NOT, "y": TYPE_OBJECT},
))
}
}
if existsMapKey(m, KEY_IF) {
if isKind(m[KEY_IF], reflect.Map) {
newSchema := &subSchema{property: KEY_IF, parent: currentSchema, ref: currentSchema.ref}
currentSchema.SetIf(newSchema)
err := d.parseSchema(m[KEY_IF], newSchema)
if err != nil {
return err
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_IF, "y": TYPE_OBJECT},
))
}
}
if existsMapKey(m, KEY_THEN) {
if isKind(m[KEY_THEN], reflect.Map) {
newSchema := &subSchema{property: KEY_THEN, parent: currentSchema, ref: currentSchema.ref}
currentSchema.SetThen(newSchema)
err := d.parseSchema(m[KEY_THEN], newSchema)
if err != nil {
return err
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_THEN, "y": TYPE_OBJECT},
))
}
}
if existsMapKey(m, KEY_ELSE) {
if isKind(m[KEY_ELSE], reflect.Map) {
newSchema := &subSchema{property: KEY_ELSE, parent: currentSchema, ref: currentSchema.ref}
currentSchema.SetElse(newSchema)
err := d.parseSchema(m[KEY_ELSE], newSchema)
if err != nil {
return err
}
} else {
return errors.New(formatErrorDescription(
Locale.MustBeOfAn(),
ErrorDetails{"x": KEY_ELSE, "y": TYPE_OBJECT},
))
}
}
return nil
}
func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema) error {
var (
refdDocumentNode interface{}
dsp *schemaPoolDocument
err error
)
jsonPointer := currentSchema.ref.GetPointer()
standaloneDocument := d.pool.GetStandaloneDocument()
newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
if currentSchema.ref.HasFragmentOnly {
refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument)
if err != nil {
return err
}
} else {
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
}
}
if !isKind(refdDocumentNode, reflect.Map) {
return errors.New(formatErrorDescription(
Locale.MustBeOfType(),
ErrorDetails{"key": STRING_SCHEMA, "type": TYPE_OBJECT},
))
}
// returns the loaded referenced subSchema for the caller to update its current subSchema
newSchemaDocument := refdDocumentNode.(map[string]interface{})
err = d.parseSchema(newSchemaDocument, newSchema)
if err != nil {
return err
}
currentSchema.refSchema = newSchema
return nil
}
func (d *Schema) parseProperties(documentNode interface{}, currentSchema *subSchema) error {
if !isKind(documentNode, reflect.Map) {
return errors.New(formatErrorDescription(
Locale.MustBeOfType(),
ErrorDetails{"key": STRING_PROPERTIES, "type": TYPE_OBJECT},
))
}
m := documentNode.(map[string]interface{})
for k := range m {
schemaProperty := k
newSchema := &subSchema{property: schemaProperty, parent: currentSchema, ref: currentSchema.ref}
currentSchema.AddPropertiesChild(newSchema)
err := d.parseSchema(m[k], newSchema)
if err != nil {
return err
}
}
return nil
}
func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *subSchema) error {
if !isKind(documentNode, reflect.Map) {
return errors.New(formatErrorDescription(
Locale.MustBeOfType(),
ErrorDetails{"key": KEY_DEPENDENCIES, "type": TYPE_OBJECT},
))
}
m := documentNode.(map[string]interface{})
currentSchema.dependencies = make(map[string]interface{})
for k := range m {
switch reflect.ValueOf(m[k]).Kind() {
case reflect.Slice:
values := m[k].([]interface{})
var valuesToRegister []string
for _, value := range values {
if !isKind(value, reflect.String) {
return errors.New(formatErrorDescription(
Locale.MustBeOfType(),
ErrorDetails{
"key": STRING_DEPENDENCY,
"type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS,
},
))
} else {
valuesToRegister = append(valuesToRegister, value.(string))
}
currentSchema.dependencies[k] = valuesToRegister
}
case reflect.Map:
depSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref}
err := d.parseSchema(m[k], depSchema)
if err != nil {
return err
}
currentSchema.dependencies[k] = depSchema
default:
return errors.New(formatErrorDescription(
Locale.MustBeOfType(),
ErrorDetails{
"key": STRING_DEPENDENCY,
"type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS,
},
))
}
}
return nil
}