golang embedded structs
golang 中把struct 转成json格式输出 package main import ( "encoding/json" "fmt" ) type Person struct { Name string `json:"name,omitempty"` DoB string `json:"dob,omitempty"` Age string `json:"-,omitempty"` } type Singer struct { Person MusicGenre string `json:"music_genre,omitempty"` Age string `json:"ageee,omitempty"` } func ToJSON(s interface{}) (string,error) { bs,err := json.Marshal(s) if err != nil { return "",err } // fmt.Println(bs) return string(bs),nil } func main() { s := Singer{ Person{"John Singer","01-02-1975","13"},"pop","12" } sJSON,_ := ToJSON(s) fmt.Println(sJSON) // {"name":"John Singer","dob":"01-02-1975","music_genre":"pop"} }
? --------------------------------- One of the things that I missed the most,apart from generics,when coming to Go while having Java background was the lack of inheritance. In JVM it was pretty simple,as you can define a parent and child classes,then have a common behavior present all the children objects. This is not the case in Go,and I had to learn how to work around this. Here,we don‘t have inheritance,we have the composition,and for that,we have a pretty useful technique called embedding structs. Manual composition vs embeddingIn the classical approach to composition,we add what we‘d call a parent?class?as another field in the?child?one. In Go terms,we should first define a parent struct with common attributes: type Person struct { Name string } Then we should add an attribute of type? type Singer struct { Parent Person MusicGenre string } This may not seem like a huge improvement,but we can use a shorter notation and?embed type Singer struct { Person MusicGenre string } In both cases,when creating an instance of? // manual composition s0 := embedding.Singer{ Parent: embedding.Person{Name: "John Singer"},MusicGenre: "pop",} // embedding s1 := embedding.Singer{ Person: embedding.Person{Name: "John Singer"},} The difference is larger than just saving a few characters in the struct definition. First of all,when doing it manually we‘d have to use attribute name ( // manual composition fmt.Println(s0.Parent.Name) While with embedding we can (but don‘t have to) refer to the attributes of the embedded struct as if they were defined in the child: // embedding fmt.Println(s1.Parent.Name) fmt.Println(s1.Name) Another interesting thing is that if we want to build a? // manual composition s0 := embedding.Singer{MusicGenre: "pop"} s0.Parent.Name = "John Singer" // we have to do this explicitly fmt.Println(s0.Parent.Name) // embedding s1 := embedding.Singer{MusicGenre: "pop"} // we can be explicit with // s1.Person.Name = "John Doe" // but we can as well do this: s1.Name = "John Singer" fmt.Println(s1.Parent.Name) Calling inherited functionsAnother useful feature of embedding structs is that we can call parent‘s functions that were inherited as if they were defined on a child struct. For example,we can add a? type Person struct { ... } func (p Person) Talk(message string) { fmt.Printf("%s (a person) says "%s"n",p.Name,message) } func main() { s := Singer{} s.Name = "John Doe" s.Talk("Hello,reader!") // John Doe (a person) says "Hello,reader!" } Awesome! Remember that the attributes and function are?promoted?to the child struct only if they are not overwritten there. If we define a? ... func (p Singer) Talk(message string) { fmt.Printf("Singer %s says "%s"n",message) } func main() { s := Singer{} s.Name = "John Doe" s.Talk("Hello,again!") // Singer John Singer says "Hello again!" } The trick is when a function that is promoted calls another one. For example,if we define a? func (p Person) Type() string { return "PERSON" } func (p Person) Talk(message string) { fmt.Printf("%s (type=%s) says "%s"n",p.Type(),message) } ... func (s Singer) Type() string { return "SINGER" } func (s Singer) Sing(title string) { fmt.Printf("%s (type=%s) sings %s in the style of %s.n",s.Name,s.Type(),title,s.MusicGenre) } ... func main() { s := Singer{MusicGenre: "rock"} s.Name = "Johny Singerra" s.Sing("Welcome to the forest") // Johny Singerra (type=SINGER) sings Welcome to the forest in the style of rock. s.Talk("Hello!") // Johny Singerra (type=PERSON) says "Hello!" } Hiding JSON propertiesAnother interesting fact is the way Go handler JSON tags that can identify attributes of a struct. For example,we can marshal? type Person struct { Name string `json:"name,omitempty"` DoB string `json:"dob,omitempty"` } ... type Singer struct { Person MusicGenre string `json:"music_genre,omitempty"` } ... func (s Singer) ToJSON() (string,error) { bs,err := json.Marshal(s) if err != nil { return "",err } return string(bs),nil } ... func main() { s := embedding.Singer{ Person: embedding.Person{ Name: "John Singer",DoB: "01-02-1975",},} sJSON,_ := s.ToJSON() fmt.Println(sJSON) // {"name":"John Singer","music_genre":"pop"} } It‘s great that both? type MusicStar struct { Singer Nickname string `json:"nickname,omitempty"` DoB string `json:"-,omitempty"` } func (ms MusicStar) ToJSON() (string,err := json.Marshal(ms) if err != nil { return "",nil } Note that we‘ve added a DoB field but added a? func main() { ms := embedding.MusicStar{ Nickname: "Starry",Singer: embedding.Singer{ Person: embedding.Person{ Name: "Joe Star",} msJSON,_ := ms.ToJSON() fmt.Println(msJSON) // "name":"Joe Star","music_genre":"pop","nickname":"Starry"} } That is because although Go sees our? type MusicStar struct { Singer Nickname string `json:"nickname,omitempty"` DoB string `json:"dob,omitempty"` } ... msJSON,_ := ms.ToJSON() fmt.Println(msJSON) // {"name":"Joe Star","nickname":"Starry"} As you can see,embedded structs solve some of the things we would achieve via classical inheritance,but it‘s necessary to understand how it works to avoid unexpected behaviors and gotchas. The full source code of these examples is available?on Github. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |