From 4ccb43ce8ab15c86e39b2735b63f6d7916bf1b73 Mon Sep 17 00:00:00 2001 From: gouhuan Date: Fri, 1 Dec 2023 18:52:21 +0800 Subject: [PATCH 1/9] Feat #36 support map kind --- api.go | 14 ++++- reflect.go | 150 +++++++++++++++++++++++++++++++++++++++++++++++- reflect_test.go | 17 ++++++ 3 files changed, 177 insertions(+), 4 deletions(-) diff --git a/api.go b/api.go index eb343ba..0f19cc9 100644 --- a/api.go +++ b/api.go @@ -41,7 +41,19 @@ type Object struct { // Property represents the property entity from the swagger definition type Property struct { - GoType reflect.Type `json:"-"` + GoType reflect.Type `json:"-"` + Type string `json:"type,omitempty"` + Description string `json:"description,omitempty"` + Enum []string `json:"enum,omitempty"` + Format string `json:"format,omitempty"` + Ref string `json:"$ref,omitempty"` + Example string `json:"example,omitempty"` + Items *Items `json:"items,omitempty"` + AddPropertie *AdditionalProperties `json:"additionalProperties,omitempty"` +} + +type AdditionalProperties struct { + GoType reflect.Type `json:"-,omitempty"` Type string `json:"type,omitempty"` Description string `json:"description,omitempty"` Enum []string `json:"enum,omitempty"` diff --git a/reflect.go b/reflect.go index cca6842..c3a48a8 100644 --- a/reflect.go +++ b/reflect.go @@ -67,16 +67,40 @@ func inspect(t reflect.Type, jsonTag string) Property { name := makeName(p.GoType) p.Ref = makeRef(name) + case reflect.Map: + p.Type = "object" + p.AddPropertie = buildMapType(p.GoType) + case reflect.Slice: p.Type = types.Array.String() p.Items = &Items{} - p.GoType = t.Elem() // dereference the slice + // dereference the slice,get the element object of the slice + // Example: + // t ==> *[]*Struct|Struct|*string|string + // p.GoType.Kind() ==> []*Struct|Struct|*string|string + if p.GoType.Kind() == reflect.Slice { + //p.GoType ==> *Struct|Struct|*string|string + p.GoType = p.GoType.Elem() + } + switch p.GoType.Kind() { case reflect.Ptr: + //p.GoType ==> Struct|string p.GoType = p.GoType.Elem() - name := makeName(p.GoType) - p.Items.Ref = makeRef(name) + + // determine the type of element object in the slice + isPrimitive := isPrimitiveType(p.GoType.Name(), p.GoType) + + if isPrimitive { + // golang built-in primitive type + kind_type := jsonSchemaType(p.GoType.String(), p.GoType) + p.Items.Type = kind_type + } else { + // Struct types + name := makeName(p.GoType) + p.Items.Ref = makeRef(name) + } case reflect.Struct: name := makeName(p.GoType) @@ -106,6 +130,126 @@ func inspect(t reflect.Type, jsonTag string) Property { return p } +// buildMapType build map type swag info +func buildMapType(mapType reflect.Type) *AdditionalProperties { + + prop := AdditionalProperties{} + + // get the element object of the map + // Example: + // mapType ==> map[string][]*Struct|Struct|*string|string + // or mapTYpe ==> map[string]*Struct|Struct|*string|string + // mapType.Elem().Kind() ==> []*Struct|Struct|*string|string + // or mapType.Elem().Kind() ==> *Struct|Struct|*string|string + if mapType.Elem().Kind().String() != "interface" { + isSlice := isSliceOrArryType(mapType.Elem().Kind()) + if isSlice || isByteArrayType(mapType.Elem()) { + // Example: + // mapType.Elem()==> []*Struct|Struct|*string|string + mapType = mapType.Elem() + } + + isPrimitive := isPrimitiveType(mapType.Elem().Name(), mapType.Elem()) + + if isByteArrayType(mapType.Elem()) { + prop.Type = "string" + } else { + if isSlice { + prop.Type = types.Array.String() + prop.Items = &Items{} + if isPrimitive { + prop.Items.Type = jsonSchemaType(mapType.Elem().String(), mapType.Elem()) + } else { + prop.Items.Ref = makeMapRef(mapType.Elem().String()) + } + } else if isPrimitive { + prop.Type = jsonSchemaType(mapType.Elem().String(), mapType.Elem()) + } else { + prop.Ref = makeMapRef(mapType.Elem().String()) + } + } + } + + return &prop +} + +func makeMapRef(typeName string) string { + type_name := strings.Trim(typeName, "*") + return makeRef(type_name) +} + +func isSliceOrArryType(t reflect.Kind) bool { + return t == reflect.Slice || t == reflect.Array +} + +func isByteArrayType(t reflect.Type) bool { + return (t.Kind() == reflect.Slice || t.Kind() == reflect.Array) && + t.Elem().Kind() == reflect.Uint8 +} + +// isPrimitiveType Whether it is a built-in primitive type +func isPrimitiveType(modelName string, modelType reflect.Type) bool { + var modelKind reflect.Kind + + if modelType.Kind() == reflect.Ptr { + modelKind = modelType.Elem().Kind() + } else { + modelKind = modelType.Kind() + } + + switch modelKind { + case reflect.Bool: + return true + case reflect.Float32, reflect.Float64, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + case reflect.String: + return true + } + + if len(modelName) == 0 { + return false + } + + return strings.Contains("time.Time time.Duration json.Number", modelName) +} + +func jsonSchemaType(modelName string, modelType reflect.Type) string { + var modelKind reflect.Kind + + if modelType.Kind() == reflect.Ptr { + modelKind = modelType.Elem().Kind() + } else { + modelKind = modelType.Kind() + } + + schemaMap := map[string]string{ + "time.Time": "string", + "time.Duration": "integer", + "json.Number": "number", + } + + if mapped, ok := schemaMap[modelName]; ok { + return mapped + } + + // check if original type is primitive + switch modelKind { + case reflect.Bool: + return "boolean" + case reflect.Float32, reflect.Float64: + return "number" + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return "integer" + case reflect.String: + return "string" + } + + return modelName // use as is (custom or struct) +} + func buildProperty(t reflect.Type) (map[string]Property, []string) { properties := make(map[string]Property) required := make([]string, 0) diff --git a/reflect_test.go b/reflect_test.go index 92d50a2..8d6a7ab 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -143,3 +143,20 @@ func TestMakeSchemaType(t *testing.T) { objSchema := MakeSchema(struct{}{}) assert.Equal(t, "", objSchema.Type, "expect array type but get %s", objSchema.Type) } + +type PetCat struct { + MapSlicePtr map[string][]*string + MapSlice map[string][]string + MapSliceStructPtr map[string][]*Person + MapSliceStruct map[string][]Person + SliceStructPtr *[]*Person + SliceStruct *[]Person + SliceStringPtr *[]*string + SliceString *[]string +} + +func TestDefineWithNewType(t *testing.T) { + v := define(PetCat{}) + b, _ := json.Marshal(v) + fmt.Printf("result:%v\n", string(b)) +} From 3b3fd7144a94063ba6d81c9da121d7bead6c407b Mon Sep 17 00:00:00 2001 From: gouhuan Date: Sat, 6 Jan 2024 17:49:35 +0800 Subject: [PATCH 2/9] support map type resolution --- api.go | 2 +- reflect.go | 26 +++++++- reflect_test.go | 37 ++++++----- testdata/pet.json | 163 +++++++++++++++++++++++++++++++++++++--------- 4 files changed, 175 insertions(+), 53 deletions(-) diff --git a/api.go b/api.go index 0f19cc9..fda374d 100644 --- a/api.go +++ b/api.go @@ -53,7 +53,7 @@ type Property struct { } type AdditionalProperties struct { - GoType reflect.Type `json:"-,omitempty"` + GoType reflect.Type `json:"-"` Type string `json:"type,omitempty"` Description string `json:"description,omitempty"` Enum []string `json:"enum,omitempty"` diff --git a/reflect.go b/reflect.go index c3a48a8..3f798c7 100644 --- a/reflect.go +++ b/reflect.go @@ -15,10 +15,9 @@ package swag import ( + "github.com/zc2638/swag/types" "reflect" "strings" - - "github.com/zc2638/swag/types" ) func inspect(t reflect.Type, jsonTag string) Property { @@ -70,6 +69,8 @@ func inspect(t reflect.Type, jsonTag string) Property { case reflect.Map: p.Type = "object" p.AddPropertie = buildMapType(p.GoType) + // get the go reflect type of the map value + p.GoType = p.AddPropertie.GoType case reflect.Slice: p.Type = types.Array.String() @@ -144,11 +145,15 @@ func buildMapType(mapType reflect.Type) *AdditionalProperties { if mapType.Elem().Kind().String() != "interface" { isSlice := isSliceOrArryType(mapType.Elem().Kind()) if isSlice || isByteArrayType(mapType.Elem()) { + // if map value is slice // Example: // mapType.Elem()==> []*Struct|Struct|*string|string mapType = mapType.Elem() } + // if map value is struct or built-in primitive type + // Example: + // mapType.Elem()==> *Struct|Struct|*string|string isPrimitive := isPrimitiveType(mapType.Elem().Name(), mapType.Elem()) if isByteArrayType(mapType.Elem()) { @@ -159,13 +164,17 @@ func buildMapType(mapType reflect.Type) *AdditionalProperties { prop.Items = &Items{} if isPrimitive { prop.Items.Type = jsonSchemaType(mapType.Elem().String(), mapType.Elem()) + prop.GoType = getGoType(mapType.Elem()) } else { prop.Items.Ref = makeMapRef(mapType.Elem().String()) + prop.GoType = getGoType(mapType.Elem()) } } else if isPrimitive { prop.Type = jsonSchemaType(mapType.Elem().String(), mapType.Elem()) + prop.GoType = getGoType(mapType.Elem()) } else { prop.Ref = makeMapRef(mapType.Elem().String()) + prop.GoType = getGoType(mapType.Elem()) } } } @@ -173,6 +182,19 @@ func buildMapType(mapType reflect.Type) *AdditionalProperties { return &prop } +func getGoType(t reflect.Type) reflect.Type { + + var goType reflect.Type + + if t.Kind() == reflect.Ptr { + goType = t.Elem() + } else { + goType = t + } + + return goType +} + func makeMapRef(typeName string) string { type_name := strings.Trim(typeName, "*") return makeRef(type_name) diff --git a/reflect_test.go b/reflect_test.go index 8d6a7ab..17dd5e3 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -50,6 +50,24 @@ type Pet struct { Bool bool Enum string `json:"enum" enum:"a,b,c" example:"b"` Anonymous + MapSlicePtr map[string][]*string + MapSlice map[string][]string + MapSliceStructPtr map[string][]*Person + MapSliceStruct map[string][]Person + SliceStructPtr *[]*Person + SliceStruct *[]Person + SliceStringPtr *[]*string + SliceString *[]string + MapNestOptions *MapObj `json:"map_nest_options,omitempty"` +} + +type MapObj struct { + RuleOptions map[string]*MapOption `json:"rule_options"` +} + +type MapOption struct { + Name string `json:"name"` + SubOptions map[string]*MapOption `json:"sub_options,omitempty"` } type Empty struct { @@ -61,7 +79,7 @@ func TestDefine(t *testing.T) { obj, ok := v["swag.Pet"] assert.True(t, ok) assert.False(t, obj.IsArray) - assert.Equal(t, 17, len(obj.Properties)) + assert.Equal(t, 26, len(obj.Properties)) content := make(map[string]Object) data, err := os.ReadFile("testdata/pet.json") @@ -143,20 +161,3 @@ func TestMakeSchemaType(t *testing.T) { objSchema := MakeSchema(struct{}{}) assert.Equal(t, "", objSchema.Type, "expect array type but get %s", objSchema.Type) } - -type PetCat struct { - MapSlicePtr map[string][]*string - MapSlice map[string][]string - MapSliceStructPtr map[string][]*Person - MapSliceStruct map[string][]Person - SliceStructPtr *[]*Person - SliceStruct *[]Person - SliceStringPtr *[]*string - SliceString *[]string -} - -func TestDefineWithNewType(t *testing.T) { - v := define(PetCat{}) - b, _ := json.Marshal(v) - fmt.Printf("result:%v\n", string(b)) -} diff --git a/testdata/pet.json b/testdata/pet.json index 2fec270..524668b 100644 --- a/testdata/pet.json +++ b/testdata/pet.json @@ -1,16 +1,48 @@ { - "swag.Person": { + "swag.MapObj": { "type": "object", "properties": { - "First": { + "rule_options": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/swag.MapOption" + } + } + } + }, + "swag.MapOption": { + "type": "object", + "properties": { + "match_src": { + "type": "string" + }, + "name": { "type": "string" + }, + "ops": { + "type": "array", + "items": { + "type": "string" + } + }, + "sub_options": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/swag.MapOption" + } + }, + "values": { + "type": "array", + "items": { + "type": "string" + } } } }, - "Anonymous": { + "swag.Person": { "type": "object", "properties": { - "AnyOne": { + "First": { "type": "string" } } @@ -21,72 +53,136 @@ "pointer" ], "properties": { + "AnyOne": { + "type": "string" + }, + "Bool": { + "type": "boolean" + }, + "Double": { + "type": "number", + "format": "double" + }, + "DoubleArray": { + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + "Float": { + "type": "number", + "format": "float" + }, + "FloatArray": { + "type": "array", + "items": { + "type": "number", + "format": "float" + } + }, "Int": { "type": "integer", "format": "int32" }, - "IntArray": { + "Int64Array": { "type": "array", "items": { "type": "integer", - "format": "int32" + "format": "int64" } }, - "Int64Array": { + "IntArray": { "type": "array", "items": { "type": "integer", - "format": "int64" + "format": "int32" } }, - "String": { - "type": "string" + "MapSlice": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } }, - "StringSecondWay": { - "type": "string" + "MapSlicePtr": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } }, - "StringArray": { + "MapSliceStruct": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/swag.Person" + } + } + }, + "MapSliceStructPtr": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/swag.Person" + } + } + }, + "SliceString": { "type": "array", "items": { "type": "string" } }, - "Float": { - "type": "number", - "format": "float" - }, - "FloatArray": { + "SliceStringPtr": { "type": "array", "items": { - "type": "number", - "format": "float" + "type": "string" } }, - "Double": { - "type": "number", - "format": "double" + "SliceStruct": { + "type": "array", + "items": { + "$ref": "#/definitions/swag.Person" + } }, - "DoubleArray": { + "SliceStructPtr": { "type": "array", "items": { - "type": "number", - "format": "double" + "$ref": "#/definitions/swag.Person" } }, - "Bool": { - "type": "boolean" + "String": { + "type": "string" }, - "AnyOne": { + "StringArray": { + "type": "array", + "items": { + "type": "string" + } + }, + "StringSecondWay": { "type": "string" }, "enum": { "type": "string", - "enum": ["a","b","c"], + "enum": [ + "a", + "b", + "c" + ], "example": "b" }, "friend": { - "$ref": "#/definitions/swag.Person", - "description": "description short expression" + "description": "description short expression", + "$ref": "#/definitions/swag.Person" }, "friends": { "type": "array", @@ -95,6 +191,9 @@ "$ref": "#/definitions/swag.Person" } }, + "map_nest_options": { + "$ref": "#/definitions/swag.MapObj" + }, "pointer": { "$ref": "#/definitions/swag.Person" }, From a4b63e254caf0ff9aab5d1476c17092b8a653595 Mon Sep 17 00:00:00 2001 From: gouhuan Date: Mon, 1 Apr 2024 15:06:16 +0800 Subject: [PATCH 3/9] fix: resolve conversation pr --- reflect.go | 23 +++++++------------- reflect_test.go | 58 ++++++++++++++++++++++++++++--------------------- types/types.go | 6 +++++ 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/reflect.go b/reflect.go index 3f798c7..8251ba1 100644 --- a/reflect.go +++ b/reflect.go @@ -80,10 +80,9 @@ func inspect(t reflect.Type, jsonTag string) Property { // Example: // t ==> *[]*Struct|Struct|*string|string // p.GoType.Kind() ==> []*Struct|Struct|*string|string - if p.GoType.Kind() == reflect.Slice { - //p.GoType ==> *Struct|Struct|*string|string - p.GoType = p.GoType.Elem() - } + + //p.GoType ==> *Struct|Struct|*string|string + p.GoType = p.GoType.Elem() switch p.GoType.Kind() { case reflect.Ptr: @@ -246,27 +245,21 @@ func jsonSchemaType(modelName string, modelType reflect.Type) string { modelKind = modelType.Kind() } - schemaMap := map[string]string{ - "time.Time": "string", - "time.Duration": "integer", - "json.Number": "number", - } - - if mapped, ok := schemaMap[modelName]; ok { + if mapped, ok := types.SchemaMap[modelName]; ok { return mapped } // check if original type is primitive switch modelKind { case reflect.Bool: - return "boolean" + return types.Boolean.String() case reflect.Float32, reflect.Float64: - return "number" + return types.Number.String() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return "integer" + return types.Integer.String() case reflect.String: - return "string" + return types.String.String() } return modelName // use as is (custom or struct) diff --git a/reflect_test.go b/reflect_test.go index 17dd5e3..5c8a21a 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -33,32 +33,32 @@ type Anonymous struct { } type Pet struct { - Friend Person `json:"friend" desc:"description short expression"` - Friends []Person `json:"friends" description:"long desc"` - Pointer *Person `json:"pointer" required:"true"` - Pointers []*Person `json:"pointers"` - Int int - IntArray []int - Int64Array []int64 - String string - StringSecondWay string `json:"StringSecondWay,string"` - StringArray []string - Float float32 - FloatArray []float32 - Double float64 - DoubleArray []float64 - Bool bool - Enum string `json:"enum" enum:"a,b,c" example:"b"` - Anonymous - MapSlicePtr map[string][]*string - MapSlice map[string][]string + //Friend Person `json:"friend" desc:"description short expression"` + //Friends []Person `json:"friends" description:"long desc"` + //Pointer *Person `json:"pointer" required:"true"` + //Pointers []*Person `json:"pointers"` + //Int int + //IntArray []int + //Int64Array []int64 + //String string + //StringSecondWay string `json:"StringSecondWay,string"` + //StringArray []string + //Float float32 + //FloatArray []float32 + //Double float64 + //DoubleArray []float64 + //Bool bool + //Enum string `json:"enum" enum:"a,b,c" example:"b"` + //Anonymous + //MapSlicePtr map[string][]*string + //MapSlice map[string][]string MapSliceStructPtr map[string][]*Person - MapSliceStruct map[string][]Person - SliceStructPtr *[]*Person - SliceStruct *[]Person - SliceStringPtr *[]*string - SliceString *[]string - MapNestOptions *MapObj `json:"map_nest_options,omitempty"` + //MapSliceStruct map[string][]Person + SliceStructPtr *[]*Person + //SliceStruct *[]Person + //SliceStringPtr *[]*string + //SliceString *[]string + //MapNestOptions *MapObj `json:"map_nest_options,omitempty"` } type MapObj struct { @@ -161,3 +161,11 @@ func TestMakeSchemaType(t *testing.T) { objSchema := MakeSchema(struct{}{}) assert.Equal(t, "", objSchema.Type, "expect array type but get %s", objSchema.Type) } + +func TestPetJson(t *testing.T) { + v := define(Pet{}) + + b, _ := json.MarshalIndent(v, "", "\t") + + fmt.Println(string(b)) +} diff --git a/types/types.go b/types/types.go index 0f375c7..f7eb4e4 100644 --- a/types/types.go +++ b/types/types.go @@ -45,6 +45,12 @@ type Context struct { PathParams map[string]string } +var SchemaMap = map[string]string{ + "time.Time": "string", + "time.Duration": "integer", + "json.Number": "number", +} + // AddURLParamsToContext returns a copy of parent in which the context value is set func AddURLParamsToContext(parent context.Context, params map[string]string) context.Context { routeVal := parent.Value(RouteContextKey) From b1ec3d0fd06eb9bfe3f2709f090bb94f68ee8d1b Mon Sep 17 00:00:00 2001 From: gouhuan Date: Mon, 1 Apr 2024 15:15:05 +0800 Subject: [PATCH 4/9] fix: restore unit test(reflect_test.go) --- reflect_test.go | 58 +++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/reflect_test.go b/reflect_test.go index 5c8a21a..17dd5e3 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -33,32 +33,32 @@ type Anonymous struct { } type Pet struct { - //Friend Person `json:"friend" desc:"description short expression"` - //Friends []Person `json:"friends" description:"long desc"` - //Pointer *Person `json:"pointer" required:"true"` - //Pointers []*Person `json:"pointers"` - //Int int - //IntArray []int - //Int64Array []int64 - //String string - //StringSecondWay string `json:"StringSecondWay,string"` - //StringArray []string - //Float float32 - //FloatArray []float32 - //Double float64 - //DoubleArray []float64 - //Bool bool - //Enum string `json:"enum" enum:"a,b,c" example:"b"` - //Anonymous - //MapSlicePtr map[string][]*string - //MapSlice map[string][]string + Friend Person `json:"friend" desc:"description short expression"` + Friends []Person `json:"friends" description:"long desc"` + Pointer *Person `json:"pointer" required:"true"` + Pointers []*Person `json:"pointers"` + Int int + IntArray []int + Int64Array []int64 + String string + StringSecondWay string `json:"StringSecondWay,string"` + StringArray []string + Float float32 + FloatArray []float32 + Double float64 + DoubleArray []float64 + Bool bool + Enum string `json:"enum" enum:"a,b,c" example:"b"` + Anonymous + MapSlicePtr map[string][]*string + MapSlice map[string][]string MapSliceStructPtr map[string][]*Person - //MapSliceStruct map[string][]Person - SliceStructPtr *[]*Person - //SliceStruct *[]Person - //SliceStringPtr *[]*string - //SliceString *[]string - //MapNestOptions *MapObj `json:"map_nest_options,omitempty"` + MapSliceStruct map[string][]Person + SliceStructPtr *[]*Person + SliceStruct *[]Person + SliceStringPtr *[]*string + SliceString *[]string + MapNestOptions *MapObj `json:"map_nest_options,omitempty"` } type MapObj struct { @@ -161,11 +161,3 @@ func TestMakeSchemaType(t *testing.T) { objSchema := MakeSchema(struct{}{}) assert.Equal(t, "", objSchema.Type, "expect array type but get %s", objSchema.Type) } - -func TestPetJson(t *testing.T) { - v := define(Pet{}) - - b, _ := json.MarshalIndent(v, "", "\t") - - fmt.Println(string(b)) -} From 49835825b98ecb04c5fff35e7019b79631264986 Mon Sep 17 00:00:00 2001 From: gouhuan Date: Mon, 1 Apr 2024 15:21:30 +0800 Subject: [PATCH 5/9] doc: update readme --- README.md | 19 +++++++++++++++++++ README_zh.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/README.md b/README.md index 2a12ee3..6daf2e6 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,25 @@ Golang 1.16+ go get -u github.com/zc2638/swag@v1.4.3 ``` +## Use note: +The time.time type uses jsonTag to parse it into a string type +```go +type XXX struct{ + Now time.Time `json:",string"` +} +``` + +The incompatibility parse is as follows: +```go +type XX struct { + Attributes1 []map[string]string //It is recommended to replace map with struct + Attributes2 [][]string + Attributes3 map[string][][]string + Attributes4 map[string][]map[string]string //It is recommended to replace map with struct + Attributes5 map[string]XX //nested self-resolution of structs is not supported +} +``` + **Tip:** As of `v1.2.0`, lower versions are no longer compatible. In order to be compatible with most web frameworks, the overall architecture has been greatly changed. diff --git a/README_zh.md b/README_zh.md index 7dd5c8c..e5219af 100644 --- a/README_zh.md +++ b/README_zh.md @@ -24,6 +24,56 @@ Golang 1.16+ go get -u github.com/zc2638/swag@v1.4.3 ``` +## 使用注意: +time.time类型使用jsonTag来将其解析为string类型 +```go +type XXX struct{ + Now time.Time `json:",string"` +} +``` + +不兼容如下解析: +```go +type XX struct { + Attributes1 []map[string]string //建议把map替换成struct + Attributes2 [][]string + Attributes3 map[string][][]string + Attributes4 map[string][]map[string]string //建议把map替换成struct + Attributes5 map[string]XX //不支持对结构体嵌套自身解析 +} +``` + +**对象嵌套自身处理方式:** + +生成swagger文档后在需手动为嵌套字段添加example,map元素嵌套结构体自身示例如下: +``` +type RuleOption struct { +MatchSrc string `json:"match_src,omitempty"` +SubOptions map[string]*RuleOption `json:"sub_options,omitempty"` +} +## 修改swagger文档中,为sub_options添加example +api.RuleOption: + type: object + properties: + match_src: + type: string + name: + type: string + ops: + type: array + items: + type: string + sub_options: + type: object + additionalProperties: + $ref: '#/definitions/api.RuleOption' + example: {"match_src":"string","name":"string","ops":"string","sub_options":{},"values":["string"]} + values: + type: array + items: + type: string +``` + **Tip:** 从 `v1.2.0` 开始,低版本不再兼容。为了兼容大部分的web框架,整体架构做了很大的改动。 ## 默认 Swagger UI 服务器 From cdddedb19b3341689dee0e5721733215cfb3883d Mon Sep 17 00:00:00 2001 From: gouhuan Date: Mon, 1 Apr 2024 16:34:26 +0800 Subject: [PATCH 6/9] doc: update README_zh.md remove example --- README_zh.md | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/README_zh.md b/README_zh.md index 61d7132..19ab67d 100644 --- a/README_zh.md +++ b/README_zh.md @@ -43,37 +43,6 @@ type XX struct { } ``` -**对象嵌套自身处理方式:** - -生成swagger文档后在需手动为嵌套字段添加example,map元素嵌套结构体自身示例如下: -``` -type RuleOption struct { -MatchSrc string `json:"match_src,omitempty"` -SubOptions map[string]*RuleOption `json:"sub_options,omitempty"` -} -## 修改swagger文档中,为sub_options添加example -api.RuleOption: - type: object - properties: - match_src: - type: string - name: - type: string - ops: - type: array - items: - type: string - sub_options: - type: object - additionalProperties: - $ref: '#/definitions/api.RuleOption' - example: {"match_src":"string","name":"string","ops":"string","sub_options":{},"values":["string"]} - values: - type: array - items: - type: string -``` - **Tip:** 从 `v1.2.0` 开始,低版本不再兼容。为了兼容大部分的web框架,整体架构做了很大的改动。 ## 默认 Swagger UI 服务器 From 71a939b98aeb56bdb0f0629966307a7602ac1155 Mon Sep 17 00:00:00 2001 From: gouhuan Date: Tue, 2 Apr 2024 11:26:00 +0800 Subject: [PATCH 7/9] update readme and isPrimitiveType return --- README.md | 18 +++++++++++------- README_zh.md | 18 +++++++++++------- reflect.go | 3 ++- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 74c9832..18ae1c5 100644 --- a/README.md +++ b/README.md @@ -32,15 +32,19 @@ type XXX struct{ } ``` -The incompatibility parse is as follows: +The compatibility is resolved as follows: ```go -type XX struct { - Attributes1 []map[string]string //It is recommended to replace map with struct - Attributes2 [][]string - Attributes3 map[string][][]string - Attributes4 map[string][]map[string]string //It is recommended to replace map with struct - Attributes5 map[string]XX //nested self-resolution of structs is not supported +type xxx struct { + MapSlicePtr map[string][]*string + MapSlice map[string][]string + MapSliceStructPtr map[string][]*Person + MapSliceStruct map[string][]Person + SliceStructPtr *[]*Person + SliceStruct *[]Person + SliceStringPtr *[]*string + SliceString *[]string } + ``` **Tip:** As of `v1.2.0`, lower versions are no longer compatible. In order to be compatible with most web frameworks, diff --git a/README_zh.md b/README_zh.md index 19ab67d..0d9e048 100644 --- a/README_zh.md +++ b/README_zh.md @@ -32,15 +32,19 @@ type XXX struct{ } ``` -不兼容如下解析: +兼容的解析如下: ```go -type XX struct { - Attributes1 []map[string]string //建议把map替换成struct - Attributes2 [][]string - Attributes3 map[string][][]string - Attributes4 map[string][]map[string]string //建议把map替换成struct - Attributes5 map[string]XX //不支持对结构体嵌套自身解析 +type xxx struct { + MapSlicePtr map[string][]*string + MapSlice map[string][]string + MapSliceStructPtr map[string][]*Person + MapSliceStruct map[string][]Person + SliceStructPtr *[]*Person + SliceStruct *[]Person + SliceStringPtr *[]*string + SliceString *[]string } + ``` **Tip:** 从 `v1.2.0` 开始,低版本不再兼容。为了兼容大部分的web框架,整体架构做了很大的改动。 diff --git a/reflect.go b/reflect.go index 8251ba1..4a6d062 100644 --- a/reflect.go +++ b/reflect.go @@ -233,7 +233,8 @@ func isPrimitiveType(modelName string, modelType reflect.Type) bool { return false } - return strings.Contains("time.Time time.Duration json.Number", modelName) + _, ok := types.SchemaMap[modelName] + return ok } func jsonSchemaType(modelName string, modelType reflect.Type) string { From f9c616cab779e1f1771e3fc2453656fd9a3377a7 Mon Sep 17 00:00:00 2001 From: gouhuan Date: Tue, 2 Apr 2024 14:52:00 +0800 Subject: [PATCH 8/9] doc: add isByteArryType annonation --- reflect.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reflect.go b/reflect.go index 4a6d062..66cebcf 100644 --- a/reflect.go +++ b/reflect.go @@ -203,6 +203,9 @@ func isSliceOrArryType(t reflect.Kind) bool { return t == reflect.Slice || t == reflect.Array } +// isByteArrayType +// Check if the data is of slice or array type +// while simultaneously verifying if the element type is of byte type(in go defined "type byte=uint8"). func isByteArrayType(t reflect.Type) bool { return (t.Kind() == reflect.Slice || t.Kind() == reflect.Array) && t.Elem().Kind() == reflect.Uint8 From 461cd285182600904a4884adb329ba26d07f5bf9 Mon Sep 17 00:00:00 2001 From: gouhuan Date: Tue, 2 Apr 2024 15:06:40 +0800 Subject: [PATCH 9/9] fix: fix unit-test testdata pet.json --- testdata/pet.json | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/testdata/pet.json b/testdata/pet.json index 6b333b9..ee477f3 100644 --- a/testdata/pet.json +++ b/testdata/pet.json @@ -1,17 +1,16 @@ { - "swag.MapObj": { - "swag.MapObj": { + "github.com_zc2638_swag.MapObj": { "type": "object", "properties": { "rule_options": { "type": "object", "additionalProperties": { - "$ref": "#/definitions/swag.MapOption" + "$ref": "#/definitions/github.com_zc2638_swag.MapOption" } } } }, - "swag.MapOption": { + "github.com_zc2638_swag.MapOption": { "type": "object", "properties": { "match_src": { @@ -29,7 +28,7 @@ "sub_options": { "type": "object", "additionalProperties": { - "$ref": "#/definitions/swag.MapOption" + "$ref": "#/definitions/github.com_zc2638_swag.MapOption" } }, "values": { @@ -40,7 +39,7 @@ } } }, - "swag.Person": { + "github.com_zc2638_swag.Person": { "type": "object", "properties": { "First": { @@ -123,7 +122,7 @@ "additionalProperties": { "type": "array", "items": { - "$ref": "#/definitions/swag.Person" + "$ref": "#/definitions/github.com_zc2638_swag.Person" } } }, @@ -132,7 +131,7 @@ "additionalProperties": { "type": "array", "items": { - "$ref": "#/definitions/swag.Person" + "$ref": "#/definitions/github.com_zc2638_swag.Person" } } }, @@ -151,13 +150,13 @@ "SliceStruct": { "type": "array", "items": { - "$ref": "#/definitions/swag.Person" + "$ref": "#/definitions/github.com_zc2638_swag.Person" } }, "SliceStructPtr": { "type": "array", "items": { - "$ref": "#/definitions/swag.Person" + "$ref": "#/definitions/github.com_zc2638_swag.Person" } }, "String": { @@ -183,7 +182,7 @@ }, "friend": { "description": "description short expression", - "$ref": "#/definitions/swag.Person" + "$ref": "#/definitions/github.com_zc2638_swag.Person" }, "friends": { "type": "array", @@ -193,7 +192,7 @@ } }, "map_nest_options": { - "$ref": "#/definitions/swag.MapObj" + "$ref": "#/definitions/github.com_zc2638_swag.MapObj" }, "pointer": { "$ref": "#/definitions/github.com_zc2638_swag.Person"