For an API I have defined a "Client" struct which contains all the fields the client has in the database. When a GET request is made with the client ID the whole struct(The struct contains about 60 fields of different types) is returned with all available data. The request can be customized with a querystring "fields" which allow the user to return specific "Client" fields only regardless if they are empty, so omitempty or "-" will not work for my usecase.
GO playground example: https://play.golang.org/p/NTGxGQFz7-Y
type Client struct {
Firstname string `json:"firstname"`
AltAddress string `json:"alt_address"`
Lastname string `json:"lastname"`
}
Example: GET GET /clients/id/1
Response:
{
"firstname": "Michael",
"alt_address": "",
"lastname": "Smith"
}
Example: GET with querystring Get /clients/id/1?fields=firstname, alt_address
Response:
{
"firstname": "Michael",
"alt_address": ""
}
I have this method(objectToFilteredMap) which converts an interface(struct for example) to a map, removes the fields that are NOT defined in "fieldNames" and outputs it as a json object. But I feel like I am using to many "for" loops. Is there a more efficient way to filter out the unneeded fields? Or any other improvements would be appreciated.
var (
regex = regexp.MustCompile(`^\w+$`)
)
func inSlice(slice []string, val string) bool {
for _, item := range slice {
if item == val {
return true
}
}
return false
}
func toMap(obj interface{}, fieldNames []string) (map[string]interface{}, error) {
for k, f := range fieldNames {
f = strings.TrimSpace(f)
if !regex.Match([]byte(f)) {
return nil, fmt.Errorf("Invalid field name: `%s`", f)
}
fieldNames[k] = strings.ToLower(f)
}
j, err := json.Marshal(obj)
if err != nil {
return nil, err
}
objMap := map[string]interface{}{}
err = json.Unmarshal(j, &objMap)
if err != nil {
return nil, err
}
for k := range objMap {
if !inSlice(fieldNames, k) {
delete(objMap, k)
}
}
if len(objMap) == 0 {
return nil, fmt.Errorf("field(s) not found")
}
return objMap, nil
}
json:"[name],omitempty"
tagged so it only contains the fields you want to return when you marshall it. Not a direct review of your code but another possible approach. Anything that's starting to get dynamic with lots ofmap[string]interface{}
is going to feel a bit off in Go compared to Python or other dynamically typed languages \$\endgroup\$