Skip to content

Commit cb632e9

Browse files
committed
fix and add tests for the case when omitzero is combined with omitempty
Without a special case the generated code can look like if v != nil && v != nil { or if v != "" && v != "" { and that gets flagged by go vet (go 1.25.5)
1 parent d0abdfa commit cb632e9

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed

gen/encoder.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,47 @@ func (g *Generator) notZeroCheck(t reflect.Type, v string) string {
362362
}
363363
}
364364

365+
func (g *Generator) notEmptyOrZeroCheck(t reflect.Type, v string) string {
366+
isDefinedIface := reflect.TypeOf((*easyjson.Optional)(nil)).Elem()
367+
implementsIsDefined := reflect.PtrTo(t).Implements(isDefinedIface)
368+
isZeroIface := reflect.TypeOf((*easyjson.IsZero)(nil)).Elem()
369+
implementsIsZero := reflect.PtrTo(t).Implements(isZeroIface)
370+
371+
if implementsIsDefined && implementsIsZero {
372+
return "(" + v + ").IsDefined() || !(" + v + ").IsZero()"
373+
} else if implementsIsDefined {
374+
return "(" + v + ").IsDefined()"
375+
} else if implementsIsZero {
376+
return "!(" + v + ").IsZero()"
377+
}
378+
379+
switch t.Kind() {
380+
case reflect.Slice, reflect.Map:
381+
return v + " != nil && len(" + v + ") != 0"
382+
case reflect.Interface, reflect.Ptr:
383+
return v + " != nil"
384+
case reflect.Bool:
385+
return v
386+
case reflect.String:
387+
return v + ` != ""`
388+
case reflect.Float32, reflect.Float64,
389+
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
390+
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
391+
reflect.Uintptr:
392+
393+
return v + " != 0"
394+
case reflect.Array:
395+
// NOTE: stdlib encoding/json does not check if array elements implement IsZero, so we don't either
396+
return "(" + v + " != " + g.getType(t) + "{})"
397+
case reflect.Struct:
398+
// NOTE: stdlib encoding/json does not check if struct fields implement IsZero, so we don't either
399+
return "(" + v + " != " + g.getType(t) + "{})"
400+
401+
default:
402+
return "true"
403+
}
404+
}
405+
365406
func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField, first, firstCondition bool) (bool, error) {
366407
jsonName := g.fieldNamer.GetJSONFieldName(t, f)
367408
tags := parseFieldTags(f)
@@ -384,7 +425,7 @@ func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField,
384425
fmt.Fprintln(g.out, " if", g.notZeroCheck(f.Type, "in."+f.Name), "{")
385426
// can be any in runtime, so toggleFirstCondition stay as is
386427
} else {
387-
fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "&&", g.notZeroCheck(f.Type, "in."+f.Name), "{")
428+
fmt.Fprintln(g.out, " if", g.notEmptyOrZeroCheck(f.Type, "in."+f.Name), "{")
388429
// can be any in runtime, so toggleFirstCondition stay as is
389430
}
390431

tests/basic_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ var testCases = []struct {
2525
{&structsValue, structsString},
2626
{&omitEmptyValue, omitEmptyString},
2727
{&omitZeroValue, omitZeroString},
28+
{&omitEmptyAndZeroValue, omitEmptyAndZeroString},
2829
{&snakeStructValue, snakeStructString},
2930
{&omitEmptyDefaultValue, omitEmptyDefaultString},
3031
{&omitZeroDefaultValue, omitZeroDefaultString},

tests/data.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,43 @@ var omitZeroString = "{" +
395395
`"SubPNZ":{"Value":"3","Value2":"4"}` +
396396
"}"
397397

398+
type OmitEmptyAndZero struct {
399+
// NOTE: first field is empty to test comma printing.
400+
401+
StrZ, StrNZ string `json:",omitempty,omitzero"`
402+
ZPtr, PtrZ, PtrNZ *string `json:",omitempty,omitzero"`
403+
404+
IntNZ int `json:"intField,omitempty,omitzero"`
405+
IntZ int `json:",omitempty,omitzero"`
406+
407+
// NOTE: omitzero DOES have effect on non-pointer struct fields.
408+
SubZ, SubNZ SubStruct `json:",omitempty,omitzero"`
409+
SubZP, SubPZ, SubPNZ *SubStruct `json:",omitempty,omitzero"`
410+
411+
// test IsZero()bool is respected
412+
Time time.Time `json:",omitempty,omitzero"`
413+
}
414+
415+
var omitEmptyAndZeroValue = OmitZero{
416+
StrNZ: "str",
417+
PtrZ: new(string),
418+
PtrNZ: &str,
419+
IntNZ: 6,
420+
SubNZ: SubStruct{Value: "1", Value2: "2"},
421+
SubPZ: &SubStruct{},
422+
SubPNZ: &SubStruct{Value: "3", Value2: "4"},
423+
}
424+
425+
var omitEmptyAndZeroString = "{" +
426+
`"StrNZ":"str",` +
427+
`"PtrZ":"",` +
428+
`"PtrNZ":"bla",` +
429+
`"intField":6,` +
430+
`"SubNZ":{"Value":"1","Value2":"2"},` +
431+
`"SubPZ":{"Value":"","Value2":""},` +
432+
`"SubPNZ":{"Value":"3","Value2":"4"}` +
433+
"}"
434+
398435
type Opts struct {
399436
StrNull opt.String
400437
StrEmpty opt.String

0 commit comments

Comments
 (0)