Skip to content

Commit 91af8d8

Browse files
authored
Merge pull request #43 from Basekick-Labs/fix/omitempty-unexported-fields
fix(encode): respect non-zero unexported fields in omitempty check (#6)
2 parents d5e2e42 + afc6b76 commit 91af8d8

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

types.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,19 @@ func (e *Encoder) isEmptyValue(v reflect.Value) bool {
338338
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
339339
return v.Len() == 0
340340
case reflect.Struct:
341-
structFields := structs.Fields(v.Type(), e.structTag)
341+
// Check unexported fields first — if any are non-zero the struct
342+
// is not empty, even if all exported fields would be omitted.
343+
typ := v.Type()
344+
for i := 0; i < typ.NumField(); i++ {
345+
sf := typ.Field(i)
346+
if sf.PkgPath == "" || sf.Anonymous {
347+
continue // exported or embedded — handled below
348+
}
349+
if !v.Field(i).IsZero() {
350+
return false
351+
}
352+
}
353+
structFields := structs.Fields(typ, e.structTag)
342354
fields := structFields.OmitEmpty(e, v)
343355
return len(fields) == 0
344356
case reflect.Bool:

types_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,3 +1253,36 @@ func mustParseTime(format, s string) time.Time {
12531253
}
12541254
return tm
12551255
}
1256+
1257+
// Issue #6: omitempty should not omit structs with non-zero unexported fields.
1258+
type structWithUnexported struct {
1259+
secret int
1260+
Pub string `msgpack:",omitempty"`
1261+
}
1262+
1263+
type wrapperOmitEmpty struct {
1264+
Inner structWithUnexported `msgpack:",omitempty"`
1265+
}
1266+
1267+
func TestOmitEmptyUnexportedFields(t *testing.T) {
1268+
// Inner has a non-zero unexported field, so it must NOT be omitted.
1269+
w := wrapperOmitEmpty{Inner: structWithUnexported{secret: 42}}
1270+
b, err := msgpack.Marshal(w)
1271+
require.NoError(t, err)
1272+
1273+
var out map[string]interface{}
1274+
require.NoError(t, msgpack.Unmarshal(b, &out))
1275+
// "Inner" key must be present because the struct is not empty.
1276+
_, ok := out["Inner"]
1277+
require.True(t, ok, "Inner with non-zero unexported field should not be omitted")
1278+
1279+
// When both unexported and exported fields are zero, it should be omitted.
1280+
w2 := wrapperOmitEmpty{Inner: structWithUnexported{}}
1281+
b2, err := msgpack.Marshal(w2)
1282+
require.NoError(t, err)
1283+
1284+
var out2 map[string]interface{}
1285+
require.NoError(t, msgpack.Unmarshal(b2, &out2))
1286+
_, ok = out2["Inner"]
1287+
require.False(t, ok, "Inner with all zero fields should be omitted")
1288+
}

0 commit comments

Comments
 (0)