diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index aff4fb4..57eafa9 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -32,6 +32,7 @@ type Generator struct { OmitEmpty bool DisallowUnknownFields bool SkipMemberNameUnescaping bool + AllowOmitEmptyStruct bool OutName string BuildTags string @@ -137,6 +138,9 @@ func (g *Generator) writeMain() (path string, err error) { if g.SkipMemberNameUnescaping { fmt.Fprintln(f, " g.SkipMemberNameUnescaping()") } + if g.AllowOmitEmptyStruct { + fmt.Fprintln(f, " g.AllowOmitEmptyStruct()") + } sort.Strings(g.Types) for _, v := range g.Types { diff --git a/easyjson/main.go b/easyjson/main.go index 55be0ac..d0bd1b8 100644 --- a/easyjson/main.go +++ b/easyjson/main.go @@ -37,6 +37,7 @@ var processPkg = flag.Bool("pkg", false, "process the whole package instead of j var disallowUnknownFields = flag.Bool("disallow_unknown_fields", false, "return error if any unknown field in json appeared") var skipMemberNameUnescaping = flag.Bool("disable_members_unescape", false, "don't perform unescaping of member names to improve performance") var showVersion = flag.Bool("version", false, "print version and exit") +var allowOmitEmptyStruct = flag.Bool("allow_omitempty_struct", false, "skip empty struct fields if omitempty tag is present") func generate(fname string) (err error) { fInfo, err := os.Stat(fname) @@ -85,6 +86,7 @@ func generate(fname string) (err error) { NoStdMarshalers: *noStdMarshalers, DisallowUnknownFields: *disallowUnknownFields, SkipMemberNameUnescaping: *skipMemberNameUnescaping, + AllowOmitEmptyStruct: *allowOmitEmptyStruct, OmitEmpty: *omitEmpty, LeaveTemps: *leaveTemps, OutName: outName, diff --git a/gen/encoder.go b/gen/encoder.go index ed6d6ad..c441d5a 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -306,6 +306,15 @@ func (g *Generator) notEmptyCheck(t reflect.Type, v string) string { switch t.Kind() { case reflect.Slice, reflect.Map: return "len(" + v + ") != 0" + case reflect.Struct: + if g.allowOmitEmptyStruct { + val, s := reflect.New(t).Elem(), "false" + for i := 0; i < val.NumField(); i++ { + s += " || " + g.notEmptyCheck(val.Field(i).Type(), v+"."+t.Field(i).Name) + } + return s + } + return "true" case reflect.Interface, reflect.Ptr: return v + " != nil" case reflect.Bool: @@ -339,7 +348,11 @@ func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField, fmt.Fprintln(g.out, " {") toggleFirstCondition = false } else { - fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "{") + fmt.Fprintln( + g.out, " if", + strings.ReplaceAll(g.notEmptyCheck(f.Type, "in."+f.Name), "|| false", ""), + "{", + ) // can be any in runtime, so toggleFirstCondition stay as is } diff --git a/gen/generator.go b/gen/generator.go index 8598fe0..2c0250d 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -39,6 +39,7 @@ type Generator struct { fieldNamer FieldNamer simpleBytes bool skipMemberNameUnescaping bool + allowOmitEmptyStruct bool // package path to local alias map for tracking imports imports map[string]string @@ -123,6 +124,11 @@ func (g *Generator) SkipMemberNameUnescaping() { g.skipMemberNameUnescaping = true } +// AllowOmitEmptyStruct instructs to allow struct field emptiness check if omitempty tag is provided. +func (g *Generator) AllowOmitEmptyStruct() { + g.allowOmitEmptyStruct = true +} + // OmitEmpty triggers `json=",omitempty"` behaviour by default. func (g *Generator) OmitEmpty() { g.omitEmpty = true