Last active
July 12, 2020 00:35
-
-
Save stnguyen90/06680ec293282d4e7249a3f548b283ab to your computer and use it in GitHub Desktop.
Handling Null JSON Arrays in Go - Performance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
"reflect" | |
"time" | |
) | |
// Bag holds items | |
type Bag struct { | |
Name string | |
Items []string | |
} | |
// BagAlias is a bag that does not implement the Marshaler interface | |
type BagAlias Bag | |
// MarshalJSON initializes nil slices and then marshals the bag to JSON | |
func (b Bag) MarshalJSON() ([]byte, error) { | |
a := struct { | |
BagAlias | |
}{ | |
BagAlias: (BagAlias)(b), | |
} | |
if a.Items == nil { | |
a.Items = make([]string, 0) | |
} | |
return json.Marshal(a) | |
} | |
// NilSliceToEmptySlice recursively sets nil slices to empty slices | |
func NilSliceToEmptySlice(inter interface{}) interface{} { | |
// original input that can't be modified | |
val := reflect.ValueOf(inter) | |
switch val.Kind() { | |
case reflect.Slice: | |
newSlice := reflect.MakeSlice(val.Type(), 0, val.Len()) | |
if !val.IsZero() { | |
// iterate over each element in slice | |
for j := 0; j < val.Len(); j++ { | |
item := val.Index(j) | |
var newItem reflect.Value | |
switch item.Kind() { | |
case reflect.Struct: | |
// recursively handle nested struct | |
newItem = reflect.Indirect(reflect.ValueOf(NilSliceToEmptySlice(item.Interface()))) | |
default: | |
newItem = item | |
} | |
newSlice = reflect.Append(newSlice, newItem) | |
} | |
} | |
return newSlice.Interface() | |
case reflect.Struct: | |
// new struct that will be returned | |
newStruct := reflect.New(reflect.TypeOf(inter)) | |
newVal := newStruct.Elem() | |
// iterate over input's fields | |
for i := 0; i < val.NumField(); i++ { | |
newValField := newVal.Field(i) | |
valField := val.Field(i) | |
switch valField.Kind() { | |
case reflect.Slice: | |
// recursively handle nested slice | |
newValField.Set(reflect.Indirect(reflect.ValueOf(NilSliceToEmptySlice(valField.Interface())))) | |
case reflect.Struct: | |
// recursively handle nested struct | |
newValField.Set(reflect.Indirect(reflect.ValueOf(NilSliceToEmptySlice(valField.Interface())))) | |
default: | |
newValField.Set(valField) | |
} | |
} | |
return newStruct.Interface() | |
case reflect.Map: | |
// new map to be returned | |
newMap := reflect.MakeMap(reflect.TypeOf(inter)) | |
// iterate over every key value pair in input map | |
iter := val.MapRange() | |
for iter.Next() { | |
k := iter.Key() | |
v := iter.Value() | |
// recursively handle nested value | |
newV := reflect.Indirect(reflect.ValueOf(NilSliceToEmptySlice(v.Interface()))) | |
newMap.SetMapIndex(k, newV) | |
} | |
return newMap.Interface() | |
case reflect.Ptr: | |
// dereference pointer | |
return NilSliceToEmptySlice(val.Elem().Interface()) | |
default: | |
return inter | |
} | |
} | |
// TimeIt runs f and returns how long it took | |
func TimeIt(f func()) time.Duration { | |
startTime := time.Now() | |
f() | |
endTime := time.Now() | |
return endTime.Sub(startTime) | |
} | |
func main() { | |
var diff time.Duration | |
length := 100000 | |
items := []string{"item1", "item2", "item3"} | |
lotsOfBags := make([]BagAlias, length) | |
for i := range lotsOfBags { | |
lotsOfBags[i] = BagAlias{Items: items} | |
} | |
diff = TimeIt(func() { | |
newLotsOfBags := NilSliceToEmptySlice(lotsOfBags) | |
json.Marshal(newLotsOfBags) | |
}) | |
fmt.Printf("NilSliceToEmptySlice() took %d milliseconds to process %d bags\n", diff.Milliseconds(), length) | |
bags := make([]Bag, length) | |
for i := range bags { | |
bags[i] = Bag{Items: items} | |
} | |
diff = TimeIt(func() { | |
json.Marshal(bags) | |
}) | |
fmt.Printf("MarshalJSON() took %d milliseconds to process %d bags\n", diff.Milliseconds(), length) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment