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
Related
I have the following struct YAML:
type YamlConfig struct {
Items struct {
RiskyRoles []struct {
Name string `yaml:"name"`
Rules []struct{
Verbs []string `yaml:"verbs"`
ResourceOperator string `yaml:"resourcesOperator"`
Resources []string `yaml:"resources"`
}
} `yaml:"RiskyRoles"`
} `yaml:"Items"`
}
I have a function that parse a YAML file to an object and then I want to send the Rules struct object to a function named DoingStuff(..):
yamlFile, err := ioutil.ReadFile("actionItems.yaml")
if err != nil {
fmt.Printf("Error reading YAML file: %s\n", err)
} else{
var yamlConfig YamlConfig
err = yaml.Unmarshal(yamlFile, &yamlConfig)
if err != nil {
fmt.Printf("Error parsing YAML file: %s\n", err)
}
for _, yamlRole := range yamlConfig.Items.RiskyRoles{
DoingStuff(yamlRole.Rules)
}
}
But inside the function DoingStuff, the struct object Rules is not recognized:
func DoingStuff(yamlRules []struct{}) {
// Not recognize ****
for _, rule := range yamlRules {
fmt.Print(rule.ResourceOperator)
}
}
How can I convert it to:
Rules []struct{
Verbs []string `yaml:"verbs"`
ResourceOperator string `yaml:"resourcesOperator"`
Resources []string `yaml:"resources"`
}
Should I re-declare this struct again?
Or cast using interfaces ?
EDIT:
I added new struct and used it inside the YamlConfig struct but the parse failed to parse the Rules:
type RulesStruct struct {
Rules []struct{
Verbs []string `yaml:"verbs"`
ResourceOperator string `yaml:"resourcesOperator"`
Resources []string `yaml:"resources"`
}
}
type YamlConfig struct {
Items struct {
RiskyRoles []struct {
Name string `yaml:"name"`
Message string `yaml:"message"`
Priority string `yaml:"priority"`
Rules []RulesStruct
} `yaml:"RiskyRoles"`
} `yaml:"Items"`
}
Thanks to #mkporiva help, I changed the structs like that:
type RulesStruct struct {
Verbs []string `yaml:"verbs"`
ResourceOperator string `yaml:"resourcesOperator"`
Resources []string `yaml:"resources"`
}
type YamlConfig struct {
Items struct {
RiskyRoles []struct {
Name string `yaml:"name"`
Message string `yaml:"message"`
Priority string `yaml:"priority"`
Rules []RulesStruct
} `yaml:"RiskyRoles"`
} `yaml:"Items"`
}
Now it works fine.
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 the following structs...
type Menu struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
Mixers []*Mixer `protobuf:"bytes,4,rep,name=mixers" json:"mixers,omitempty"`
Sections []*Section `protobuf:"bytes,5,rep,name=sections" json:"sections,omitempty"`
}
And...
type Menu struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Description string `json:"description" bson:"description"`
Mixers []Mixer `json:"mixers" bson:"mixers"`
Sections []Section `json:"sections" bson:"sections"`
}
I basically need to convert between the two struct types, I've attempted to use mergo, but that can only merge structs that are assignable to one another. The only solution I have so far is iterating through each struct, converting the ID by re-assigning it and converting its type between string and bson.ObjectId. Then iterating through each map field and doing the same. Which feels like an inefficient solution.
So I'm attempting to use reflection to be more generic in converting between the two ID's. But I can't figure out how I can effectively merge all of the other fields that do match automatically, so I can just worry about converting between the ID types.
Here's the code I have so far...
package main
import (
"fmt"
"reflect"
"gopkg.in/mgo.v2/bson"
)
type Sub struct {
Id bson.ObjectId
}
type PbSub struct {
Id string
}
type PbMenu struct {
Id string
Subs []PbSub
}
type Menu struct {
Id bson.ObjectId
Subs []Sub
}
func main() {
pbMenus := []*PbMenu{
&PbMenu{"1", []PbSub{PbSub{"1"}}},
&PbMenu{"2", []PbSub{PbSub{"1"}}},
&PbMenu{"3", []PbSub{PbSub{"1"}}},
}
newMenus := Serialise(pbMenus)
fmt.Println(newMenus)
}
type union struct {
PbMenu
Menu
}
func Serialise(menus []*PbMenu) []Menu {
newMenus := []Menu{}
for _, v := range menus {
m := reflect.TypeOf(*v)
fmt.Println(m)
length := m.NumField()
for i := 0; i < length; i++ {
field := reflect.TypeOf(v).Field(i)
fmt.Println(field.Type.Kind())
if field.Type.Kind() == reflect.Map {
fmt.Println("is map")
}
if field.Name == "Id" && field.Type.String() == "string" {
// Convert ID type
id := bson.ObjectId(v.Id)
var dst Menu
dst.Id = id
// Need to merge other matching struct fields
newMenus = append(newMenus, dst)
}
}
}
return newMenus
}
I'm can't just manually re-assign the fields because I'm hoping to detect maps on the structs fields and recursively perform this function on them, but the fields won't be the same on embedded structs.
Hope this makes sense!
I think that it is probably better to write your own converter, because you will always have some cases that are not covered by existing libs\tools for that.
My initial implementation of it would be something like this: basic impl of structs merger
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
I am new to golang I am trying to make a xml in which i am using nested tags
For that my code is
type MyXml struct {
XMLName xml.Name `xml:"myXml"`
Id int `xml:"id,attr"`
NewXml
}
type NewXml struct {
XMLName xml.Name `xml:"newXml"`
OneMoreXml
}
type OneMoreXml struct {
Msg interface{} `xml:"oneMore"`
}
type Child struct {
Param1 string `xml:"Param1"`
}
func main() {
baseXml := &Child{Param1: "Param1"}
retXml := GetXml(baseXml)
fmt.Println("my xml is", retXml)
}
func MarshallXml(reqString interface{}) (newXml string) {
xmlBody, err := xml.Marshal(reqString)
if err != nil {
fmt.Printf("error: %v\n", err)
}
newXml = string(xmlBody)
//fmt.Println(newXml)
return
}
func GetXml(baseXml interface{}) (finalXml string) {
startXml := new(MyXml)
startXml.Id = 1
startXml.Msg = baseXml
finalXml = MarshallXml(startXml)
return
}
but in my output xml Tag newXml is missing. I have tried it in various ways but some tag is always missing. I guess i am not understanding struct tag properly. So What i am doing wrong in above code and which basic concept of golang struct i am missing
I had a look at the xml package doc and they say that "an anonymous struct field is handled as if the fields of its value were part of the outer struct". In your case, all fields thus get serialized as if they were part of MyXml.
NewXml does not have any field (you just give it a name but there is nothing else) so nothing gets serialized for it.
If you add a new field to it, you can see that it is serialized.
type NewXml struct {
XMLName xml.Name `xml:"newXml"`
Test int
OneMoreXml
}
http://play.golang.org/p/vibSeQHTCr