This and that using reflect

Purpose

I will leave it as a memorandum because I lack understanding of reflect.

Use reflect to populate the fields of the structure

goal

I want to enter a specific value for a structure whose field elements are unknown.

go playground Here

Main processing

Corresponding code

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
	}
}

Outline of processing

When the type of reflect.Value is a pointer

With 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
	}
When the type of reflect.Value is a structure

Enumerate 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
	}
When the type of 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
	}

Use reflect to populate the structure field from map and print out any missing data

goal

Convert 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

Main processing

Corresponding code

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, ""
}

Outline of processing

Structure tag analysis

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
	}
Extract the value corresponding to the tag from 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, ""
	}

Other

I also want to consider the case where - andomitemptycan be specified in the tag.

Recommended Posts

This and that using reflect
This and that using NLTK (memo)
matplotlib this and that
This and that about pd.DataFrame
Zabbix API this and that
This and that learned from boost.python
This and that of python properties
This and that for using Step Functions with CDK + Python
This and that of the inclusion notation.
This and that useful when used with nohup
This and that around Mysql in Apache environment (Note)
When using if and when using while
Try using pytest-Overview and Samples-
[Notes / Updated from time to time] This and that of Azure Functions
Authentication using tweepy-User authentication and application authentication (Python)
Send messages and images using LineNotify
Precautions when using codecs and pandas
Notes on using post-receive and post-merge
Multi-threaded Zundokokiyoshi using Event and Queue
Using Sessions and Reflections with SQLAlchemy
Clustering and visualization using Python and CytoScape
Easy and easy IoT life using Micropython!
Backtrace using DWARF information and pyelftools