Skip to content

Commit d0477d7

Browse files
committed
Fix false-positive omitempty detection in JSON struct tags
Introduce a hasOmitemptyTag helper that treats "omitempty" as a comma-delimited option rather than an arbitrary substring. Signed-off-by: Arthur Diniz <arthurbd@debian.org>
1 parent c27f9ff commit d0477d7

2 files changed

Lines changed: 65 additions & 3 deletions

File tree

pkg/generators/openapi.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func isOptional(m *types.Member) (bool, error) {
100100

101101
// If neither +optional nor +required is present in the comments,
102102
// infer optional from the json tags.
103-
return strings.Contains(reflect.StructTag(m.Tags).Get("json"), "omitempty"), nil
103+
return hasOmitemptyTag(m), nil
104104
}
105105

106106
func apiTypeFilterFunc(c *generator.Context, t *types.Type) bool {
@@ -222,6 +222,11 @@ func getReferableName(m *types.Member) string {
222222
}
223223
}
224224

225+
func hasOmitemptyTag(m *types.Member) bool {
226+
jsonTag, _ := reflect.StructTag(m.Tags).Lookup("json")
227+
return strings.HasSuffix(jsonTag, ",omitempty") || strings.Contains(jsonTag, ",omitempty,")
228+
}
229+
225230
func shouldInlineMembers(m *types.Member) bool {
226231
jsonTag, jsonTagExists := reflect.StructTag(m.Tags).Lookup("json")
227232
return m.Embedded && jsonTagExists && (jsonTag == "" || strings.HasPrefix(jsonTag, ","))
@@ -985,7 +990,7 @@ func structZeroDefault(t *types.Type) (interface{}, error) {
985990
continue
986991
}
987992
// Include zero value for this required field
988-
memberOmitEmpty := strings.Contains(reflect.StructTag(m.Tags).Get("json"), "omitempty")
993+
memberOmitEmpty := hasOmitemptyTag(&m)
989994
zero, err := mustEnforceDefault(m.Type, memberOmitEmpty)
990995
if err != nil {
991996
return nil, err
@@ -1086,7 +1091,7 @@ func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type)
10861091
g.Do("},\n},\n", nil)
10871092
return nil
10881093
}
1089-
omitEmpty := strings.Contains(reflect.StructTag(m.Tags).Get("json"), "omitempty")
1094+
omitEmpty := hasOmitemptyTag(m)
10901095
if err := g.generateDefault(m.CommentLines, m.Type, omitEmpty, parent); err != nil {
10911096
return fmt.Errorf("failed to generate default in %v: %v: %v", parent, m.Name, err)
10921097
}

pkg/generators/openapi_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3363,3 +3363,60 @@ func TestShouldInlineMembers(t *testing.T) {
33633363
})
33643364
}
33653365
}
3366+
3367+
func TestHasOmitemptyTag(t *testing.T) {
3368+
tests := []struct {
3369+
name string
3370+
member types.Member
3371+
expected bool
3372+
}{
3373+
{
3374+
name: "standard omitempty",
3375+
member: types.Member{Tags: `json:"foo,omitempty"`},
3376+
expected: true,
3377+
},
3378+
{
3379+
name: "omitempty with no name",
3380+
member: types.Member{Tags: `json:",omitempty"`},
3381+
expected: true,
3382+
},
3383+
{
3384+
name: "omitempty in the middle of options",
3385+
member: types.Member{Tags: `json:"foo,omitempty,string"`},
3386+
expected: true,
3387+
},
3388+
{
3389+
name: "no omitempty",
3390+
member: types.Member{Tags: `json:"foo"`},
3391+
expected: false,
3392+
},
3393+
{
3394+
name: "no json tag",
3395+
member: types.Member{Tags: ``},
3396+
expected: false,
3397+
},
3398+
{
3399+
name: "field name contains omitempty - should not match",
3400+
member: types.Member{Tags: `json:"omitemptyTest"`},
3401+
expected: false,
3402+
},
3403+
{
3404+
name: "field name ends with omitempty - should not match",
3405+
member: types.Member{Tags: `json:"fooOmitempty"`},
3406+
expected: false,
3407+
},
3408+
{
3409+
name: "field name is omitempty with no options - should not match",
3410+
member: types.Member{Tags: `json:"omitempty"`},
3411+
expected: false,
3412+
},
3413+
}
3414+
3415+
for _, tc := range tests {
3416+
t.Run(tc.name, func(t *testing.T) {
3417+
if got := hasOmitemptyTag(&tc.member); got != tc.expected {
3418+
t.Errorf("hasOmitemptyTag(%+v) = %v, want %v", tc.member, got, tc.expected)
3419+
}
3420+
})
3421+
}
3422+
}

0 commit comments

Comments
 (0)