Skip to content

Commit 3b78029

Browse files
committed
fix: keep default json tag when go.tag has no json key
When go.tag is set without a json: entry and gen_json_tag is enabled, the default json tag is now preserved instead of being dropped. Also deprecate always_gen_json_tag, which existed only as a workaround for this behavior.
1 parent ed5b3ba commit 3b78029

4 files changed

Lines changed: 87 additions & 4 deletions

File tree

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ Boolean options accept `true`, `false`, or empty (empty means `true`).
118118
| `unescape_double_quote` | **true** | Unescape double quotes in tag literals. |
119119
| `gen_type_meta` | false | Generate and register type metadata for structs. |
120120
| `gen_json_tag` | **true** | Generate `json` struct tags. |
121-
| `always_gen_json_tag` | false | Generate `json` tags even when a `go.tag` annotation is present. |
122121
| `snake_style_json_tag` | false | Use `snake_case` style for JSON tags. |
123122
| `lower_camel_style_json_tag` | false | Use `lowerCamelCase` style for JSON tags. |
124123
| `with_reflection` | false | Generate `*-reflection.go` files with runtime type metadata for structs.<br>Required by `with_field_mask`. |

generator/golang/option.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ type Features struct {
4545
EscapeDoubleInTag bool `unescape_double_quote:"Unescape the double quotes in literals when generating go tags."`
4646
GenerateTypeMeta bool `gen_type_meta:"Generate and register type meta for structures."`
4747
GenerateJSONTag bool `gen_json_tag:"Generate struct with 'json' tag"`
48-
AlwaysGenerateJSONTag bool `always_gen_json_tag:"Always generate 'json' tag even if go.tag is provided (Disabled by default)"`
48+
AlwaysGenerateJSONTag bool `always_gen_json_tag:"Deprecated."`
4949
SnakeTyleJSONTag bool `snake_style_json_tag:"Generate snake style json tag"`
5050
LowerCamelCaseJSONTag bool `lower_camel_style_json_tag:"Generate lower camel case style json tag"`
5151
WithReflection bool `with_reflection:"Generate *-reflection.go files with runtime type metadata for structs (required by with_field_mask)."`
@@ -294,6 +294,10 @@ func (cu *CodeUtils) validateOptions() error {
294294
return fmt.Errorf("always_gen_json_tag requires gen_json_tag")
295295
}
296296

297+
if f.AlwaysGenerateJSONTag {
298+
cu.Warn("always_gen_json_tag is deprecated: gen_json_tag now keeps the default json tag when go.tag has no json key")
299+
}
300+
297301
if f.StreamX && !f.ThriftStreaming {
298302
cu.Warn("streamx has no effect without thrift_streaming")
299303
}

generator/golang/util.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package golang
1717
import (
1818
"fmt"
1919
"path/filepath"
20+
"reflect"
2021
"regexp"
2122
"runtime"
2223
"sort"
@@ -329,6 +330,7 @@ func (cu *CodeUtils) genFieldTags(f *Field, insertPoint string, extend []string)
329330
tags = append(tags, extend...)
330331

331332
gotags := f.Annotations.Get("go.tag")
333+
hasJSONInGoTag := false
332334
if len(gotags) > 0 {
333335
tag := gotags[0]
334336
if cu.Features().EscapeDoubleInTag {
@@ -339,14 +341,15 @@ func (cu *CodeUtils) genFieldTags(f *Field, insertPoint string, extend []string)
339341
return m
340342
})
341343
}
344+
_, hasJSONInGoTag = reflect.StructTag(tag).Lookup("json")
342345
tags = append(tags, tag)
343346
} else {
344347
if cu.Features().GenDatabaseTag {
345348
tags = append(tags, fmt.Sprintf(`db:"%s"`, f.Name))
346349
}
347350
}
348351

349-
if len(gotags) == 0 && cu.Features().GenerateJSONTag || cu.Features().AlwaysGenerateJSONTag {
352+
if (!hasJSONInGoTag && cu.Features().GenerateJSONTag) || cu.Features().AlwaysGenerateJSONTag {
350353
id := f.Name
351354
if cu.Features().SnakeTyleJSONTag {
352355
id = snakify(id)

generator/golang/util_test.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
package golang
1616

1717
import (
18-
"github.com/cloudwego/thriftgo/parser"
1918
"testing"
19+
20+
"github.com/cloudwego/thriftgo/generator/backend"
21+
"github.com/cloudwego/thriftgo/parser"
2022
)
2123

2224
func TestSnakify(t *testing.T) {
@@ -222,3 +224,78 @@ func TestGenAnnotations(t *testing.T) {
222224
})
223225
}
224226
}
227+
228+
func TestGenFieldTags(t *testing.T) {
229+
newField := func(annos parser.Annotations) *Field {
230+
return &Field{Field: &parser.Field{
231+
ID: 1,
232+
Name: "Name",
233+
Requiredness: parser.FieldType_Default,
234+
Annotations: annos,
235+
}}
236+
}
237+
238+
cases := []struct {
239+
desc string
240+
field *Field
241+
feats func(f *Features)
242+
expected string
243+
}{
244+
{
245+
desc: "no go.tag, gen_json_tag default on",
246+
field: newField(nil),
247+
expected: "`thrift:\"Name,1\" json:\"Name\"`",
248+
},
249+
{
250+
desc: "go.tag with json: keeps go.tag json, no duplicate",
251+
field: newField(parser.Annotations{{Key: "go.tag", Values: []string{`json:"n" yaml:"n"`}}}),
252+
expected: "`thrift:\"Name,1\" json:\"n\" yaml:\"n\"`",
253+
},
254+
{
255+
desc: "go.tag without json: still gets default json tag",
256+
field: newField(parser.Annotations{{Key: "go.tag", Values: []string{`yaml:"n"`}}}),
257+
expected: "`thrift:\"Name,1\" yaml:\"n\" json:\"Name\"`",
258+
},
259+
{
260+
desc: "go.tag without json: but gen_json_tag off → no json tag",
261+
field: newField(parser.Annotations{{Key: "go.tag", Values: []string{`yaml:"n"`}}}),
262+
feats: func(f *Features) {
263+
f.GenerateJSONTag = false
264+
},
265+
expected: "`thrift:\"Name,1\" yaml:\"n\"`",
266+
},
267+
{
268+
desc: "always_gen_json_tag forces json even when go.tag has json",
269+
field: newField(parser.Annotations{{Key: "go.tag", Values: []string{`json:"n"`}}}),
270+
feats: func(f *Features) {
271+
f.AlwaysGenerateJSONTag = true
272+
},
273+
expected: "`thrift:\"Name,1\" json:\"n\" json:\"Name\"`",
274+
},
275+
{
276+
// e.g. single-quoted thrift literal `'json:\"n\"'` survives the parser as raw `json:\"n\"`;
277+
// the lookup must run after EscapeDoubleInTag or the json key is missed.
278+
desc: "go.tag with escaped quotes still detects json key",
279+
field: newField(parser.Annotations{{Key: "go.tag", Values: []string{`json:\"n\"`}}}),
280+
expected: "`thrift:\"Name,1\" json:\"n\"`",
281+
},
282+
}
283+
284+
for _, c := range cases {
285+
t.Run(c.desc, func(t *testing.T) {
286+
cu := NewCodeUtils(backend.DummyLogFunc())
287+
feats := defaultFeatures
288+
if c.feats != nil {
289+
c.feats(&feats)
290+
}
291+
cu.SetFeatures(feats)
292+
got, err := cu.GenFieldTags(c.field, "")
293+
if err != nil {
294+
t.Fatalf("unexpected err: %v", err)
295+
}
296+
if got != c.expected {
297+
t.Fatalf("got %q, want %q", got, c.expected)
298+
}
299+
})
300+
}
301+
}

0 commit comments

Comments
 (0)