I am creating an app in Go that is supposed to receive a JSON and unmarshal it into different possible values.
To do this, I created a parent struct, that contains the possible JSON formats that the app will receive.
So the idea is to try to unmarshal the received JSON into each child struct and check if any of them didn't receive any values. If this is the case, then the app will set that struct to nil
at a later point.
The problem is that when I try to use the isNil()
method from Go's reflection, it doesn't work for a generic struct. It only works if I know the struct's format.
Here is an example (playground link):
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type User struct {
Username *string `json:"username,omitempty" required:"true"`
Password *string `json:"password,omitempty" required:"false"`
}
type UserError struct {
Error *string `json:"error,omitempty" required:"true"`
Code *int `json:"code,omitempty" required:"true"`
}
type UserOrError struct {
User *User
UserError *UserError
}
var userJson = `{ "username": "john@email.com", "password": "password" }`
func main() {
user := &UserOrError{}
data := []byte(userJson)
types := reflect.TypeOf(user).Elem()
values := reflect.ValueOf(user).Elem()
for i := 0; i < types.NumField(); i++ {
fieldType := types.Field(i)
unmarshalledValue := reflect.New(fieldType.Type)
err := json.Unmarshal(data, unmarshalledValue.Interface())
if err != nil {
panic(err)
}
value := unmarshalledValue.Elem()
fmt.Printf("Unmarshalled: %+v - hasValues: %v \n", value, hasValues(value))
// Unmarshalled: &{Username:0x14000102110 Password:0x14000102120} - hasValues: true
// Unmarshalled: &{Error:<nil> Code:<nil>} - hasValues: true
values.Field(i).Set(value)
}
fmt.Printf("User: %+v - hasValues: %v\n", user.User, hasValues(user.User))
// User: &{Username:0x14000102110 Password:0x14000102120} - hasValues: true
fmt.Printf("UserError: %+v - hasValues: %v\n", user.UserError, hasValues(user.UserError))
// UserError: &{Error:<nil> Code:<nil>} - hasValues: false
}
func hasValues(obj any) bool {
var values reflect.Value
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
values = reflect.ValueOf(obj).Elem()
} else {
values = reflect.ValueOf(obj)
}
for i := 0; i < values.NumField(); i++ {
innerValue := values.Field(i)
if innerValue.Kind() == reflect.Ptr && !innerValue.IsNil() {
return true
} else if !innerValue.IsZero() {
return true
}
}
return false
}
As you can see, isNil()
works if I know the struct beforehand, so hasValues
is false
for the UserError
struct, but if I call the same function using the generic struct that I created through reflect.New
(unmarshalledValue
), then hasValues
always returns true
.
What am I doing wrong here?
Aucun commentaire:
Enregistrer un commentaire