@@ -56,6 +56,8 @@ type fieldTags struct {
5656 omit bool
5757 omitEmpty bool
5858 noOmitEmpty bool
59+ omitZero bool
60+ noOmitZero bool
5961 asString bool
6062 required bool
6163 intern bool
@@ -76,6 +78,10 @@ func parseFieldTags(f reflect.StructField) fieldTags {
7678 ret .omitEmpty = true
7779 case s == "!omitempty" :
7880 ret .noOmitEmpty = true
81+ case s == "omitzero" :
82+ ret .omitZero = true
83+ case s == "!omitzero" :
84+ ret .noOmitZero = true
7985 case s == "string" :
8086 ret .asString = true
8187 case s == "required" :
@@ -330,7 +336,8 @@ func (g *Generator) notEmptyCheck(t reflect.Type, v string) string {
330336 return v + ` != ""`
331337 case reflect .Float32 , reflect .Float64 ,
332338 reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
333- reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
339+ reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 ,
340+ reflect .Uintptr :
334341
335342 return v + " != 0"
336343
@@ -340,6 +347,78 @@ func (g *Generator) notEmptyCheck(t reflect.Type, v string) string {
340347 }
341348}
342349
350+ func (g * Generator ) notZeroCheck (t reflect.Type , v string ) string {
351+ optionalIface := reflect .TypeOf ((* easyjson .IsZero )(nil )).Elem ()
352+ if reflect .PtrTo (t ).Implements (optionalIface ) {
353+ return "!(" + v + ").IsZero()"
354+ }
355+
356+ switch t .Kind () {
357+ case reflect .Slice , reflect .Map , reflect .Interface , reflect .Ptr :
358+ return v + " != nil"
359+ case reflect .Bool :
360+ return v
361+ case reflect .String :
362+ return v + ` != ""`
363+ case reflect .Float32 , reflect .Float64 ,
364+ reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
365+ reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 ,
366+ reflect .Uintptr :
367+
368+ return v + " != 0"
369+ case reflect .Array :
370+ // NOTE: stdlib encoding/json does not check if array elements implement IsZero, so we don't either
371+ return "(" + v + " != " + g .getType (t ) + "{})"
372+ case reflect .Struct :
373+ // NOTE: stdlib encoding/json does not check if struct fields implement IsZero, so we don't either
374+ return "(" + v + " != " + g .getType (t ) + "{})"
375+
376+ default :
377+ return "true"
378+ }
379+ }
380+
381+ func (g * Generator ) notEmptyOrZeroCheck (t reflect.Type , v string ) string {
382+ isDefinedIface := reflect .TypeOf ((* easyjson .Optional )(nil )).Elem ()
383+ implementsIsDefined := reflect .PtrTo (t ).Implements (isDefinedIface )
384+ isZeroIface := reflect .TypeOf ((* easyjson .IsZero )(nil )).Elem ()
385+ implementsIsZero := reflect .PtrTo (t ).Implements (isZeroIface )
386+
387+ if implementsIsDefined && implementsIsZero {
388+ return "(" + v + ").IsDefined() || !(" + v + ").IsZero()"
389+ } else if implementsIsDefined {
390+ return "(" + v + ").IsDefined()"
391+ } else if implementsIsZero {
392+ return "!(" + v + ").IsZero()"
393+ }
394+
395+ switch t .Kind () {
396+ case reflect .Slice , reflect .Map :
397+ return v + " != nil && len(" + v + ") != 0"
398+ case reflect .Interface , reflect .Ptr :
399+ return v + " != nil"
400+ case reflect .Bool :
401+ return v
402+ case reflect .String :
403+ return v + ` != ""`
404+ case reflect .Float32 , reflect .Float64 ,
405+ reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
406+ reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 ,
407+ reflect .Uintptr :
408+
409+ return v + " != 0"
410+ case reflect .Array :
411+ // NOTE: stdlib encoding/json does not check if array elements implement IsZero, so we don't either
412+ return "(" + v + " != " + g .getType (t ) + "{})"
413+ case reflect .Struct :
414+ // NOTE: stdlib encoding/json does not check if struct fields implement IsZero, so we don't either
415+ return "(" + v + " != " + g .getType (t ) + "{})"
416+
417+ default :
418+ return "true"
419+ }
420+ }
421+
343422func (g * Generator ) genStructFieldEncoder (t reflect.Type , f reflect.StructField , first , firstCondition bool ) (bool , error ) {
344423 jsonName := g .fieldNamer .GetJSONFieldName (t , f )
345424 tags := parseFieldTags (f )
@@ -351,18 +430,25 @@ func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField,
351430 toggleFirstCondition := firstCondition
352431
353432 noOmitEmpty := (! tags .omitEmpty && ! g .omitEmpty ) || tags .noOmitEmpty
354- if noOmitEmpty {
433+ noOmitZero := (! tags .omitZero && ! g .omitZero ) || tags .noOmitZero
434+ if noOmitEmpty && noOmitZero {
355435 fmt .Fprintln (g .out , " {" )
356436 toggleFirstCondition = false
357- } else {
437+ } else if noOmitZero {
358438 fmt .Fprintln (g .out , " if" , g .notEmptyCheck (f .Type , "in." + f .Name ), "{" )
359439 // can be any in runtime, so toggleFirstCondition stay as is
440+ } else if noOmitEmpty {
441+ fmt .Fprintln (g .out , " if" , g .notZeroCheck (f .Type , "in." + f .Name ), "{" )
442+ // can be any in runtime, so toggleFirstCondition stay as is
443+ } else {
444+ fmt .Fprintln (g .out , " if" , g .notEmptyOrZeroCheck (f .Type , "in." + f .Name ), "{" )
445+ // can be any in runtime, so toggleFirstCondition stay as is
360446 }
361447
362448 if firstCondition {
363449 fmt .Fprintf (g .out , " const prefix string = %q\n " , "," + strconv .Quote (jsonName )+ ":" )
364450 if first {
365- if ! noOmitEmpty {
451+ if ! noOmitEmpty || ! noOmitZero {
366452 fmt .Fprintln (g .out , " first = false" )
367453 }
368454 fmt .Fprintln (g .out , " out.RawString(prefix[1:])" )
0 commit comments