Ceci et cela en utilisant Reflect

Objectif

Je vais le laisser comme un mémorandum parce que je n'ai pas la capacité de comprendre «refléter».

Utilisez Reflect pour remplir les champs de la structure

objectif

Je souhaite saisir une valeur spécifique pour une structure dont les éléments de champ sont inconnus.

go playground Ici

Traitement principal

Code correspondant

J'ai utilisé une fonction récursive.

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

Aperçu du traitement

Lorsque le type de reflect.Value est un pointeur

Avec ce processus, il est déterminé si le champ est un pointeur, et s'il est "nil", "reflct.Value" est généré. Si ce n'est pas «nil», utilisez le premier argument de la fonction récursive tel quel. Passez la référence du pointeur au premier argument de la fonction récursive et appelez-le à nouveau. Après l'appel, si «CanSet» est «true», définissez «Value» sur «Set» comme premier 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
	}
Lorsque le type de reflect.Value est une structure

Énumérez les champs de la structure, passez chaque champ comme premier argument de la fonction récursive et appelez-le à nouveau. Cependant, le cas de PkgPath (package) et le cas de Anonymous (type embarqué) sont exclus (il y a un cas de boucle permanente si non exclue).

	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
	}
Lorsque le type de reflect.Value est string, int

Dans le cas de "string", il est converti, et dans le cas de "int", il est "Set" with "reflect.ValueOf" tel quel. (Puisqu'il existe des cas autres que string et int, nous cherchons à savoir si cela peut être simplifié un peu plus ...)

	if rt == reflect.String {
		strV := strconv.Itoa(v)
		in.Set(reflect.ValueOf(strV))
		return
	}
	if rt == reflect.Int {
		in.Set(reflect.ValueOf(v))
		return
	}

Utilisez Reflect pour remplir les champs de structure de map et imprimer les données manquantes

objectif

Convertir de map [string] string en une structure arbitraire, et s'il n'y a pas assez d'éléments dans les champs de la structure, j'aimerais connaître les détails. Spécifiez une balise pour le champ de la structure et si le champ est du côté du pointeur, traitez-le comme facultatif. De plus, en faisant du premier argument du type bindParameters une interface, il correspond à n'importe quelle structure.

go playground Ici

Traitement principal

Code correspondant

Modifié en fonction de la fonction récursive de [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, ""
}

Aperçu du traitement

Analyse des balises de structure

Vous pouvez obtenir le nom de balise de la structure avec 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
	}
Extraire la valeur correspondant à la balise de map

S'il ne peut pas être extrait, «paramètre n'est pas spécifié», s'il ne peut pas être converti en «chaîne», «valeur non numérique» est remplacé par «retour». (Nous étudions un style d'écriture un peu plus intelligent, y compris comment gérer d'autres 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, ""
	}

Autre

Je veux également considérer le cas où «-» et «omitempty» peuvent être spécifiés dans la balise.

Recommended Posts

Ceci et cela en utilisant Reflect
Ceci et cela en utilisant NLTK (mémo)
matplotlib ceci et cela
Ceci et cela à propos de pd.DataFrame
API Zabbix ceci et cela
Ceci et cela appris de boost.python
Ceci et cela des propriétés python
Ceci et celui de la notation d'inclusion.
Ceci et cela utile lorsqu'il est utilisé avec nohup
Ceci et cela autour de Mysql dans l'environnement Apache (Note)
Lors de l'utilisation si et lors de l'utilisation pendant
Essayez d'utiliser pytest-Overview and Samples-
Authentification à l'aide de l'authentification des utilisateurs tweepy et de l'authentification d'application (Python)
Envoyer des messages et des images à l'aide de LineNotify
Précautions lors de l'utilisation de codecs et de pandas
Remarques sur l'utilisation de la post-réception et de la post-fusion
Zundokokiyoshi multithread utilisant l'événement et la file d'attente
Utilisation de sessions et de réflexions avec SQL Alchemy
Clustering et visualisation à l'aide de Python et CytoScape
La vie IoT facile et facile avec Micropython!
Backtrace en utilisant les informations DWARF et pyelftools