diff --git a/parser.go b/parser.go index 52c32678a..5d86ab319 100644 --- a/parser.go +++ b/parser.go @@ -1207,8 +1207,14 @@ func (parser *Parser) parseField(pkgName string, field *ast.Field) (*structField } jsonTag := structTag.Get("json") + hasStringTag := false // json:"tag,hoge" if strings.Contains(jsonTag, ",") { + // json:"name,string" or json:",string" + if strings.Contains(jsonTag, ",string") { + hasStringTag = true + } + // json:",hoge" if strings.HasPrefix(jsonTag, ",") { jsonTag = "" @@ -1249,11 +1255,16 @@ func (parser *Parser) parseField(pkgName string, field *ast.Field) (*structField } } if exampleTag := structTag.Get("example"); exampleTag != "" { - example, err := defineTypeOfExample(structField.schemaType, structField.arrayType, exampleTag) - if err != nil { - return nil, err + if hasStringTag { + // then the example must be in string format + structField.exampleValue = exampleTag + } else { + example, err := defineTypeOfExample(structField.schemaType, structField.arrayType, exampleTag) + if err != nil { + return nil, err + } + structField.exampleValue = example } - structField.exampleValue = example } if formatTag := structTag.Get("format"); formatTag != "" { structField.formatType = formatTag @@ -1337,6 +1348,30 @@ func (parser *Parser) parseField(pkgName string, field *ast.Field) (*structField structField.readOnly = readOnly == "true" } + // perform this after setting everything else (min, max, etc...) + if hasStringTag { + + // @encoding/json: "It applies only to fields of string, floating point, integer, or boolean types." + defaultValues := map[string]string{ + // Zero Values as string + "string": "", + "integer": "0", + "boolean": "false", + "number": "0", + } + + if defaultValue, ok := defaultValues[structField.schemaType]; ok { + structField.schemaType = "string" + + if structField.exampleValue == nil { + // if exampleValue is not defined by the user, + // we will force an example with a correct value + // (eg: int->"0", bool:"false") + structField.exampleValue = defaultValue + } + } + } + return structField, nil } diff --git a/parser_test.go b/parser_test.go index 7baa835c0..1bf371bee 100644 --- a/parser_test.go +++ b/parser_test.go @@ -3111,3 +3111,96 @@ func Fun() { ref = path.Get.Responses.ResponsesProps.StatusCodeResponses[200].ResponseProps.Schema.Ref assert.Equal(t, "#/definitions/Teacher", ref.String()) } + +func TestParseJSONFieldString(t *testing.T) { + expected := `{ + "swagger": "2.0", + "info": { + "description": "This is a sample server.", + "title": "Swagger Example API", + "contact": {}, + "license": {}, + "version": "1.0" + }, + "host": "localhost:4000", + "basePath": "/", + "paths": { + "/do-something": { + "post": { + "description": "Does something", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Call DoSomething", + "parameters": [ + { + "description": "My Struct", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/main.MyStruct" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.MyStruct" + } + }, + "500": {} + } + } + } + }, + "definitions": { + "main.MyStruct": { + "type": "object", + "properties": { + "boolvar": { + "description": "boolean as a string", + "type": "string", + "example": "false" + }, + "floatvar": { + "description": "float as a string", + "type": "string", + "example": "0" + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "myint": { + "description": "integer as string", + "type": "string", + "example": "0" + }, + "name": { + "type": "string", + "example": "poti" + }, + "truebool": { + "description": "boolean as a string", + "type": "string", + "example": "true" + } + } + } + } +}` + + searchDir := "testdata/json_field_string" + mainAPIFile := "main.go" + p := New() + err := p.ParseAPI(searchDir, mainAPIFile) + assert.NoError(t, err) + b, _ := json.MarshalIndent(p.swagger, "", " ") + assert.Equal(t, expected, string(b)) +} diff --git a/testdata/json_field_string/main.go b/testdata/json_field_string/main.go old mode 100644 new mode 100755 index 04515dccb..599545224 --- a/testdata/json_field_string/main.go +++ b/testdata/json_field_string/main.go @@ -1,39 +1,43 @@ package main import ( + "net/http" + "github.com/gin-gonic/gin" ) type MyStruct struct { - ID int `json:"id" example:"1" format:"int64"` - // Post name - Name string `json:"name" example:"poti"` - // Post data - Data struct { - // Post tag - Tag []string `json:"name"` - } `json:"data"` - // Integer represented by a string - MyInt int `json:"myint,string"` + ID int `json:"id" example:"1" format:"int64"` + Name string `json:"name" example:"poti"` + Intvar int `json:"myint,string"` // integer as string + Boolvar bool `json:",string"` // boolean as a string + TrueBool bool `json:"truebool,string" example:"true"` // boolean as a string + Floatvar float64 `json:",string"` // float as a string } // @Summary Call DoSomething -// @Description Does something, but internal (non-exported) fields inside a struct won't be marshaled into JSON +// @Description Does something // @Accept json // @Produce json -// @Success 200 {string} MyStruct -// @Router /so-something [get] +// @Param body body MyStruct true "My Struct" +// @Success 200 {object} MyStruct +// @Failure 500 +// @Router /do-something [post] func DoSomething(c *gin.Context) { - //write your code + objectFromJSON := new(MyStruct) + if err := c.BindJSON(&objectFromJSON); err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + } + c.JSON(http.StatusOK, objectFromJSON) } // @title Swagger Example API // @version 1.0 // @description This is a sample server. // @host localhost:4000 -// @basePath /api +// @basePath / func main() { r := gin.New() - r.GET("/do-something", api.DoSomething) + r.POST("/do-something", DoSomething) r.Run() }