From 0e1cb15dc051cf9e592385b6a17293a2fad1234b Mon Sep 17 00:00:00 2001 From: Jack Ling <34231795+lingcoder@users.noreply.github.com> Date: Fri, 27 Feb 2026 16:14:26 +0800 Subject: [PATCH] fix(os/gstructs): strip tag options in TagPriorityName to avoid field name pollution (#4681) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Fix `TagPriorityName()` to strip comma-separated tag options (e.g., `omitempty`) from tag values - Before: `json:"user_name,omitempty"` → field name = `user_name,omitempty` - After: `json:"user_name,omitempty"` → field name = `user_name` - Aligns with `structcache.genPriorityTagAndFieldName()` which already handles this correctly - When tag name is empty (e.g., `gconv:",omitempty"`), continues to next priority tag instead of breaking ## Test plan - [x] Reproduced bug: `RuleFuncInput.Field` was `user_name,omitempty` instead of `user_name` - [x] Verified fix: field name correctly extracted as `user_name` - [x] Verified fallthrough: `gconv:",omitempty"` + `json:"name"` → uses `name` - [x] Existing `Test_Fields_TagPriorityName` passes - [x] Full `os/gstructs` test suite passes - [x] Full `util/gvalid` test suite passes - [x] Full `util/gconv` test suite passes closes #4665 --- os/gstructs/gstructs_field_tag.go | 8 ++++++-- os/gstructs/gstructs_z_unit_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/os/gstructs/gstructs_field_tag.go b/os/gstructs/gstructs_field_tag.go index fa7211515..08342b2cc 100644 --- a/os/gstructs/gstructs_field_tag.go +++ b/os/gstructs/gstructs_field_tag.go @@ -101,8 +101,12 @@ func (f *Field) TagPriorityName() string { name := f.Name() for _, tagName := range gtag.StructTagPriority { if tagValue := f.Tag(tagName); tagValue != "" { - name = tagValue - break + // Strip tag options after comma, e.g., json:"name,omitempty" -> "name". + tagValue = strings.Split(tagValue, ",")[0] + if tagValue != "" { + name = tagValue + break + } } } return name diff --git a/os/gstructs/gstructs_z_unit_test.go b/os/gstructs/gstructs_z_unit_test.go index 15b1befd5..fd68f7322 100644 --- a/os/gstructs/gstructs_z_unit_test.go +++ b/os/gstructs/gstructs_z_unit_test.go @@ -546,4 +546,32 @@ func Test_Fields_TagPriorityName(t *testing.T) { t.Assert(fields[2].TagPriorityName(), "pass_json") t.Assert(fields[3].TagPriorityName(), "IsMen") }) + // Tag with omitempty option should return name only, not "name,omitempty". + gtest.C(t, func(t *gtest.T) { + type User struct { + Name string `json:"user_name,omitempty"` + Age uint `gconv:"age,string" json:"age_json"` + } + var user *User + fields, _ := gstructs.Fields(gstructs.FieldsInput{ + Pointer: user, + RecursiveOption: 0, + }) + t.Assert(fields[0].TagPriorityName(), "user_name") + t.Assert(fields[1].TagPriorityName(), "age") + }) + // Empty tag name with options (e.g., gconv:",omitempty") should fallthrough to next priority tag. + gtest.C(t, func(t *gtest.T) { + type User struct { + Name string `gconv:",omitempty" json:"name_json"` + Age uint `gconv:",string" param:",omitempty" json:"age_json"` + } + var user *User + fields, _ := gstructs.Fields(gstructs.FieldsInput{ + Pointer: user, + RecursiveOption: 0, + }) + t.Assert(fields[0].TagPriorityName(), "name_json") + t.Assert(fields[1].TagPriorityName(), "age_json") + }) }