Very new to statically typed languages, so I have this complex struct I'm Unmarshalling
type MyStruc1 struct {
Property1 uint16 `json:property1`
Property2 struct {
Sub2Property1 string `json:sub2property1`
Sub2Property2 uint16 `json:sub2property2`
Sub2Property3 struct {
SubSub2Property1 string `json:subsub2property1`
SubSub2Property2 string `json:subsub2property1`
} `json:sub2property3`
} `json:property2`
Property3 struct {
Sub3Property1 string `json:sub3property1`
Sub3Property2 uint16 `json:sub3property2`
Sub3Property3 struct {
SubSub3Property1 string `json:subsub3property1`
SubSub3Property2 string `json:subsub3property1`
} `json:sub3peroperty3`
} `json:property3`
Property4 string `json:property4`
}
How do i write a function or struct method to accept any one of these arrays and return the value from MyStruct1? Is this possible?
strArray1 := []string{"Property2", "Sub2Property3", "SubSub2Property1"}
strArray2 := []string{"Property3", "Sub3Property1"}
strArray3 := []string{"Property4"}
strArray4 := []string{"Property1"}
Thank you ahead of time with any replies
You can with reflect
func FieldBySlice(strct interface{}, fields []string) interface{} {
val := reflect.ValueOf(strct)
for _, field := range fields {
val = val.FieldByName(field)
}
return val.Interface()
}
This code works, but you will use it only if very much necessary. It's ugly and not recommended. Try to think different, Go is not scripting, dynamic language
Related
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'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[:], " / ")
}
I want to unmarshal several types from JSON and use the interface to represent the actual struct that it is different. But when I send the struct as interface{} it converts it to a map. The animal.json is:
"{"type":"cat","policies":[{"name":"kitty","parameter":{"duration":600,"percent":90}}]}"
package main
import (
"reflect"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
func main() {
var err error
animal := New()
viper.SetConfigType("json")
viper.SetConfigName("animal")
viper.AddConfigPath("~/Desktop/")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
return
}
if err = viper.Unmarshal(&animal); err != nil {
return
}
for _, policy := range animal.Policies {
log.Info(policy.Name)
log.Info(policy.Parameter)
//INFO[0000] map[duration:600 percent:90]
log.Info(reflect.TypeOf(policy.Parameter))
//INFO[0000] map[string]interface {}, Why is it not an interface{} and how do I get it?
switch t := policy.Parameter.(type) {
//why does the switch not work?
case *CatParameter:
log.Info("cat", t)
case *DogParameter:
log.Info("dog", t)
}
}
}
func New() *Animal {
var animal Animal
animal.Type = "cat"
return &animal
}
type Animal struct {
Type string `json:"type" form:"type"`
Policies []Policy `json:"policies" form:"policies"`
}
type CatParameter struct {
Duration int `json:"duration"`
Percent int `json:"percent"`
}
type DogParameter struct {
Percent int `json:"percent"`
Duration int `json:"duration"`
Operation string `json:"operation"`
}
type Policy struct {
Name string `json:"name"`
Parameter interface{} `json:"parameter"`
}
It's json unmarshal feature
If you use an interface{} as a decoder, the default json object for interface{} is map[string]interface{}
You can see it here:
https://godoc.org/encoding/json#Unmarshal
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
So in t := policy.Parameter.(type), the t is map[string]interface{}
For solving your problem, you can try to define another field to distinguish CatParameter or DogParameter
Maybe:
type Policy struct {
Name string `json:"name"`
Parameter Parameter `json:"parameter"`
}
type Parameter struct {
Name string `json:"name"` // cat or dog
Percent int `json:"percent,omitempty"`
Duration int `json:"duration,omitempty"`
Operation string `json:"operation,omitempty"`
}
I have two structs in golang as below
type Data struct {
Name string
Description string
HasMore bool
}
type DataWithItems struct {
Name string
Description string
HasMore bool
Items []Items
}
At most DataWithItems struct can be rewritten as
type DataWithItems struct {
Info Data
Items []Items
}
But the above make it difficult when decoding a json object into DataWithItems. I know this can be solved with inheritance in other programming languages but Is there a way I can solve this in Go?
You can "embed" the one struct into the other:
type Items string
type Data struct {
Name string
Description string
HasMore bool
}
type DataWithItems struct {
Data // Notice that this is just the type name
Items []Items
}
func main() {
d := DataWithItems{}
d.Data.Name = "some-name"
d.Data.Description = "some-description"
d.Data.HasMore = true
d.Items = []Items{"some-item-1", "some-item-2"}
result, err := json.Marshal(d)
if err != nil {
panic(err)
}
println(string(result))
}
this prints
{"Name":"some-name","Description":"some-description","HasMore":true,"Items":["some-item-1","some-item-2"]}
Just use one struct - DataWithItems and sometimes leave items blank
Apologies for the title, but this is a weird one and out of my ability to understand.
I'm working with a go library that's sort of finished, but sort of not:
https://github.com/yfronto/go-statuspage-api
The statuspage.io API supports the following params when posting an incident:
incident[components][component_id] - Map of status changes to apply to affected components.
An example would be:
"incident[components][ftgks51sfs2d]=degraded_performance"
Unfortunately, the struct defined in the library doesn't support that particular field:
type NewIncidentUpdate struct {
Name string
Status string
Message string
WantsTwitterUpdate bool
ImpactOverride string
ComponentIDs []string
}
func (i *NewIncidentUpdate) String() string {
return encodeParams(map[string]interface{}{
"incident[name]": i.Name,
"incident[status]": i.Status,
"incident[message]": i.Message,
"incident[wants_twitter_update]": i.WantsTwitterUpdate,
"incident[impact_override]": i.ImpactOverride,
"incident[component_ids]": i.ComponentIDs,
})
}
How can I update this struct (and the associated encodeParams function) to support passing an arbitrary map of components and associated statuses?
You could embed a NewIncidentUpdate inside your own struct that defines the component status changes.
type MyIncidentUpdate struct {
NewIncidentUpdate
ComponentStatusChanges map[string]string
}
then define String the same way, while accomodating for your ComponentStatusChanges map.
func (i *MyIncidentUpdate) String() string {
params := map[string]interface{}{
"incident[name]": i.Name,
"incident[status]": i.Status,
"incident[message]": i.Message,
"incident[wants_twitter_update]": i.WantsTwitterUpdate,
"incident[impact_override]": i.ImpactOverride,
"incident[component_ids]": i.ComponentIDs,
}
for k, val := range i.ComponentStatusChanges {
key := fmt.Sprintf("incident[components][%s]", k)
params[key] = val
}
return encodeParams(params)
}