diff --git a/data_source.go b/data_source.go index 54f7e7c..b67f619 100644 --- a/data_source.go +++ b/data_source.go @@ -528,7 +528,12 @@ func (d *StructData) Get(field string) (val any, exist bool) { // TryGet value by field name. support get sub-value by path. func (d *StructData) TryGet(field string) (val any, exist, zero bool) { - field = strutil.UpperFirst(field) + // Only uppercase if the field is not already registered with its original casing. + // This allows private/unexported embedded struct fields to be accessed correctly + // when ValidatePrivateFields is enabled (e.g., "foo.Field1" must not become "Foo.Field1"). + if _, ok := d.fieldNames[field]; !ok { + field = strutil.UpperFirst(field) + } // try read from cache if fv, ok := d.fieldValues[field]; ok { return fv.Interface(), true, fv.IsZero() @@ -632,7 +637,11 @@ func (d *StructData) TryGet(field string) (val any, exist, zero bool) { // // Notice: `StructData.src` the incoming struct must be a pointer to set the value func (d *StructData) Set(field string, val any) (newVal any, err error) { - field = strutil.UpperFirst(field) + // Only uppercase if the field is not already registered with its original casing. + // This mirrors the same fix in TryGet for private embedded struct fields. + if _, ok := d.fieldNames[field]; !ok { + field = strutil.UpperFirst(field) + } if !d.HasField(field) { // field not found return nil, ErrNoField } diff --git a/data_source_test.go b/data_source_test.go index f3dc879..f07d065 100644 --- a/data_source_test.go +++ b/data_source_test.go @@ -270,23 +270,24 @@ func TestValidatePrivateFieldsWhenTrue(t *testing.T) { Field2 int `validate:"required|int" message:"Field2 outside of range"` } - fooInt := 4 - barInt := 25 - - myFoo := foo{Field1: fooInt} - barz := &bar{ - foo: myFoo, - Field2: barInt, - } - Config(func(opt *GlobalOption) { opt.ValidatePrivateFields = true }) + // Field1 = 4 violates min:5 — validation must fail for the right reason (rule violation) + barz := &bar{ + foo: foo{Field1: 4}, + Field2: 25, + } v := Struct(barz) v.Validate() - assert.Equal(t, v.hasError, true) + + // Field1 = 5 satisfies all rules — validation must pass + barz.foo.Field1 = 5 + v = Struct(barz) + v.Validate() + assert.Equal(t, v.hasError, false) } func TestValidatePrivateFieldsWhenFalse(t *testing.T) {