...

Source file src/github.com/xeipuuv/gojsonschema/schema.go

Documentation: github.com/xeipuuv/gojsonschema

     1  // Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // author           xeipuuv
    16  // author-github    https://github.com/xeipuuv
    17  // author-mail      xeipuuv@gmail.com
    18  //
    19  // repository-name  gojsonschema
    20  // repository-desc  An implementation of JSON Schema, based on IETF's draft v4 - Go language.
    21  //
    22  // description      Defines Schema, the main entry to every subSchema.
    23  //                  Contains the parsing logic and error checking.
    24  //
    25  // created          26-02-2013
    26  
    27  package gojsonschema
    28  
    29  import (
    30  	"errors"
    31  	"math/big"
    32  	"reflect"
    33  	"regexp"
    34  	"text/template"
    35  
    36  	"github.com/xeipuuv/gojsonreference"
    37  )
    38  
    39  var (
    40  	// Locale is the default locale to use
    41  	// Library users can overwrite with their own implementation
    42  	Locale locale = DefaultLocale{}
    43  
    44  	// ErrorTemplateFuncs allows you to define custom template funcs for use in localization.
    45  	ErrorTemplateFuncs template.FuncMap
    46  )
    47  
    48  // NewSchema instances a schema using the given JSONLoader
    49  func NewSchema(l JSONLoader) (*Schema, error) {
    50  	return NewSchemaLoader().Compile(l)
    51  }
    52  
    53  // Schema holds a schema
    54  type Schema struct {
    55  	documentReference gojsonreference.JsonReference
    56  	rootSchema        *subSchema
    57  	pool              *schemaPool
    58  	referencePool     *schemaReferencePool
    59  }
    60  
    61  func (d *Schema) parse(document interface{}, draft Draft) error {
    62  	d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY, draft: &draft}
    63  	return d.parseSchema(document, d.rootSchema)
    64  }
    65  
    66  // SetRootSchemaName sets the root-schema name
    67  func (d *Schema) SetRootSchemaName(name string) {
    68  	d.rootSchema.property = name
    69  }
    70  
    71  // Parses a subSchema
    72  //
    73  // Pretty long function ( sorry :) )... but pretty straight forward, repetitive and boring
    74  // Not much magic involved here, most of the job is to validate the key names and their values,
    75  // then the values are copied into subSchema struct
    76  //
    77  func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error {
    78  
    79  	if currentSchema.draft == nil {
    80  		if currentSchema.parent == nil {
    81  			return errors.New("Draft not set")
    82  		}
    83  		currentSchema.draft = currentSchema.parent.draft
    84  	}
    85  
    86  	// As of draft 6 "true" is equivalent to an empty schema "{}" and false equals "{"not":{}}"
    87  	if *currentSchema.draft >= Draft6 && isKind(documentNode, reflect.Bool) {
    88  		b := documentNode.(bool)
    89  		currentSchema.pass = &b
    90  		return nil
    91  	}
    92  
    93  	if !isKind(documentNode, reflect.Map) {
    94  		return errors.New(formatErrorDescription(
    95  			Locale.ParseError(),
    96  			ErrorDetails{
    97  				"expected": STRING_SCHEMA,
    98  			},
    99  		))
   100  	}
   101  
   102  	m := documentNode.(map[string]interface{})
   103  
   104  	if currentSchema.parent == nil {
   105  		currentSchema.ref = &d.documentReference
   106  		currentSchema.id = &d.documentReference
   107  	}
   108  
   109  	if currentSchema.id == nil && currentSchema.parent != nil {
   110  		currentSchema.id = currentSchema.parent.id
   111  	}
   112  
   113  	// In draft 6 the id keyword was renamed to $id
   114  	// Hybrid mode uses the old id by default
   115  	var keyID string
   116  
   117  	switch *currentSchema.draft {
   118  	case Draft4:
   119  		keyID = KEY_ID
   120  	case Hybrid:
   121  		keyID = KEY_ID_NEW
   122  		if existsMapKey(m, KEY_ID) {
   123  			keyID = KEY_ID
   124  		}
   125  	default:
   126  		keyID = KEY_ID_NEW
   127  	}
   128  	if existsMapKey(m, keyID) && !isKind(m[keyID], reflect.String) {
   129  		return errors.New(formatErrorDescription(
   130  			Locale.InvalidType(),
   131  			ErrorDetails{
   132  				"expected": TYPE_STRING,
   133  				"given":    keyID,
   134  			},
   135  		))
   136  	}
   137  	if k, ok := m[keyID].(string); ok {
   138  		jsonReference, err := gojsonreference.NewJsonReference(k)
   139  		if err != nil {
   140  			return err
   141  		}
   142  		if currentSchema == d.rootSchema {
   143  			currentSchema.id = &jsonReference
   144  		} else {
   145  			ref, err := currentSchema.parent.id.Inherits(jsonReference)
   146  			if err != nil {
   147  				return err
   148  			}
   149  			currentSchema.id = ref
   150  		}
   151  	}
   152  
   153  	// definitions
   154  	if existsMapKey(m, KEY_DEFINITIONS) {
   155  		if isKind(m[KEY_DEFINITIONS], reflect.Map, reflect.Bool) {
   156  			for _, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) {
   157  				if isKind(dv, reflect.Map, reflect.Bool) {
   158  
   159  					newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema}
   160  
   161  					err := d.parseSchema(dv, newSchema)
   162  
   163  					if err != nil {
   164  						return err
   165  					}
   166  				} else {
   167  					return errors.New(formatErrorDescription(
   168  						Locale.InvalidType(),
   169  						ErrorDetails{
   170  							"expected": STRING_ARRAY_OF_SCHEMAS,
   171  							"given":    KEY_DEFINITIONS,
   172  						},
   173  					))
   174  				}
   175  			}
   176  		} else {
   177  			return errors.New(formatErrorDescription(
   178  				Locale.InvalidType(),
   179  				ErrorDetails{
   180  					"expected": STRING_ARRAY_OF_SCHEMAS,
   181  					"given":    KEY_DEFINITIONS,
   182  				},
   183  			))
   184  		}
   185  
   186  	}
   187  
   188  	// title
   189  	if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) {
   190  		return errors.New(formatErrorDescription(
   191  			Locale.InvalidType(),
   192  			ErrorDetails{
   193  				"expected": TYPE_STRING,
   194  				"given":    KEY_TITLE,
   195  			},
   196  		))
   197  	}
   198  	if k, ok := m[KEY_TITLE].(string); ok {
   199  		currentSchema.title = &k
   200  	}
   201  
   202  	// description
   203  	if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) {
   204  		return errors.New(formatErrorDescription(
   205  			Locale.InvalidType(),
   206  			ErrorDetails{
   207  				"expected": TYPE_STRING,
   208  				"given":    KEY_DESCRIPTION,
   209  			},
   210  		))
   211  	}
   212  	if k, ok := m[KEY_DESCRIPTION].(string); ok {
   213  		currentSchema.description = &k
   214  	}
   215  
   216  	// $ref
   217  	if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) {
   218  		return errors.New(formatErrorDescription(
   219  			Locale.InvalidType(),
   220  			ErrorDetails{
   221  				"expected": TYPE_STRING,
   222  				"given":    KEY_REF,
   223  			},
   224  		))
   225  	}
   226  
   227  	if k, ok := m[KEY_REF].(string); ok {
   228  
   229  		jsonReference, err := gojsonreference.NewJsonReference(k)
   230  		if err != nil {
   231  			return err
   232  		}
   233  
   234  		currentSchema.ref = &jsonReference
   235  
   236  		if sch, ok := d.referencePool.Get(currentSchema.ref.String()); ok {
   237  			currentSchema.refSchema = sch
   238  		} else {
   239  			err := d.parseReference(documentNode, currentSchema)
   240  
   241  			if err != nil {
   242  				return err
   243  			}
   244  
   245  			return nil
   246  		}
   247  	}
   248  
   249  	// type
   250  	if existsMapKey(m, KEY_TYPE) {
   251  		if isKind(m[KEY_TYPE], reflect.String) {
   252  			if k, ok := m[KEY_TYPE].(string); ok {
   253  				err := currentSchema.types.Add(k)
   254  				if err != nil {
   255  					return err
   256  				}
   257  			}
   258  		} else {
   259  			if isKind(m[KEY_TYPE], reflect.Slice) {
   260  				arrayOfTypes := m[KEY_TYPE].([]interface{})
   261  				for _, typeInArray := range arrayOfTypes {
   262  					if reflect.ValueOf(typeInArray).Kind() != reflect.String {
   263  						return errors.New(formatErrorDescription(
   264  							Locale.InvalidType(),
   265  							ErrorDetails{
   266  								"expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS,
   267  								"given":    KEY_TYPE,
   268  							},
   269  						))
   270  					}
   271  					if err := currentSchema.types.Add(typeInArray.(string)); err != nil {
   272  						return err
   273  					}
   274  				}
   275  
   276  			} else {
   277  				return errors.New(formatErrorDescription(
   278  					Locale.InvalidType(),
   279  					ErrorDetails{
   280  						"expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS,
   281  						"given":    KEY_TYPE,
   282  					},
   283  				))
   284  			}
   285  		}
   286  	}
   287  
   288  	// properties
   289  	if existsMapKey(m, KEY_PROPERTIES) {
   290  		err := d.parseProperties(m[KEY_PROPERTIES], currentSchema)
   291  		if err != nil {
   292  			return err
   293  		}
   294  	}
   295  
   296  	// additionalProperties
   297  	if existsMapKey(m, KEY_ADDITIONAL_PROPERTIES) {
   298  		if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Bool) {
   299  			currentSchema.additionalProperties = m[KEY_ADDITIONAL_PROPERTIES].(bool)
   300  		} else if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Map) {
   301  			newSchema := &subSchema{property: KEY_ADDITIONAL_PROPERTIES, parent: currentSchema, ref: currentSchema.ref}
   302  			currentSchema.additionalProperties = newSchema
   303  			err := d.parseSchema(m[KEY_ADDITIONAL_PROPERTIES], newSchema)
   304  			if err != nil {
   305  				return errors.New(err.Error())
   306  			}
   307  		} else {
   308  			return errors.New(formatErrorDescription(
   309  				Locale.InvalidType(),
   310  				ErrorDetails{
   311  					"expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA,
   312  					"given":    KEY_ADDITIONAL_PROPERTIES,
   313  				},
   314  			))
   315  		}
   316  	}
   317  
   318  	// patternProperties
   319  	if existsMapKey(m, KEY_PATTERN_PROPERTIES) {
   320  		if isKind(m[KEY_PATTERN_PROPERTIES], reflect.Map) {
   321  			patternPropertiesMap := m[KEY_PATTERN_PROPERTIES].(map[string]interface{})
   322  			if len(patternPropertiesMap) > 0 {
   323  				currentSchema.patternProperties = make(map[string]*subSchema)
   324  				for k, v := range patternPropertiesMap {
   325  					_, err := regexp.MatchString(k, "")
   326  					if err != nil {
   327  						return errors.New(formatErrorDescription(
   328  							Locale.RegexPattern(),
   329  							ErrorDetails{"pattern": k},
   330  						))
   331  					}
   332  					newSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref}
   333  					err = d.parseSchema(v, newSchema)
   334  					if err != nil {
   335  						return errors.New(err.Error())
   336  					}
   337  					currentSchema.patternProperties[k] = newSchema
   338  				}
   339  			}
   340  		} else {
   341  			return errors.New(formatErrorDescription(
   342  				Locale.InvalidType(),
   343  				ErrorDetails{
   344  					"expected": STRING_SCHEMA,
   345  					"given":    KEY_PATTERN_PROPERTIES,
   346  				},
   347  			))
   348  		}
   349  	}
   350  
   351  	// propertyNames
   352  	if existsMapKey(m, KEY_PROPERTY_NAMES) && *currentSchema.draft >= Draft6 {
   353  		if isKind(m[KEY_PROPERTY_NAMES], reflect.Map, reflect.Bool) {
   354  			newSchema := &subSchema{property: KEY_PROPERTY_NAMES, parent: currentSchema, ref: currentSchema.ref}
   355  			currentSchema.propertyNames = newSchema
   356  			err := d.parseSchema(m[KEY_PROPERTY_NAMES], newSchema)
   357  			if err != nil {
   358  				return err
   359  			}
   360  		} else {
   361  			return errors.New(formatErrorDescription(
   362  				Locale.InvalidType(),
   363  				ErrorDetails{
   364  					"expected": STRING_SCHEMA,
   365  					"given":    KEY_PATTERN_PROPERTIES,
   366  				},
   367  			))
   368  		}
   369  	}
   370  
   371  	// dependencies
   372  	if existsMapKey(m, KEY_DEPENDENCIES) {
   373  		err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema)
   374  		if err != nil {
   375  			return err
   376  		}
   377  	}
   378  
   379  	// items
   380  	if existsMapKey(m, KEY_ITEMS) {
   381  		if isKind(m[KEY_ITEMS], reflect.Slice) {
   382  			for _, itemElement := range m[KEY_ITEMS].([]interface{}) {
   383  				if isKind(itemElement, reflect.Map, reflect.Bool) {
   384  					newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
   385  					newSchema.ref = currentSchema.ref
   386  					currentSchema.itemsChildren = append(currentSchema.itemsChildren, newSchema)
   387  					err := d.parseSchema(itemElement, newSchema)
   388  					if err != nil {
   389  						return err
   390  					}
   391  				} else {
   392  					return errors.New(formatErrorDescription(
   393  						Locale.InvalidType(),
   394  						ErrorDetails{
   395  							"expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS,
   396  							"given":    KEY_ITEMS,
   397  						},
   398  					))
   399  				}
   400  				currentSchema.itemsChildrenIsSingleSchema = false
   401  			}
   402  		} else if isKind(m[KEY_ITEMS], reflect.Map, reflect.Bool) {
   403  			newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
   404  			newSchema.ref = currentSchema.ref
   405  			currentSchema.itemsChildren = append(currentSchema.itemsChildren, newSchema)
   406  			err := d.parseSchema(m[KEY_ITEMS], newSchema)
   407  			if err != nil {
   408  				return err
   409  			}
   410  			currentSchema.itemsChildrenIsSingleSchema = true
   411  		} else {
   412  			return errors.New(formatErrorDescription(
   413  				Locale.InvalidType(),
   414  				ErrorDetails{
   415  					"expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS,
   416  					"given":    KEY_ITEMS,
   417  				},
   418  			))
   419  		}
   420  	}
   421  
   422  	// additionalItems
   423  	if existsMapKey(m, KEY_ADDITIONAL_ITEMS) {
   424  		if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Bool) {
   425  			currentSchema.additionalItems = m[KEY_ADDITIONAL_ITEMS].(bool)
   426  		} else if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Map) {
   427  			newSchema := &subSchema{property: KEY_ADDITIONAL_ITEMS, parent: currentSchema, ref: currentSchema.ref}
   428  			currentSchema.additionalItems = newSchema
   429  			err := d.parseSchema(m[KEY_ADDITIONAL_ITEMS], newSchema)
   430  			if err != nil {
   431  				return errors.New(err.Error())
   432  			}
   433  		} else {
   434  			return errors.New(formatErrorDescription(
   435  				Locale.InvalidType(),
   436  				ErrorDetails{
   437  					"expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA,
   438  					"given":    KEY_ADDITIONAL_ITEMS,
   439  				},
   440  			))
   441  		}
   442  	}
   443  
   444  	// validation : number / integer
   445  
   446  	if existsMapKey(m, KEY_MULTIPLE_OF) {
   447  		multipleOfValue := mustBeNumber(m[KEY_MULTIPLE_OF])
   448  		if multipleOfValue == nil {
   449  			return errors.New(formatErrorDescription(
   450  				Locale.InvalidType(),
   451  				ErrorDetails{
   452  					"expected": STRING_NUMBER,
   453  					"given":    KEY_MULTIPLE_OF,
   454  				},
   455  			))
   456  		}
   457  		if multipleOfValue.Cmp(big.NewRat(0, 1)) <= 0 {
   458  			return errors.New(formatErrorDescription(
   459  				Locale.GreaterThanZero(),
   460  				ErrorDetails{"number": KEY_MULTIPLE_OF},
   461  			))
   462  		}
   463  		currentSchema.multipleOf = multipleOfValue
   464  	}
   465  
   466  	if existsMapKey(m, KEY_MINIMUM) {
   467  		minimumValue := mustBeNumber(m[KEY_MINIMUM])
   468  		if minimumValue == nil {
   469  			return errors.New(formatErrorDescription(
   470  				Locale.MustBeOfA(),
   471  				ErrorDetails{"x": KEY_MINIMUM, "y": STRING_NUMBER},
   472  			))
   473  		}
   474  		currentSchema.minimum = minimumValue
   475  	}
   476  
   477  	if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) {
   478  		switch *currentSchema.draft {
   479  		case Draft4:
   480  			if !isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
   481  				return errors.New(formatErrorDescription(
   482  					Locale.InvalidType(),
   483  					ErrorDetails{
   484  						"expected": TYPE_BOOLEAN,
   485  						"given":    KEY_EXCLUSIVE_MINIMUM,
   486  					},
   487  				))
   488  			}
   489  			if currentSchema.minimum == nil {
   490  				return errors.New(formatErrorDescription(
   491  					Locale.CannotBeUsedWithout(),
   492  					ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM},
   493  				))
   494  			}
   495  			if m[KEY_EXCLUSIVE_MINIMUM].(bool) {
   496  				currentSchema.exclusiveMinimum = currentSchema.minimum
   497  				currentSchema.minimum = nil
   498  			}
   499  		case Hybrid:
   500  			if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
   501  				if currentSchema.minimum == nil {
   502  					return errors.New(formatErrorDescription(
   503  						Locale.CannotBeUsedWithout(),
   504  						ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM},
   505  					))
   506  				}
   507  				if m[KEY_EXCLUSIVE_MINIMUM].(bool) {
   508  					currentSchema.exclusiveMinimum = currentSchema.minimum
   509  					currentSchema.minimum = nil
   510  				}
   511  			} else if isJSONNumber(m[KEY_EXCLUSIVE_MINIMUM]) {
   512  				currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM])
   513  			} else {
   514  				return errors.New(formatErrorDescription(
   515  					Locale.InvalidType(),
   516  					ErrorDetails{
   517  						"expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER,
   518  						"given":    KEY_EXCLUSIVE_MINIMUM,
   519  					},
   520  				))
   521  			}
   522  		default:
   523  			if isJSONNumber(m[KEY_EXCLUSIVE_MINIMUM]) {
   524  				currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM])
   525  			} else {
   526  				return errors.New(formatErrorDescription(
   527  					Locale.InvalidType(),
   528  					ErrorDetails{
   529  						"expected": TYPE_NUMBER,
   530  						"given":    KEY_EXCLUSIVE_MINIMUM,
   531  					},
   532  				))
   533  			}
   534  		}
   535  	}
   536  
   537  	if existsMapKey(m, KEY_MAXIMUM) {
   538  		maximumValue := mustBeNumber(m[KEY_MAXIMUM])
   539  		if maximumValue == nil {
   540  			return errors.New(formatErrorDescription(
   541  				Locale.MustBeOfA(),
   542  				ErrorDetails{"x": KEY_MAXIMUM, "y": STRING_NUMBER},
   543  			))
   544  		}
   545  		currentSchema.maximum = maximumValue
   546  	}
   547  
   548  	if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) {
   549  		switch *currentSchema.draft {
   550  		case Draft4:
   551  			if !isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
   552  				return errors.New(formatErrorDescription(
   553  					Locale.InvalidType(),
   554  					ErrorDetails{
   555  						"expected": TYPE_BOOLEAN,
   556  						"given":    KEY_EXCLUSIVE_MAXIMUM,
   557  					},
   558  				))
   559  			}
   560  			if currentSchema.maximum == nil {
   561  				return errors.New(formatErrorDescription(
   562  					Locale.CannotBeUsedWithout(),
   563  					ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM},
   564  				))
   565  			}
   566  			if m[KEY_EXCLUSIVE_MAXIMUM].(bool) {
   567  				currentSchema.exclusiveMaximum = currentSchema.maximum
   568  				currentSchema.maximum = nil
   569  			}
   570  		case Hybrid:
   571  			if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
   572  				if currentSchema.maximum == nil {
   573  					return errors.New(formatErrorDescription(
   574  						Locale.CannotBeUsedWithout(),
   575  						ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM},
   576  					))
   577  				}
   578  				if m[KEY_EXCLUSIVE_MAXIMUM].(bool) {
   579  					currentSchema.exclusiveMaximum = currentSchema.maximum
   580  					currentSchema.maximum = nil
   581  				}
   582  			} else if isJSONNumber(m[KEY_EXCLUSIVE_MAXIMUM]) {
   583  				currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM])
   584  			} else {
   585  				return errors.New(formatErrorDescription(
   586  					Locale.InvalidType(),
   587  					ErrorDetails{
   588  						"expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER,
   589  						"given":    KEY_EXCLUSIVE_MAXIMUM,
   590  					},
   591  				))
   592  			}
   593  		default:
   594  			if isJSONNumber(m[KEY_EXCLUSIVE_MAXIMUM]) {
   595  				currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM])
   596  			} else {
   597  				return errors.New(formatErrorDescription(
   598  					Locale.InvalidType(),
   599  					ErrorDetails{
   600  						"expected": TYPE_NUMBER,
   601  						"given":    KEY_EXCLUSIVE_MAXIMUM,
   602  					},
   603  				))
   604  			}
   605  		}
   606  	}
   607  
   608  	// validation : string
   609  
   610  	if existsMapKey(m, KEY_MIN_LENGTH) {
   611  		minLengthIntegerValue := mustBeInteger(m[KEY_MIN_LENGTH])
   612  		if minLengthIntegerValue == nil {
   613  			return errors.New(formatErrorDescription(
   614  				Locale.MustBeOfAn(),
   615  				ErrorDetails{"x": KEY_MIN_LENGTH, "y": TYPE_INTEGER},
   616  			))
   617  		}
   618  		if *minLengthIntegerValue < 0 {
   619  			return errors.New(formatErrorDescription(
   620  				Locale.MustBeGTEZero(),
   621  				ErrorDetails{"key": KEY_MIN_LENGTH},
   622  			))
   623  		}
   624  		currentSchema.minLength = minLengthIntegerValue
   625  	}
   626  
   627  	if existsMapKey(m, KEY_MAX_LENGTH) {
   628  		maxLengthIntegerValue := mustBeInteger(m[KEY_MAX_LENGTH])
   629  		if maxLengthIntegerValue == nil {
   630  			return errors.New(formatErrorDescription(
   631  				Locale.MustBeOfAn(),
   632  				ErrorDetails{"x": KEY_MAX_LENGTH, "y": TYPE_INTEGER},
   633  			))
   634  		}
   635  		if *maxLengthIntegerValue < 0 {
   636  			return errors.New(formatErrorDescription(
   637  				Locale.MustBeGTEZero(),
   638  				ErrorDetails{"key": KEY_MAX_LENGTH},
   639  			))
   640  		}
   641  		currentSchema.maxLength = maxLengthIntegerValue
   642  	}
   643  
   644  	if currentSchema.minLength != nil && currentSchema.maxLength != nil {
   645  		if *currentSchema.minLength > *currentSchema.maxLength {
   646  			return errors.New(formatErrorDescription(
   647  				Locale.CannotBeGT(),
   648  				ErrorDetails{"x": KEY_MIN_LENGTH, "y": KEY_MAX_LENGTH},
   649  			))
   650  		}
   651  	}
   652  
   653  	if existsMapKey(m, KEY_PATTERN) {
   654  		if isKind(m[KEY_PATTERN], reflect.String) {
   655  			regexpObject, err := regexp.Compile(m[KEY_PATTERN].(string))
   656  			if err != nil {
   657  				return errors.New(formatErrorDescription(
   658  					Locale.MustBeValidRegex(),
   659  					ErrorDetails{"key": KEY_PATTERN},
   660  				))
   661  			}
   662  			currentSchema.pattern = regexpObject
   663  		} else {
   664  			return errors.New(formatErrorDescription(
   665  				Locale.MustBeOfA(),
   666  				ErrorDetails{"x": KEY_PATTERN, "y": TYPE_STRING},
   667  			))
   668  		}
   669  	}
   670  
   671  	if existsMapKey(m, KEY_FORMAT) {
   672  		formatString, ok := m[KEY_FORMAT].(string)
   673  		if !ok {
   674  			return errors.New(formatErrorDescription(
   675  				Locale.MustBeOfType(),
   676  				ErrorDetails{"key": KEY_FORMAT, "type": TYPE_STRING},
   677  			))
   678  		}
   679  		currentSchema.format = formatString
   680  	}
   681  
   682  	// validation : object
   683  
   684  	if existsMapKey(m, KEY_MIN_PROPERTIES) {
   685  		minPropertiesIntegerValue := mustBeInteger(m[KEY_MIN_PROPERTIES])
   686  		if minPropertiesIntegerValue == nil {
   687  			return errors.New(formatErrorDescription(
   688  				Locale.MustBeOfAn(),
   689  				ErrorDetails{"x": KEY_MIN_PROPERTIES, "y": TYPE_INTEGER},
   690  			))
   691  		}
   692  		if *minPropertiesIntegerValue < 0 {
   693  			return errors.New(formatErrorDescription(
   694  				Locale.MustBeGTEZero(),
   695  				ErrorDetails{"key": KEY_MIN_PROPERTIES},
   696  			))
   697  		}
   698  		currentSchema.minProperties = minPropertiesIntegerValue
   699  	}
   700  
   701  	if existsMapKey(m, KEY_MAX_PROPERTIES) {
   702  		maxPropertiesIntegerValue := mustBeInteger(m[KEY_MAX_PROPERTIES])
   703  		if maxPropertiesIntegerValue == nil {
   704  			return errors.New(formatErrorDescription(
   705  				Locale.MustBeOfAn(),
   706  				ErrorDetails{"x": KEY_MAX_PROPERTIES, "y": TYPE_INTEGER},
   707  			))
   708  		}
   709  		if *maxPropertiesIntegerValue < 0 {
   710  			return errors.New(formatErrorDescription(
   711  				Locale.MustBeGTEZero(),
   712  				ErrorDetails{"key": KEY_MAX_PROPERTIES},
   713  			))
   714  		}
   715  		currentSchema.maxProperties = maxPropertiesIntegerValue
   716  	}
   717  
   718  	if currentSchema.minProperties != nil && currentSchema.maxProperties != nil {
   719  		if *currentSchema.minProperties > *currentSchema.maxProperties {
   720  			return errors.New(formatErrorDescription(
   721  				Locale.KeyCannotBeGreaterThan(),
   722  				ErrorDetails{"key": KEY_MIN_PROPERTIES, "y": KEY_MAX_PROPERTIES},
   723  			))
   724  		}
   725  	}
   726  
   727  	if existsMapKey(m, KEY_REQUIRED) {
   728  		if isKind(m[KEY_REQUIRED], reflect.Slice) {
   729  			requiredValues := m[KEY_REQUIRED].([]interface{})
   730  			for _, requiredValue := range requiredValues {
   731  				if isKind(requiredValue, reflect.String) {
   732  					if isStringInSlice(currentSchema.required, requiredValue.(string)) {
   733  						return errors.New(formatErrorDescription(
   734  							Locale.KeyItemsMustBeUnique(),
   735  							ErrorDetails{"key": KEY_REQUIRED},
   736  						))
   737  					}
   738  					currentSchema.required = append(currentSchema.required, requiredValue.(string))
   739  				} else {
   740  					return errors.New(formatErrorDescription(
   741  						Locale.KeyItemsMustBeOfType(),
   742  						ErrorDetails{"key": KEY_REQUIRED, "type": TYPE_STRING},
   743  					))
   744  				}
   745  			}
   746  		} else {
   747  			return errors.New(formatErrorDescription(
   748  				Locale.MustBeOfAn(),
   749  				ErrorDetails{"x": KEY_REQUIRED, "y": TYPE_ARRAY},
   750  			))
   751  		}
   752  	}
   753  
   754  	// validation : array
   755  
   756  	if existsMapKey(m, KEY_MIN_ITEMS) {
   757  		minItemsIntegerValue := mustBeInteger(m[KEY_MIN_ITEMS])
   758  		if minItemsIntegerValue == nil {
   759  			return errors.New(formatErrorDescription(
   760  				Locale.MustBeOfAn(),
   761  				ErrorDetails{"x": KEY_MIN_ITEMS, "y": TYPE_INTEGER},
   762  			))
   763  		}
   764  		if *minItemsIntegerValue < 0 {
   765  			return errors.New(formatErrorDescription(
   766  				Locale.MustBeGTEZero(),
   767  				ErrorDetails{"key": KEY_MIN_ITEMS},
   768  			))
   769  		}
   770  		currentSchema.minItems = minItemsIntegerValue
   771  	}
   772  
   773  	if existsMapKey(m, KEY_MAX_ITEMS) {
   774  		maxItemsIntegerValue := mustBeInteger(m[KEY_MAX_ITEMS])
   775  		if maxItemsIntegerValue == nil {
   776  			return errors.New(formatErrorDescription(
   777  				Locale.MustBeOfAn(),
   778  				ErrorDetails{"x": KEY_MAX_ITEMS, "y": TYPE_INTEGER},
   779  			))
   780  		}
   781  		if *maxItemsIntegerValue < 0 {
   782  			return errors.New(formatErrorDescription(
   783  				Locale.MustBeGTEZero(),
   784  				ErrorDetails{"key": KEY_MAX_ITEMS},
   785  			))
   786  		}
   787  		currentSchema.maxItems = maxItemsIntegerValue
   788  	}
   789  
   790  	if existsMapKey(m, KEY_UNIQUE_ITEMS) {
   791  		if isKind(m[KEY_UNIQUE_ITEMS], reflect.Bool) {
   792  			currentSchema.uniqueItems = m[KEY_UNIQUE_ITEMS].(bool)
   793  		} else {
   794  			return errors.New(formatErrorDescription(
   795  				Locale.MustBeOfA(),
   796  				ErrorDetails{"x": KEY_UNIQUE_ITEMS, "y": TYPE_BOOLEAN},
   797  			))
   798  		}
   799  	}
   800  
   801  	if existsMapKey(m, KEY_CONTAINS) && *currentSchema.draft >= Draft6 {
   802  		newSchema := &subSchema{property: KEY_CONTAINS, parent: currentSchema, ref: currentSchema.ref}
   803  		currentSchema.contains = newSchema
   804  		err := d.parseSchema(m[KEY_CONTAINS], newSchema)
   805  		if err != nil {
   806  			return err
   807  		}
   808  	}
   809  
   810  	// validation : all
   811  
   812  	if existsMapKey(m, KEY_CONST) && *currentSchema.draft >= Draft6 {
   813  		is, err := marshalWithoutNumber(m[KEY_CONST])
   814  		if err != nil {
   815  			return err
   816  		}
   817  		currentSchema._const = is
   818  	}
   819  
   820  	if existsMapKey(m, KEY_ENUM) {
   821  		if isKind(m[KEY_ENUM], reflect.Slice) {
   822  			for _, v := range m[KEY_ENUM].([]interface{}) {
   823  				is, err := marshalWithoutNumber(v)
   824  				if err != nil {
   825  					return err
   826  				}
   827  				if isStringInSlice(currentSchema.enum, *is) {
   828  					return errors.New(formatErrorDescription(
   829  						Locale.KeyItemsMustBeUnique(),
   830  						ErrorDetails{"key": KEY_ENUM},
   831  					))
   832  				}
   833  				currentSchema.enum = append(currentSchema.enum, *is)
   834  			}
   835  		} else {
   836  			return errors.New(formatErrorDescription(
   837  				Locale.MustBeOfAn(),
   838  				ErrorDetails{"x": KEY_ENUM, "y": TYPE_ARRAY},
   839  			))
   840  		}
   841  	}
   842  
   843  	// validation : subSchema
   844  
   845  	if existsMapKey(m, KEY_ONE_OF) {
   846  		if isKind(m[KEY_ONE_OF], reflect.Slice) {
   847  			for _, v := range m[KEY_ONE_OF].([]interface{}) {
   848  				newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref}
   849  				currentSchema.oneOf = append(currentSchema.oneOf, newSchema)
   850  				err := d.parseSchema(v, newSchema)
   851  				if err != nil {
   852  					return err
   853  				}
   854  			}
   855  		} else {
   856  			return errors.New(formatErrorDescription(
   857  				Locale.MustBeOfAn(),
   858  				ErrorDetails{"x": KEY_ONE_OF, "y": TYPE_ARRAY},
   859  			))
   860  		}
   861  	}
   862  
   863  	if existsMapKey(m, KEY_ANY_OF) {
   864  		if isKind(m[KEY_ANY_OF], reflect.Slice) {
   865  			for _, v := range m[KEY_ANY_OF].([]interface{}) {
   866  				newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref}
   867  				currentSchema.anyOf = append(currentSchema.anyOf, newSchema)
   868  				err := d.parseSchema(v, newSchema)
   869  				if err != nil {
   870  					return err
   871  				}
   872  			}
   873  		} else {
   874  			return errors.New(formatErrorDescription(
   875  				Locale.MustBeOfAn(),
   876  				ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY},
   877  			))
   878  		}
   879  	}
   880  
   881  	if existsMapKey(m, KEY_ALL_OF) {
   882  		if isKind(m[KEY_ALL_OF], reflect.Slice) {
   883  			for _, v := range m[KEY_ALL_OF].([]interface{}) {
   884  				newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref}
   885  				currentSchema.allOf = append(currentSchema.allOf, newSchema)
   886  				err := d.parseSchema(v, newSchema)
   887  				if err != nil {
   888  					return err
   889  				}
   890  			}
   891  		} else {
   892  			return errors.New(formatErrorDescription(
   893  				Locale.MustBeOfAn(),
   894  				ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY},
   895  			))
   896  		}
   897  	}
   898  
   899  	if existsMapKey(m, KEY_NOT) {
   900  		if isKind(m[KEY_NOT], reflect.Map, reflect.Bool) {
   901  			newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref}
   902  			currentSchema.not = newSchema
   903  			err := d.parseSchema(m[KEY_NOT], newSchema)
   904  			if err != nil {
   905  				return err
   906  			}
   907  		} else {
   908  			return errors.New(formatErrorDescription(
   909  				Locale.MustBeOfAn(),
   910  				ErrorDetails{"x": KEY_NOT, "y": TYPE_OBJECT},
   911  			))
   912  		}
   913  	}
   914  
   915  	if *currentSchema.draft >= Draft7 {
   916  		if existsMapKey(m, KEY_IF) {
   917  			if isKind(m[KEY_IF], reflect.Map, reflect.Bool) {
   918  				newSchema := &subSchema{property: KEY_IF, parent: currentSchema, ref: currentSchema.ref}
   919  				currentSchema._if = newSchema
   920  				err := d.parseSchema(m[KEY_IF], newSchema)
   921  				if err != nil {
   922  					return err
   923  				}
   924  			} else {
   925  				return errors.New(formatErrorDescription(
   926  					Locale.MustBeOfAn(),
   927  					ErrorDetails{"x": KEY_IF, "y": TYPE_OBJECT},
   928  				))
   929  			}
   930  		}
   931  
   932  		if existsMapKey(m, KEY_THEN) {
   933  			if isKind(m[KEY_THEN], reflect.Map, reflect.Bool) {
   934  				newSchema := &subSchema{property: KEY_THEN, parent: currentSchema, ref: currentSchema.ref}
   935  				currentSchema._then = newSchema
   936  				err := d.parseSchema(m[KEY_THEN], newSchema)
   937  				if err != nil {
   938  					return err
   939  				}
   940  			} else {
   941  				return errors.New(formatErrorDescription(
   942  					Locale.MustBeOfAn(),
   943  					ErrorDetails{"x": KEY_THEN, "y": TYPE_OBJECT},
   944  				))
   945  			}
   946  		}
   947  
   948  		if existsMapKey(m, KEY_ELSE) {
   949  			if isKind(m[KEY_ELSE], reflect.Map, reflect.Bool) {
   950  				newSchema := &subSchema{property: KEY_ELSE, parent: currentSchema, ref: currentSchema.ref}
   951  				currentSchema._else = newSchema
   952  				err := d.parseSchema(m[KEY_ELSE], newSchema)
   953  				if err != nil {
   954  					return err
   955  				}
   956  			} else {
   957  				return errors.New(formatErrorDescription(
   958  					Locale.MustBeOfAn(),
   959  					ErrorDetails{"x": KEY_ELSE, "y": TYPE_OBJECT},
   960  				))
   961  			}
   962  		}
   963  	}
   964  
   965  	return nil
   966  }
   967  
   968  func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema) error {
   969  	var (
   970  		refdDocumentNode interface{}
   971  		dsp              *schemaPoolDocument
   972  		err              error
   973  	)
   974  
   975  	newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
   976  
   977  	d.referencePool.Add(currentSchema.ref.String(), newSchema)
   978  
   979  	dsp, err = d.pool.GetDocument(*currentSchema.ref)
   980  	if err != nil {
   981  		return err
   982  	}
   983  	newSchema.id = currentSchema.ref
   984  
   985  	refdDocumentNode = dsp.Document
   986  	newSchema.draft = dsp.Draft
   987  
   988  	if err != nil {
   989  		return err
   990  	}
   991  
   992  	if !isKind(refdDocumentNode, reflect.Map, reflect.Bool) {
   993  		return errors.New(formatErrorDescription(
   994  			Locale.MustBeOfType(),
   995  			ErrorDetails{"key": STRING_SCHEMA, "type": TYPE_OBJECT},
   996  		))
   997  	}
   998  
   999  	err = d.parseSchema(refdDocumentNode, newSchema)
  1000  	if err != nil {
  1001  		return err
  1002  	}
  1003  
  1004  	currentSchema.refSchema = newSchema
  1005  
  1006  	return nil
  1007  
  1008  }
  1009  
  1010  func (d *Schema) parseProperties(documentNode interface{}, currentSchema *subSchema) error {
  1011  
  1012  	if !isKind(documentNode, reflect.Map) {
  1013  		return errors.New(formatErrorDescription(
  1014  			Locale.MustBeOfType(),
  1015  			ErrorDetails{"key": STRING_PROPERTIES, "type": TYPE_OBJECT},
  1016  		))
  1017  	}
  1018  
  1019  	m := documentNode.(map[string]interface{})
  1020  	for k := range m {
  1021  		schemaProperty := k
  1022  		newSchema := &subSchema{property: schemaProperty, parent: currentSchema, ref: currentSchema.ref}
  1023  		currentSchema.propertiesChildren = append(currentSchema.propertiesChildren, newSchema)
  1024  		err := d.parseSchema(m[k], newSchema)
  1025  		if err != nil {
  1026  			return err
  1027  		}
  1028  	}
  1029  
  1030  	return nil
  1031  }
  1032  
  1033  func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *subSchema) error {
  1034  
  1035  	if !isKind(documentNode, reflect.Map) {
  1036  		return errors.New(formatErrorDescription(
  1037  			Locale.MustBeOfType(),
  1038  			ErrorDetails{"key": KEY_DEPENDENCIES, "type": TYPE_OBJECT},
  1039  		))
  1040  	}
  1041  
  1042  	m := documentNode.(map[string]interface{})
  1043  	currentSchema.dependencies = make(map[string]interface{})
  1044  
  1045  	for k := range m {
  1046  		switch reflect.ValueOf(m[k]).Kind() {
  1047  
  1048  		case reflect.Slice:
  1049  			values := m[k].([]interface{})
  1050  			var valuesToRegister []string
  1051  
  1052  			for _, value := range values {
  1053  				if !isKind(value, reflect.String) {
  1054  					return errors.New(formatErrorDescription(
  1055  						Locale.MustBeOfType(),
  1056  						ErrorDetails{
  1057  							"key":  STRING_DEPENDENCY,
  1058  							"type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS,
  1059  						},
  1060  					))
  1061  				}
  1062  				valuesToRegister = append(valuesToRegister, value.(string))
  1063  				currentSchema.dependencies[k] = valuesToRegister
  1064  			}
  1065  
  1066  		case reflect.Map, reflect.Bool:
  1067  			depSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref}
  1068  			err := d.parseSchema(m[k], depSchema)
  1069  			if err != nil {
  1070  				return err
  1071  			}
  1072  			currentSchema.dependencies[k] = depSchema
  1073  
  1074  		default:
  1075  			return errors.New(formatErrorDescription(
  1076  				Locale.MustBeOfType(),
  1077  				ErrorDetails{
  1078  					"key":  STRING_DEPENDENCY,
  1079  					"type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS,
  1080  				},
  1081  			))
  1082  		}
  1083  
  1084  	}
  1085  
  1086  	return nil
  1087  }
  1088  

View as plain text