| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- // Copyright 2017 Google Inc. All Rights Reserved.
- //
- // 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.
- package jsonschema
- import (
- "fmt"
- "io/ioutil"
- "gopkg.in/yaml.v2"
- )
- // This is a global map of all known Schemas.
- // It is initialized when the first Schema is created and inserted.
- var schemas map[string]*Schema
- // NewSchemaFromFile reads a schema from a file.
- // Currently this assumes that schemas are stored in the source distribution of this project.
- func NewSchemaFromFile(filename string) (schema *Schema, err error) {
- file, err := ioutil.ReadFile(filename)
- if err != nil {
- return nil, err
- }
- var info yaml.MapSlice
- err = yaml.Unmarshal(file, &info)
- if err != nil {
- return nil, err
- }
- return NewSchemaFromObject(info), nil
- }
- // NewSchemaFromObject constructs a schema from a parsed JSON object.
- // Due to the complexity of the schema representation, this is a
- // custom reader and not the standard Go JSON reader (encoding/json).
- func NewSchemaFromObject(jsonData interface{}) *Schema {
- switch t := jsonData.(type) {
- default:
- fmt.Printf("schemaValue: unexpected type %T\n", t)
- return nil
- case yaml.MapSlice:
- schema := &Schema{}
- for _, mapItem := range t {
- k := mapItem.Key.(string)
- v := mapItem.Value
- switch k {
- case "$schema":
- schema.Schema = schema.stringValue(v)
- case "id":
- schema.ID = schema.stringValue(v)
- case "multipleOf":
- schema.MultipleOf = schema.numberValue(v)
- case "maximum":
- schema.Maximum = schema.numberValue(v)
- case "exclusiveMaximum":
- schema.ExclusiveMaximum = schema.boolValue(v)
- case "minimum":
- schema.Minimum = schema.numberValue(v)
- case "exclusiveMinimum":
- schema.ExclusiveMinimum = schema.boolValue(v)
- case "maxLength":
- schema.MaxLength = schema.intValue(v)
- case "minLength":
- schema.MinLength = schema.intValue(v)
- case "pattern":
- schema.Pattern = schema.stringValue(v)
- case "additionalItems":
- schema.AdditionalItems = schema.schemaOrBooleanValue(v)
- case "items":
- schema.Items = schema.schemaOrSchemaArrayValue(v)
- case "maxItems":
- schema.MaxItems = schema.intValue(v)
- case "minItems":
- schema.MinItems = schema.intValue(v)
- case "uniqueItems":
- schema.UniqueItems = schema.boolValue(v)
- case "maxProperties":
- schema.MaxProperties = schema.intValue(v)
- case "minProperties":
- schema.MinProperties = schema.intValue(v)
- case "required":
- schema.Required = schema.arrayOfStringsValue(v)
- case "additionalProperties":
- schema.AdditionalProperties = schema.schemaOrBooleanValue(v)
- case "properties":
- schema.Properties = schema.mapOfSchemasValue(v)
- case "patternProperties":
- schema.PatternProperties = schema.mapOfSchemasValue(v)
- case "dependencies":
- schema.Dependencies = schema.mapOfSchemasOrStringArraysValue(v)
- case "enum":
- schema.Enumeration = schema.arrayOfEnumValuesValue(v)
- case "type":
- schema.Type = schema.stringOrStringArrayValue(v)
- case "allOf":
- schema.AllOf = schema.arrayOfSchemasValue(v)
- case "anyOf":
- schema.AnyOf = schema.arrayOfSchemasValue(v)
- case "oneOf":
- schema.OneOf = schema.arrayOfSchemasValue(v)
- case "not":
- schema.Not = NewSchemaFromObject(v)
- case "definitions":
- schema.Definitions = schema.mapOfSchemasValue(v)
- case "title":
- schema.Title = schema.stringValue(v)
- case "description":
- schema.Description = schema.stringValue(v)
- case "default":
- schema.Default = &v
- case "format":
- schema.Format = schema.stringValue(v)
- case "$ref":
- schema.Ref = schema.stringValue(v)
- default:
- fmt.Printf("UNSUPPORTED (%s)\n", k)
- }
- }
- // insert schema in global map
- if schema.ID != nil {
- if schemas == nil {
- schemas = make(map[string]*Schema, 0)
- }
- schemas[*(schema.ID)] = schema
- }
- return schema
- }
- return nil
- }
- //
- // BUILDERS
- // The following methods build elements of Schemas from interface{} values.
- // Each returns nil if it is unable to build the desired element.
- //
- // Gets the string value of an interface{} value if possible.
- func (schema *Schema) stringValue(v interface{}) *string {
- switch v := v.(type) {
- default:
- fmt.Printf("stringValue: unexpected type %T\n", v)
- case string:
- return &v
- }
- return nil
- }
- // Gets the numeric value of an interface{} value if possible.
- func (schema *Schema) numberValue(v interface{}) *SchemaNumber {
- number := &SchemaNumber{}
- switch v := v.(type) {
- default:
- fmt.Printf("numberValue: unexpected type %T\n", v)
- case float64:
- v2 := float64(v)
- number.Float = &v2
- return number
- case float32:
- v2 := float64(v)
- number.Float = &v2
- return number
- case int:
- v2 := int64(v)
- number.Integer = &v2
- }
- return nil
- }
- // Gets the integer value of an interface{} value if possible.
- func (schema *Schema) intValue(v interface{}) *int64 {
- switch v := v.(type) {
- default:
- fmt.Printf("intValue: unexpected type %T\n", v)
- case float64:
- v2 := int64(v)
- return &v2
- case int64:
- return &v
- case int:
- v2 := int64(v)
- return &v2
- }
- return nil
- }
- // Gets the bool value of an interface{} value if possible.
- func (schema *Schema) boolValue(v interface{}) *bool {
- switch v := v.(type) {
- default:
- fmt.Printf("boolValue: unexpected type %T\n", v)
- case bool:
- return &v
- }
- return nil
- }
- // Gets a map of Schemas from an interface{} value if possible.
- func (schema *Schema) mapOfSchemasValue(v interface{}) *[]*NamedSchema {
- switch v := v.(type) {
- default:
- fmt.Printf("mapOfSchemasValue: unexpected type %T\n", v)
- case yaml.MapSlice:
- m := make([]*NamedSchema, 0)
- for _, mapItem := range v {
- k2 := mapItem.Key.(string)
- v2 := mapItem.Value
- pair := &NamedSchema{Name: k2, Value: NewSchemaFromObject(v2)}
- m = append(m, pair)
- }
- return &m
- }
- return nil
- }
- // Gets an array of Schemas from an interface{} value if possible.
- func (schema *Schema) arrayOfSchemasValue(v interface{}) *[]*Schema {
- switch v := v.(type) {
- default:
- fmt.Printf("arrayOfSchemasValue: unexpected type %T\n", v)
- case []interface{}:
- m := make([]*Schema, 0)
- for _, v2 := range v {
- switch v2 := v2.(type) {
- default:
- fmt.Printf("arrayOfSchemasValue: unexpected type %T\n", v2)
- case yaml.MapSlice:
- s := NewSchemaFromObject(v2)
- m = append(m, s)
- }
- }
- return &m
- case yaml.MapSlice:
- m := make([]*Schema, 0)
- s := NewSchemaFromObject(v)
- m = append(m, s)
- return &m
- }
- return nil
- }
- // Gets a Schema or an array of Schemas from an interface{} value if possible.
- func (schema *Schema) schemaOrSchemaArrayValue(v interface{}) *SchemaOrSchemaArray {
- switch v := v.(type) {
- default:
- fmt.Printf("schemaOrSchemaArrayValue: unexpected type %T\n", v)
- case []interface{}:
- m := make([]*Schema, 0)
- for _, v2 := range v {
- switch v2 := v2.(type) {
- default:
- fmt.Printf("schemaOrSchemaArrayValue: unexpected type %T\n", v2)
- case map[string]interface{}:
- s := NewSchemaFromObject(v2)
- m = append(m, s)
- }
- }
- return &SchemaOrSchemaArray{SchemaArray: &m}
- case yaml.MapSlice:
- s := NewSchemaFromObject(v)
- return &SchemaOrSchemaArray{Schema: s}
- }
- return nil
- }
- // Gets an array of strings from an interface{} value if possible.
- func (schema *Schema) arrayOfStringsValue(v interface{}) *[]string {
- switch v := v.(type) {
- default:
- fmt.Printf("arrayOfStringsValue: unexpected type %T\n", v)
- case []string:
- return &v
- case string:
- a := []string{v}
- return &a
- case []interface{}:
- a := make([]string, 0)
- for _, v2 := range v {
- switch v2 := v2.(type) {
- default:
- fmt.Printf("arrayOfStringsValue: unexpected type %T\n", v2)
- case string:
- a = append(a, v2)
- }
- }
- return &a
- }
- return nil
- }
- // Gets a string or an array of strings from an interface{} value if possible.
- func (schema *Schema) stringOrStringArrayValue(v interface{}) *StringOrStringArray {
- switch v := v.(type) {
- default:
- fmt.Printf("arrayOfStringsValue: unexpected type %T\n", v)
- case []string:
- s := &StringOrStringArray{}
- s.StringArray = &v
- return s
- case string:
- s := &StringOrStringArray{}
- s.String = &v
- return s
- case []interface{}:
- a := make([]string, 0)
- for _, v2 := range v {
- switch v2 := v2.(type) {
- default:
- fmt.Printf("arrayOfStringsValue: unexpected type %T\n", v2)
- case string:
- a = append(a, v2)
- }
- }
- s := &StringOrStringArray{}
- s.StringArray = &a
- return s
- }
- return nil
- }
- // Gets an array of enum values from an interface{} value if possible.
- func (schema *Schema) arrayOfEnumValuesValue(v interface{}) *[]SchemaEnumValue {
- a := make([]SchemaEnumValue, 0)
- switch v := v.(type) {
- default:
- fmt.Printf("arrayOfEnumValuesValue: unexpected type %T\n", v)
- case []interface{}:
- for _, v2 := range v {
- switch v2 := v2.(type) {
- default:
- fmt.Printf("arrayOfEnumValuesValue: unexpected type %T\n", v2)
- case string:
- a = append(a, SchemaEnumValue{String: &v2})
- case bool:
- a = append(a, SchemaEnumValue{Bool: &v2})
- }
- }
- }
- return &a
- }
- // Gets a map of schemas or string arrays from an interface{} value if possible.
- func (schema *Schema) mapOfSchemasOrStringArraysValue(v interface{}) *[]*NamedSchemaOrStringArray {
- m := make([]*NamedSchemaOrStringArray, 0)
- switch v := v.(type) {
- default:
- fmt.Printf("mapOfSchemasOrStringArraysValue: unexpected type %T %+v\n", v, v)
- case yaml.MapSlice:
- for _, mapItem := range v {
- k2 := mapItem.Key.(string)
- v2 := mapItem.Value
- switch v2 := v2.(type) {
- default:
- fmt.Printf("mapOfSchemasOrStringArraysValue: unexpected type %T %+v\n", v2, v2)
- case []interface{}:
- a := make([]string, 0)
- for _, v3 := range v2 {
- switch v3 := v3.(type) {
- default:
- fmt.Printf("mapOfSchemasOrStringArraysValue: unexpected type %T %+v\n", v3, v3)
- case string:
- a = append(a, v3)
- }
- }
- s := &SchemaOrStringArray{}
- s.StringArray = &a
- pair := &NamedSchemaOrStringArray{Name: k2, Value: s}
- m = append(m, pair)
- }
- }
- }
- return &m
- }
- // Gets a schema or a boolean value from an interface{} value if possible.
- func (schema *Schema) schemaOrBooleanValue(v interface{}) *SchemaOrBoolean {
- schemaOrBoolean := &SchemaOrBoolean{}
- switch v := v.(type) {
- case bool:
- schemaOrBoolean.Boolean = &v
- case yaml.MapSlice:
- schemaOrBoolean.Schema = NewSchemaFromObject(v)
- default:
- fmt.Printf("schemaOrBooleanValue: unexpected type %T\n", v)
- case []map[string]interface{}:
- }
- return schemaOrBoolean
- }
|