diff --git a/.gitignore b/.gitignore index 68e993ce..fab45f35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.sw[nop] *.iml .vscode/ +.idea diff --git a/schema.go b/schema.go index 9e93cd79..303957a5 100644 --- a/schema.go +++ b/schema.go @@ -27,6 +27,7 @@ package gojsonschema import ( + "encoding/json" "errors" "math/big" "reflect" @@ -1085,3 +1086,19 @@ func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *subS return nil } + +// MarshalJSON convert schema to json +func (d *Schema) MarshalJSON() ([]byte, error) { + return json.Marshal(d.rootSchema) +} + +// UnmarshalJSON convert json to schema +func (d *Schema) UnmarshalJSON(bytes []byte) error { + loader := NewSchemaLoader() + if schema, err := loader.Compile(NewBytesLoader(bytes)); err != nil { + return err + } else { + *d = *schema + return nil + } +} diff --git a/subSchema.go b/subSchema.go index ec779812..9fb93f4a 100644 --- a/subSchema.go +++ b/subSchema.go @@ -27,8 +27,10 @@ package gojsonschema import ( + "encoding/json" "github.com/xeipuuv/gojsonreference" "math/big" + "reflect" "regexp" ) @@ -147,3 +149,113 @@ type subSchema struct { _then *subSchema _else *subSchema } + +func (v *subSchema) MarshalJSON() ([]byte, error) { + tmp := make(map[string]interface{}) + result := make(map[string]interface{}) + if len(v.types.types) > 0 { + switch v.types.types[0] { + //case "boolean", "null": // do nothing + case "number", "integer": + // type、multipleOf、minimum、exclusiveMinimum、maximum、exclusiveMaximum + tmp = map[string]interface{}{ + "multipleOf": ensureField(v.multipleOf), + "minimum": ensureField(v.minimum), + "exclusiveMinimum": ensureField(v.exclusiveMinimum), + "maximum": ensureField(v.maximum), + "exclusiveMaximum": ensureField(v.exclusiveMaximum), + } + case "string": + // type、minLength、maxLength、pattern、format + tmp = map[string]interface{}{ + "minLength": ensureField(v.minLength), + "maxLength": ensureField(v.maxLength), + "pattern": ensureField(v.pattern), + "format": ensureField(v.format), + } + case "object": + // type、properties、additionalProperties、required, propertyNames、minProperties、 + tmp = map[string]interface{}{ + "minProperties": ensureField(v.minProperties), + "maxProperties": ensureField(v.maxProperties), + "required": ensureField(v.required), + "dependencies": ensureField(v.dependencies), + "additionalProperties": ensureField(v.additionalProperties), + "patternProperties": ensureField(v.patternProperties), + "propertyNames": ensureField(v.propertyNames), + } + if v.propertiesChildren != nil { + properties := make(map[string]interface{}) + for _, propertyChild := range v.propertiesChildren { + properties[propertyChild.property] = propertyChild + } + tmp["properties"] = properties + } + case "array": + tmp = map[string]interface{}{ + "minItems": ensureField(v.minItems), + "maxItems": ensureField(v.maxItems), + "uniqueItems": ensureField(v.uniqueItems), + "contains": ensureField(v.contains), + "additionalItems": ensureField(v.additionalItems), + } + } + tmp["type"] = v.types.types[0] + tmp["const"] = ensureField(v._const) + tmp["enum"] = ensureField(v.enum) + } + for key, value := range tmp { + if value != nil { + result[key] = value + } + } + return json.Marshal(result) +} + +func ensureField(p interface{}) interface{} { + if isNil(p) { + return nil + } + var ret interface{} + rv := reflect.ValueOf(p) + if rv.Kind() == reflect.Ptr { + if rv.Type() == reflect.TypeOf(&big.Rat{}) { + ret = (p.(*big.Rat)).Num() + } else if rv.Type() == reflect.TypeOf(®exp.Regexp{}) { + ret = (p.(*regexp.Regexp)).String() + } else { + switch rv.Elem().Kind() { + case reflect.Int: + ret = *(p.(*int)) + case reflect.String: + ret = *(p.(*string)) + case reflect.Slice, reflect.Interface, reflect.Map: + ret = p + } + } + } else { + switch rv.Kind() { + case reflect.String: + tmp := p.(string) + if len(tmp) != 0 { + ret = tmp + } + case reflect.Slice: + ret = p + } + } + return ret +} + +func isNil(i interface{}) bool { + // https://mangatmodi.medium.com/go-check-nil-interface-the-right-way-d142776edef1 + if i == nil { + return true + } + switch reflect.TypeOf(i).Kind() { + case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: + return reflect.ValueOf(i).IsNil() + } + return false + +}