I will leave it as a memorandum because I lack understanding of reflect.
I want to enter a specific value for a structure whose field elements are unknown.
go playground Here
I used a recursive function.
func recursive(in reflect.Value, v int) {
rt := in.Kind()
if rt == reflect.Ptr {
vPtr := in
if in.IsNil() {
vPtr = reflect.New(in.Type().Elem())
}
recursive(vPtr.Elem(), v)
if in.CanSet() {
in.Set(vPtr)
}
return
}
if rt == reflect.Struct {
tValue := in.Type()
for i := 0; i < in.NumField(); i++ {
sf := tValue.Field(i)
if sf.PkgPath != "" && !sf.Anonymous {
continue
}
recursive(in.Field(i), v)
}
return
}
if rt == reflect.String {
strV := strconv.Itoa(v)
in.Set(reflect.ValueOf(strV))
return
}
if rt == reflect.Int {
in.Set(reflect.ValueOf(v))
return
}
}
reflect.Value is a pointerWith this process, it is determined whether the field is a pointer, and if it is nil, reflct.Value is generated.
If it is not nil, use the first argument of the recursive function as it is.
Pass the pointer reference to the first argument of the recursive function and call it again.
After calling, if CanSet is true, set Value to Set as the first argument.
if rt == reflect.Ptr {
vPtr := in
if in.IsNil() {
vPtr = reflect.New(in.Type().Elem())
}
recursive(vPtr.Elem(), v)
if in.CanSet() {
in.Set(vPtr)
}
return
}
reflect.Value is a structureEnumerate the fields of the structure, pass each field as the first argument of the recursive function, and call it again.
However, the case of PkgPath (package) and the case of Anonymous (embedded type) are excluded (there is a case where it becomes a permanent loop if it is not excluded).
if rt == reflect.Struct {
tValue := in.Type()
for i := 0; i < in.NumField(); i++ {
sf := tValue.Field(i)
if sf.PkgPath != "" && !sf.Anonymous {
continue
}
recursive(in.Field(i), v)
}
return
}
reflect.Value is string, intIn the case of string, it is converted, and in the case of int, it is Set with reflect.ValueOf.
(Since there are cases other than string and int, we are investigating whether it can be simplified a little more ...)
if rt == reflect.String {
strV := strconv.Itoa(v)
in.Set(reflect.ValueOf(strV))
return
}
if rt == reflect.Int {
in.Set(reflect.ValueOf(v))
return
}
map and print out any missing dataConvert from map [string] string to an arbitrary structure, and if there are not enough elements in the fields of the structure, I would like to know the details.
Specify a tag for the field of the structure, and if the field is on the pointer side, treat it as optional.
Also, by making the first argument of bindParameters a interface type, it corresponds to any structure.
go playground Here
Modified based on the recursive function of [here](#corresponding code).
func recursive(in reflect.Value, tag string, required bool, v map[string]string, res *map[string]interface{}) bool {
rk := in.Kind()
if rk == reflect.Ptr {
vPtr := in
if in.IsNil() {
vPtr = reflect.New(in.Type().Elem())
}
isSet := recursive(vPtr.Elem(), tag, false, v, res)
if in.CanSet() && isSet {
in.Set(vPtr)
}
return false
}
if rk == reflect.Struct {
tValue := in.Type()
isSet := false
for i := 0; i < in.NumField(); i++ {
sf := tValue.Field(i)
if sf.PkgPath != "" && !sf.Anonymous {
continue
}
tagName := ""
required = false
if uriTag := sf.Tag.Get(key); uriTag != "" {
tagName = uriTag
required = true
}
r := recursive(in.Field(i), tagName, required, v, res)
if r {
isSet = true
}
}
return isSet
}
ptrValue, errString := getValue(rk, required, tag, v)
if errString != "" {
e := *res
e[tag] = errString
res = &e
}
if ptrValue != nil {
value := *ptrValue
in.Set(value)
return true
}
return false
}
func getValue(kind reflect.Kind, required bool, tag string, m map[string]string) (*reflect.Value, string) {
var value string
var ok bool
if value, ok = m[tag]; !ok && required {
return nil, "parameter is not specified"
}
if value == "" && !required {
return nil, ""
}
if kind == reflect.String {
r := reflect.ValueOf(value)
return &r, ""
}
if kind == reflect.Int {
i, err := strconv.Atoi(value)
if err != nil {
return nil, "not numeric value"
}
r := reflect.ValueOf(i)
return &r, ""
}
return nil, ""
}
You can get the tag name of the structure with reflect.StructField.sf.Tag.Get.
if rk == reflect.Struct {
tValue := in.Type()
isSet := false
for i := 0; i < in.NumField(); i++ {
sf := tValue.Field(i)
if sf.PkgPath != "" && !sf.Anonymous {
continue
}
tagName := ""
required = false
if uriTag := sf.Tag.Get(key); uriTag != "" {
tagName = uriTag
required = true
}
r := recursive(in.Field(i), tagName, required, v, res)
if r {
isSet = true
}
}
return isSet
}
mapIf it cannot be extracted, parameter is not specified, and if it cannot be converted to string, not numeric value is return.
(We are investigating a little smarter writing style, including how to handle other types ...)
if value, ok = m[tag]; !ok && required {
return nil, "parameter is not specified"
}
if value == "" && !required {
return nil, ""
}
if kind == reflect.String {
r := reflect.ValueOf(value)
return &r, ""
}
if kind == reflect.Int {
i, err := strconv.Atoi(value)
if err != nil {
return nil, "not numeric value"
}
r := reflect.ValueOf(i)
return &r, ""
}
I also want to consider the case where - andomitemptycan be specified in the tag.
Recommended Posts