From aa9f9a8cfec7a42a32aa56d87e966ebf1c3a0492 Mon Sep 17 00:00:00 2001 From: dingwenjiang Date: Mon, 29 Mar 2021 18:58:53 +0800 Subject: [PATCH] feat: implement Marshaler & Unmarshaler for schema --- schema.go | 19 ++++++-- subSchema.go | 123 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 112 insertions(+), 30 deletions(-) diff --git a/schema.go b/schema.go index 27ce30f..303957a 100644 --- a/schema.go +++ b/schema.go @@ -27,6 +27,7 @@ package gojsonschema import ( + "encoding/json" "errors" "math/big" "reflect" @@ -1086,6 +1087,18 @@ func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *subS return nil } -func (d *Schema) GetSchema() *subSchema { - return d.rootSchema -} \ No newline at end of file +// 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 53a6f12..9fb93f4 100644 --- a/subSchema.go +++ b/subSchema.go @@ -30,6 +30,7 @@ import ( "encoding/json" "github.com/xeipuuv/gojsonreference" "math/big" + "reflect" "regexp" ) @@ -150,43 +151,111 @@ type subSchema struct { } func (v *subSchema) MarshalJSON() ([]byte, error) { - ret := make(map[string]interface{}) - if len(v.property) != 0{ - ret["title"] = v.property - } + tmp := make(map[string]interface{}) + result := make(map[string]interface{}) if len(v.types.types) > 0 { - ret["type"] = v.types.types[0] - switch ret["type"] { - case "number": - if v.multipleOf != nil{ - ret["multipleOf"] = v.multipleOf - } - if v.maximum != nil { - ret["maximum"] = v.maximum.Num() - } - if v.minimum != nil { - ret["minimum"] = v.minimum.Num() + 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": - if len(v.format) != 0 { - ret["format"] = v.format + // 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), } - if v.minLength != nil { - ret["minLength"] = v.minLength + 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.maxLength != nil { - ret["maxLength"] = v.maxLength + if v.propertiesChildren != nil { + properties := make(map[string]interface{}) + for _, propertyChild := range v.propertiesChildren { + properties[propertyChild.property] = propertyChild + } + tmp["properties"] = properties } - if v.pattern != nil { - ret["pattern"] = v.maxLength + 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), } - case "boolean": - case "object": } + 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 } - return json.Marshal(ret) + 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 +}