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 the structure of a sub-subSchema. 23// A sub-subSchema can contain other sub-schemas. 24// 25// created 27-02-2013 26 27package gojsonschema 28 29import ( 30 "errors" 31 "regexp" 32 "strings" 33 34 "github.com/xeipuuv/gojsonreference" 35) 36 37const ( 38 KEY_SCHEMA = "$subSchema" 39 KEY_ID = "$id" 40 KEY_REF = "$ref" 41 KEY_TITLE = "title" 42 KEY_DESCRIPTION = "description" 43 KEY_TYPE = "type" 44 KEY_ITEMS = "items" 45 KEY_ADDITIONAL_ITEMS = "additionalItems" 46 KEY_PROPERTIES = "properties" 47 KEY_PATTERN_PROPERTIES = "patternProperties" 48 KEY_ADDITIONAL_PROPERTIES = "additionalProperties" 49 KEY_DEFINITIONS = "definitions" 50 KEY_MULTIPLE_OF = "multipleOf" 51 KEY_MINIMUM = "minimum" 52 KEY_MAXIMUM = "maximum" 53 KEY_EXCLUSIVE_MINIMUM = "exclusiveMinimum" 54 KEY_EXCLUSIVE_MAXIMUM = "exclusiveMaximum" 55 KEY_MIN_LENGTH = "minLength" 56 KEY_MAX_LENGTH = "maxLength" 57 KEY_PATTERN = "pattern" 58 KEY_FORMAT = "format" 59 KEY_MIN_PROPERTIES = "minProperties" 60 KEY_MAX_PROPERTIES = "maxProperties" 61 KEY_DEPENDENCIES = "dependencies" 62 KEY_REQUIRED = "required" 63 KEY_MIN_ITEMS = "minItems" 64 KEY_MAX_ITEMS = "maxItems" 65 KEY_UNIQUE_ITEMS = "uniqueItems" 66 KEY_ENUM = "enum" 67 KEY_ONE_OF = "oneOf" 68 KEY_ANY_OF = "anyOf" 69 KEY_ALL_OF = "allOf" 70 KEY_NOT = "not" 71) 72 73type subSchema struct { 74 75 // basic subSchema meta properties 76 id *string 77 title *string 78 description *string 79 80 property string 81 82 // Types associated with the subSchema 83 types jsonSchemaType 84 85 // Reference url 86 ref *gojsonreference.JsonReference 87 // Schema referenced 88 refSchema *subSchema 89 // Json reference 90 subSchema *gojsonreference.JsonReference 91 92 // hierarchy 93 parent *subSchema 94 definitions map[string]*subSchema 95 definitionsChildren []*subSchema 96 itemsChildren []*subSchema 97 itemsChildrenIsSingleSchema bool 98 propertiesChildren []*subSchema 99 100 // validation : number / integer 101 multipleOf *float64 102 maximum *float64 103 exclusiveMaximum bool 104 minimum *float64 105 exclusiveMinimum bool 106 107 // validation : string 108 minLength *int 109 maxLength *int 110 pattern *regexp.Regexp 111 format string 112 113 // validation : object 114 minProperties *int 115 maxProperties *int 116 required []string 117 118 dependencies map[string]interface{} 119 additionalProperties interface{} 120 patternProperties map[string]*subSchema 121 122 // validation : array 123 minItems *int 124 maxItems *int 125 uniqueItems bool 126 127 additionalItems interface{} 128 129 // validation : all 130 enum []string 131 132 // validation : subSchema 133 oneOf []*subSchema 134 anyOf []*subSchema 135 allOf []*subSchema 136 not *subSchema 137} 138 139func (s *subSchema) AddEnum(i interface{}) error { 140 141 is, err := marshalToJsonString(i) 142 if err != nil { 143 return err 144 } 145 146 if isStringInSlice(s.enum, *is) { 147 return errors.New(formatErrorDescription( 148 Locale.KeyItemsMustBeUnique(), 149 ErrorDetails{"key": KEY_ENUM}, 150 )) 151 } 152 153 s.enum = append(s.enum, *is) 154 155 return nil 156} 157 158func (s *subSchema) ContainsEnum(i interface{}) (bool, error) { 159 160 is, err := marshalToJsonString(i) 161 if err != nil { 162 return false, err 163 } 164 165 return isStringInSlice(s.enum, *is), nil 166} 167 168func (s *subSchema) AddOneOf(subSchema *subSchema) { 169 s.oneOf = append(s.oneOf, subSchema) 170} 171 172func (s *subSchema) AddAllOf(subSchema *subSchema) { 173 s.allOf = append(s.allOf, subSchema) 174} 175 176func (s *subSchema) AddAnyOf(subSchema *subSchema) { 177 s.anyOf = append(s.anyOf, subSchema) 178} 179 180func (s *subSchema) SetNot(subSchema *subSchema) { 181 s.not = subSchema 182} 183 184func (s *subSchema) AddRequired(value string) error { 185 186 if isStringInSlice(s.required, value) { 187 return errors.New(formatErrorDescription( 188 Locale.KeyItemsMustBeUnique(), 189 ErrorDetails{"key": KEY_REQUIRED}, 190 )) 191 } 192 193 s.required = append(s.required, value) 194 195 return nil 196} 197 198func (s *subSchema) AddDefinitionChild(child *subSchema) { 199 s.definitionsChildren = append(s.definitionsChildren, child) 200} 201 202func (s *subSchema) AddItemsChild(child *subSchema) { 203 s.itemsChildren = append(s.itemsChildren, child) 204} 205 206func (s *subSchema) AddPropertiesChild(child *subSchema) { 207 s.propertiesChildren = append(s.propertiesChildren, child) 208} 209 210func (s *subSchema) PatternPropertiesString() string { 211 212 if s.patternProperties == nil || len(s.patternProperties) == 0 { 213 return STRING_UNDEFINED // should never happen 214 } 215 216 patternPropertiesKeySlice := []string{} 217 for pk := range s.patternProperties { 218 patternPropertiesKeySlice = append(patternPropertiesKeySlice, `"`+pk+`"`) 219 } 220 221 if len(patternPropertiesKeySlice) == 1 { 222 return patternPropertiesKeySlice[0] 223 } 224 225 return "[" + strings.Join(patternPropertiesKeySlice, ",") + "]" 226 227} 228