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
, int
In 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
}
map
If 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 -
andomitempty
can be specified in the tag.
Recommended Posts