Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support set null map entries for non-simple map values #1782

Merged
merged 2 commits into from Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 25 additions & 1 deletion internal/gensupport/json.go
Expand Up @@ -86,7 +86,12 @@ func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNu
if f.Type.Kind() == reflect.Map && useNullMaps[f.Name] != nil {
ms, ok := v.Interface().(map[string]string)
if !ok {
return nil, fmt.Errorf("field %q has keys in NullFields but is not a map[string]string", f.Name)
mi, err := initMapSlow(v, f.Name, useNullMaps)
if err != nil {
return nil, err
}
m[tag.apiName] = mi
continue
}
mi := map[string]interface{}{}
for k, v := range ms {
Expand Down Expand Up @@ -120,6 +125,25 @@ func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNu
return m, nil
}

// initMapSlow uses reflection to build up a map object. This is slower than
// the default behavior so it should be used only as a fallback.
func initMapSlow(rv reflect.Value, fieldName string, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) {
mi := map[string]interface{}{}
iter := rv.MapRange()
for iter.Next() {
k, ok := iter.Key().Interface().(string)
if !ok {
return nil, fmt.Errorf("field %q has keys in NullFields but is not a map[string]any", fieldName)
}
v := iter.Value().Interface()
mi[k] = v
}
for k := range useNullMaps[fieldName] {
mi[k] = nil
}
return mi, nil
}

// formatAsString returns a string representation of v, dereferencing it first if possible.
func formatAsString(v reflect.Value, kind reflect.Kind) string {
if kind == reflect.Ptr && !v.IsNil() {
Expand Down
26 changes: 20 additions & 6 deletions internal/gensupport/json_test.go
Expand Up @@ -12,6 +12,10 @@ import (
"google.golang.org/api/googleapi"
)

type CustomType struct {
Foo string `json:"foo,omitempty"`
}

type schema struct {
// Basic types
B bool `json:"b,omitempty"`
Expand All @@ -28,12 +32,13 @@ type schema struct {
PStr *string `json:"pstr,omitempty"`

// Other types
Int64s googleapi.Int64s `json:"i64s,omitempty"`
S []int `json:"s,omitempty"`
M map[string]string `json:"m,omitempty"`
Any interface{} `json:"any,omitempty"`
Child *child `json:"child,omitempty"`
MapToAnyArray map[string][]interface{} `json:"maptoanyarray,omitempty"`
Int64s googleapi.Int64s `json:"i64s,omitempty"`
S []int `json:"s,omitempty"`
M map[string]string `json:"m,omitempty"`
Any interface{} `json:"any,omitempty"`
Child *child `json:"child,omitempty"`
MapToAnyArray map[string][]interface{} `json:"maptoanyarray,omitempty"`
MapToCustomType map[string]CustomType `json:"maptocustomtype,omitempty"`

ForceSendFields []string `json:"-"`
NullFields []string `json:"-"`
Expand Down Expand Up @@ -254,6 +259,15 @@ func TestMapField(t *testing.T) {
},
want: `{}`,
},
{
s: schema{
MapToCustomType: map[string]CustomType{
"a": {Foo: "foo"},
},
NullFields: []string{"MapToCustomType.b"},
},
want: `{"maptocustomtype": {"a": {"foo": "foo"}, "b": null}}`,
},
} {
checkMarshalJSON(t, tc)
}
Expand Down