i'm a Golang newbie trying to build a simple CLI. I'm converting an API call payload into an struct and want to format some information from this struct into a nice printable string. One of the informations i want to print is from an array of structs, like in this example:
type Pokemon struct {
Abilities []struct {
Ability struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"ability"`
IsHidden bool `json:"is_hidden"`
Slot int `json:"slot"`
} `json:"abilities"`
Height int `json:"height"`
ID int `json:"id"`
Name string `json:"name"`
Types []struct {
Slot int `json:"slot"`
Type struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"type"`
} `json:"types"`
Weight int `json:"weight"`
}
}
I'm trying to write a generic receptor function that iterates over some fields that are struct arrays and join its fields in a string. I can do a function that iterate specifically over some field like this:
func (p Pokemon) stringifyPokemonAbilities() string {
var listOfAbilities []string
for _, ability := range p.Abilities {
listOfAbilities = append(listOfAbilities, ability.Ability.Name)
}
return strings.Join(listOfAbilities[:], " / ")
}
Returning e.g. synchronize / inner-focus
Working like this, i'll have to write almost the same function to the Type field. My question is, how to make this function more generic, accepting a field and iterating on it. Any thoughts?
You could do that using relfect. There is a well written tutorial, which you could have a look at.
According to the struct you give, I have written a demo. The main idea is
find the struct filed by name, then iterator the slice, find the name in the struct.
You could get the answer using p.stringifyPokemon("Types") or p.stringifyPokemon("Abilities") now.
func (p Pokemon) stringifyPokemon(field string) string {
value := reflect.ValueOf(p)
struct1 := value.FieldByName(field)
if !struct1.IsValid() {
return ""
}
if struct1.Type().Kind() != reflect.Slice {
return ""
}
ans := make([]string, 0)
for i := 0; i < struct1.Len(); i++ {
slice1 := struct1.Index(i)
if slice1.Type().Kind() != reflect.Struct {
continue
}
for j := 0; j < slice1.NumField(); j++ {
struct2 := slice1.Field(j)
if struct2.Type().Kind() != reflect.Struct {
continue
}
name := struct2.FieldByName("Name")
if name.Kind() != reflect.String {
continue
}
ans = append(ans, name.String())
}
}
return strings.Join(ans[:], " / ")
}
Related
I'm trying to extract tags from a certain Value which is a struct. I am able to get the fields of the struct but unable to extract the tags. What am I doing wrong here? I've tried many different ways (using reflect.Type, interface{} etc) but all failed.
type House struct {
Room string
Humans Human
}
type Human struct {
Name string `anonymize:"true"` // Unable to get this tag
Body string
Tail string `anonymize:"true"` // Unable to get this tag
}
func printStructTags(f reflect.Value) { // f is of struct type `human`
for i := 0; i < f.NumField(); i++ {
fmt.Printf("Tags are %s\n", reflect.TypeOf(f).Field(i).Tag) // TAGS AREN'T PRINTED
}
}
The reason I use reflect.Value as the parameter is because this function is called from another function in the following manner
var payload interface{}
payload = &House{}
// Setup complete
v := reflect.ValueOf(payload).Elem()
for j := 0; j < v.NumField(); j++ { // Go through all fields of payload
f := v.Field(j)
if f.Kind().String() == "struct" {
printStructTags(f)
}
}
Any insights would be extremely valuable
When you call reflect.TypeOf(f) you get the type of f, which is already a reflect.Value.
Use the Type() func of this f to get the type and do the Field check on it:
func printStructTags(f reflect.Value) { // f is of struct type `human`
for i := 0; i < f.NumField(); i++ {
fmt.Printf("Tags are %s\n", f.Type().Field(i).Tag)
}
}
However, it's easier to check for a tag and get its value with f.Type().Field(i).Tag.Get("anonymize"). This way you directly get the assigned value, for example true in your case and an empty string if the tag doesn't exist.
I am new to GOlang, I trying to filter slice of struct in GO using struct which contain some parameter for filter it. I am trying to do below things but its not work for me. In that code I have filter function which take slice of struct i.e GRN which need to filter using FilterParameter struct.
My Struct which need to filer
type GRN struct {
DocType string `json:"docType"`
GRNNO string `json:"GRNNO"`
PRID string `json:"PRID"`
PRODUCE string `json:"PRODUCE"`
VARIETY string `json:"VARIETY"`
}
manyGRNUsers := []GRN{{"GRNAsset","GRN0000109","PRFAINAPKR0086","Sweetcorn","CP2"},
{"GRNAsset","GRN0000110","PRFAINAPKR0087","Sweetcorn","CP1",},
{"GRNAsset","GRN0000112","PRFAINAPKR0087", "Sweetcorn","CP2",},
{"GRNAsset","GRN0000113","PRFAINAPKR0089","Apple","Gala",}}
Struct which will used for filter parameter.here one thing the field in FilterParameter is dyanamic as above GRN struct value. it could me more then two field but less than or equal to GRN struct field
type FilterParameter struct {
PRODUCE string `json:"PRODUCE"`
VARIETY string `json:"VARIETY"`
}
manyFilterParameter := []FilterParameter{{"Sweetcorn","CP2" }
I tried below function but its not work for me as I can do only with one field from FilterParameter i.e PRODUCE only and its not dynamic
func filter(fu []GRN, su []FilterParameter) (out []GRN) {
f := make(map[string]struct{}, len(su))
for _, u := range su {
f[u.PRODUCE] = struct{}{}
}
for _, u := range fu {
if _, ok := f[u.PRODUCE]; ok {
out = append(out, u)
}
}
return
}
You can't really do what you're trying to do, as it's not possible to iterate the fields of a struct (maybe with reflection). For another approach, you could use maps instead of structs, or even easier, if you can just explicitly use the fields you care about in the function parameters:
package main
import "fmt"
type GRN struct { DocType, GRNNO, PRID, PRODUCE, VARIETY string }
func filter(in []GRN, produce, variety string) []GRN {
var out []GRN
for _, each := range in {
if each.PRODUCE == produce && each.VARIETY == variety {
out = append(out, each)
}
}
return out
}
func main() {
manyGRNUsers := []GRN{
{"GRNAsset","GRN0000109","PRFAINAPKR0086","Sweetcorn","CP2"},
{"GRNAsset","GRN0000110","PRFAINAPKR0087","Sweetcorn","CP1"},
{"GRNAsset","GRN0000112","PRFAINAPKR0087", "Sweetcorn","CP2"},
{"GRNAsset","GRN0000113","PRFAINAPKR0089","Apple","Gala"},
}
out := filter(manyGRNUsers, "Sweetcorn", "CP2")
fmt.Println(out)
}
I want to translate some fields of my Rest response from English to Hindi language. I have few translator files where I have mapping of words from English to Hindi. The name of the file I want to provide via field tags.
So my struct will look something like this
type myResponse struct {
City string `translatorFile:"CityEToH"`
State string `translatorFile:"StateEToH"`
StationCode []string `translatorFile:"StationCodeEToH"`
InsideStruct insideStruct
}
type insideStruct struct {
trainName string `translatorFile:"TrainEToH"`
StartingPoint string `translatorFile:"StationCodeEToH"`
FinishPoint string `translatorFile:"StationCodeEToH"`
}
I want to write a common translator method that will take interface{} as the input parameter and will return an interface (after converting the input) as output. I have just started to learn Go and I am stuck with the implementation. I am not able to create a map kind of structure that will map the fieldName to the corresponding translator file Name.
I have tried reflect.typeOf(input), but with this I am not able to get the tags of insideStruct. This is just an example structure of payload, I could have 4-5 inherited level of struct as well.
Is there a way to get the fieldName, tags and fieldValue together. Or is there any other better way to implement this ?
Here's a function that walks through values and calls a function for each string with the associated struct tag:
func walkStrings(v reflect.Value, tag reflect.StructTag, fn func(reflect.Value, reflect.StructTag)) {
v = reflect.Indirect(v)
switch v.Kind() {
case reflect.Struct:
t := v.Type()
for i := 0; i < t.NumField(); i++ {
walkStrings(v.Field(i), t.Field(i).Tag, fn)
}
case reflect.Slice, reflect.Array:
if v.Type().Elem().Kind() == reflect.String {
for i := 0; i < v.Len(); i++ {
walkStrings(v.Index(i), tag, fn)
}
}
case reflect.String:
fn(v, tag)
}
}
The function fn can use Value.String to get the value as a string and Value.SetString to change the value. Use StructTag.Get to get the tag. An example function is:
func translate(v reflect.Value, tag reflect.StructTag) {
if !v.CanSet() {
// unexported fields cannot be set
return
}
file := tag.Get("translatorFile")
if file == "" {
return
}
v.SetString(translatStringWithFile(v.String(), file)
}
Call walkStrings with a reflect.Value of a struct pointer and the empty string as the tag:
v := myResponse{
City: "mycity",
StationCode: []string{"code1", "code2"},
InsideStruct: insideStruct{"trainname", "start", "finish"},
}
walkStrings(reflect.ValueOf(&v), "", translate)
Run it on the playground.
type Teacher struct {
Name string `json:"name"`
Age int `json:"age"`
}
func getTag(i interface{}) string
getTag(teacher.Name) // print name
getTag(teacher.Age) // print age
I wanna roll my function like the code segment, but I can't find a way to achieve this. Any Ideas?
maybe this can help. https://play.golang.org/p/WtqdQAmsbi
x := struct {
Bar int `json:"bar"`
Foo string `json:"foo"`
}{2, "foo"}
v := reflect.ValueOf(x)
for i := 0; i < v.NumField(); i++ {
fmt.Println(v.Type().Field(i).Name)
fmt.Println(v.Type().Field(i).Tag)
}
To fetch the name of the struct field as well as its tag
I have the following code:
type DisplayObject struct {
ID string `json:"id,omitempty" bson:"id"`
URI string `json:"uri,omitempty" bson:"uri"`
Display string `json:"display,omitempty" bson:"display"`
}
if DisplayObject.ID != "" {
// do something
}
if DisplayObject.URI != "" {
// do something
}
if DisplayObject.Display != "" {
// do something
}
In javascript I would do
for (var key in DisplayObject) {
if (DisplayObject.hasOwnProperty(key)) {
// do something
}
}
How can I accomplish this for loop through an object in go?
You could use reflection to accomplish something like that:
package main
import (
"fmt"
"reflect"
)
type DisplayObject struct {
ID string `json:"id,omitempty" bson:"id"`
URI string `json:"uri,omitempty" bson:"uri"`
Display string `json:"display,omitempty" bson:"display"`
}
func main() {
displayObj := &DisplayObject{ID: "foo"}
s := reflect.ValueOf(displayObj).Elem()
for i := 0; i < s.NumField(); i++ {
fieldName := s.Type().Field(i).Name
fieldValue := s.Field(i).String()
fmt.Printf("%s: %s\n", fieldName, fieldValue)
// do something with the field data
}
}
You are trying compare uncomparable. Javascript object is similar to map[string]interface{} . In your case could also be map[string]string and for maps you can use len(m) == 0.
Struct is much faster container, but less flexible container. You cannot change number or types of members.