From d6768890eced49d7982c7f412403df21b2e47997 Mon Sep 17 00:00:00 2001 From: Aravind Gopalan Date: Thu, 21 Nov 2019 18:03:16 -0800 Subject: [PATCH] Allow empty maps for required map fields (issue #256) As reported in issue #256, currently, an empty map is unmarshalled into nil by easyjson If a field is marked "required" or "!omitempty", then the empty map is a valid input and we should unmarshal it into an empty map. Similar logic for marshalling. This change is to fix the behavior --- .gitignore | 1 + gen/decoder.go | 13 +++++++++---- tests/data.go | 6 ++++++ tests/required_test.go | 23 +++++++++++++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 26156fb..fbfaf7a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.iml .idea *.swp +bin/* diff --git a/gen/decoder.go b/gen/decoder.go index ab79869..cd8d42d 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -228,16 +228,21 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field } // else assume the caller knows what they are doing and that the custom unmarshaler performs the translation from string or integer keys to the key type elem := t.Elem() tmpVar := g.uniqueVarName() + keepEmpty := tags.required || tags.noOmitEmpty || (!g.omitEmpty && !tags.omitEmpty) fmt.Fprintln(g.out, ws+"if in.IsNull() {") fmt.Fprintln(g.out, ws+" in.Skip()") fmt.Fprintln(g.out, ws+"} else {") fmt.Fprintln(g.out, ws+" in.Delim('{')") - fmt.Fprintln(g.out, ws+" if !in.IsDelim('}') {") + if !keepEmpty { + fmt.Fprintln(g.out, ws+" if !in.IsDelim('}') {") + } fmt.Fprintln(g.out, ws+" "+out+" = make("+g.getType(t)+")") - fmt.Fprintln(g.out, ws+" } else {") - fmt.Fprintln(g.out, ws+" "+out+" = nil") - fmt.Fprintln(g.out, ws+" }") + if !keepEmpty { + fmt.Fprintln(g.out, ws+" } else {") + fmt.Fprintln(g.out, ws+" "+out+" = nil") + fmt.Fprintln(g.out, ws+" }") + } fmt.Fprintln(g.out, ws+" for !in.IsDelim('}') {") // NOTE: extra check for TextUnmarshaler. It overrides default methods. diff --git a/tests/data.go b/tests/data.go index 6ae90a0..5e6c610 100644 --- a/tests/data.go +++ b/tests/data.go @@ -678,6 +678,12 @@ type RequiredOptionalStruct struct { Lastname string `json:"last_name"` } +type RequiredOptionalMap struct { + ReqMap map[int]string `json:"req_map,required"` + OmitEmptyMap map[int]string `json:"oe_map,omitempty"` + NoOmitEmptyMap map[int]string `json:"noe_map,!omitempty"` +} + //easyjson:json type EncodingFlagsTestMap struct { F map[string]string diff --git a/tests/required_test.go b/tests/required_test.go index 8cc743d..36a37c8 100644 --- a/tests/required_test.go +++ b/tests/required_test.go @@ -2,6 +2,7 @@ package tests import ( "fmt" + "reflect" "testing" ) @@ -26,3 +27,25 @@ func TestRequiredField(t *testing.T) { } } } + +func TestRequiredOptionalMap(t *testing.T) { + baseJson := `{"req_map":{}, "oe_map":{}, "noe_map":{}, "oe_slice":[]}` + wantDecoding := RequiredOptionalMap{MapIntString{}, nil, MapIntString{}} + + var v RequiredOptionalMap + if err := v.UnmarshalJSON([]byte(baseJson)); err != nil { + t.Errorf("%s. UnmarshalJSON didn't expect error: %v", baseJson, err) + } + if !reflect.DeepEqual(v, wantDecoding) { + t.Errorf("%s. UnmarshalJSON expected to gen: %v. got: %v", baseJson, wantDecoding, v) + } + + baseStruct := RequiredOptionalMap{MapIntString{}, MapIntString{}, MapIntString{}} + wantJson := `{"req_map":{},"noe_map":{}}` + data, err := baseStruct.MarshalJSON() + if err != nil { + t.Errorf("MarshalJSON didn't expect error: %v on %v", err, data) + } else if string(data) != wantJson { + t.Errorf("%v. MarshalJSON wanted: %s got %s", baseStruct, wantJson, string(data)) + } +}