Skip to content

Commit

Permalink
Merge pull request #677 from tcarreira/fix-665-json-strings
Browse files Browse the repository at this point in the history
fix: fix example when using `json=",string"`
  • Loading branch information
sdghchj authored Apr 16, 2020
2 parents 86f9d9c + aff959e commit 93e5f15
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 20 deletions.
43 changes: 39 additions & 4 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down
93 changes: 93 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
36 changes: 20 additions & 16 deletions testdata/json_field_string/main.go
100644 → 100755
Original file line number Diff line number Diff line change
@@ -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()
}

0 comments on commit 93e5f15

Please sign in to comment.