普通结构体
1.public
type Flower struct {
Name string
Color string
Category string
}
外部包可以直接引用和赋值
2.private
type leaf struct {
count int
size int
}
外部包不能直接引用
需要定义public函数提供操作,例如
func (l *leaf) SetColor(color string) {
l.color = color
}
3.public结构体里面有private
type Flower struct {
name string
color string
category string
}
这种情况下,只能通过public的函数或者方法来操作内部的private属性
4.private结构体里面有public属性
type leaf struct {
color string
size int
Count int
}
这种也是允许的,例如:
func NewLeaf(color string, size, count int) *leaf{
var l = leaf{color, size, count}
return &l
}
在另一个包中引用这些结构体
func main() {
leaf := plant.NewLeaf("green", 5, 10)
fmt.Println(leaf)
leaf.Count = 5
fmt.Println(leaf)
}
输出:
&{green 5 10}
&{green 5 5}
很少看到这么使用
嵌套结构体
type Leaf struct {
Color string
Size int
Count int
}
type Flower struct {
Leaf Leaf
Name string
Color string
Category string
}
访问方式:
func main() {
f := plant.Flower{}
f.Category = "Roseceae"
f.Name = "Rose"
f.Color = "Red"
f.Leaf.Color = "green"
f.Leaf.Count = 5
f.Leaf.Size = 10
fmt.Println(f)
}
这种嵌套,在访问内部结构的成员时,必须显示的指向,如:
f.Leaf.Color
嵌套匿名结构体
type Leaf struct {
Color string
Size int
Count int
}
type Flower struct {
Leaf
Name string
Color string
Category string
}
访问方式:
func main() {
f := plant.Flower{}
f.Category = "Roseceae"
f.Name = "Rose"
f.Color = "Red"
f.Count = 5
f.Size = 10
f.Leaf.Color = "green"
fmt.Println(f)
}
如上可以看出,有两种访问方式:
a.直接由外部结构体访问内部结构体成员,跟访问外部结构体内部自己的成员一样
f.Count = 5
f.Size = 10
b.外部结构体指向内部结构体,再访问其内部成员
f.Leaf.Color = "green"
注意:在内外结构体有成员名称相同时,如果访问嵌套结构体的成员,必须按照b方式访问,否则是直接访问的外层结构体成员变量。
补充:
这种匿名嵌套的结构体初始化的时候,必须一层层初始化,例如:
f := &Flower{
Name:"Rose",
Color:"Red",
Category:"Roseceae",
Size:10,
Count:5,
}
这种初始化会报错,找不到成员Size和Count
正确的初始化方法:
l := Leaf{
Size: 10,
Count: 5,
Color: "Green",
}
f := &Flower{
Name: "Rose",
Color: "Red",
Category: "Roseceae",
Leaf: l,
}
或者:
f := &Flower{
Name: "Rose",
Color: "Red",
Category: "Roseceae",
Leaf: Leaf{
Size: 10,
Count: 5,
Color: "Green",
},
}
结构体中嵌套接口
以golang内置的包context为例
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
cancel(removeFromParent bool, err error)
Done() <-chan struct{}
}
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done chan struct{} // created lazily, closed by first cancel call
children map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
这种嵌套,可以当成继承来理解,
首先Context接口有四个方法,全部被cancelCtx这个结构体继承了,然后重写了其中三个方法
func (c *cancelCtx) Value(key interface{}) interface{} {
if key == &cancelCtxKey {
return c
}
return c.Context.Value(key)
}
func (c *cancelCtx) Done() <-chan struct{} {
c.mu.Lock()
if c.done == nil {
c.done = make(chan struct{})
}
d := c.done
c.mu.Unlock()
return d
}
func (c *cancelCtx) Err() error {
c.mu.Lock()
err := c.err
c.mu.Unlock()
return err
}
同时,cancelCtx又实现了canceler这个接口定义的方法,所以cancelCtx又可以当成canceler接口用:
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
//具体实现见源码
}
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{Context: parent}
}
// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
//具体实现见源码
}
从WithCancel中可以看到,执行c := newCancelCtx(parent)之后,c其实是cancelCtx类型,再调用propagateCancel时,第二个参数是canceler接口类型,直接取c地址传入,然后&c就当canceler接口用了,最后return时,又将&c当Context接口返回了
Context包代码不多,可以自己研究源码,认识会更深刻。
json结构体
JSON:
Json结构体定义的时候,成员名称和结构体名称必须可导出,即必须以大写开头,如:
type Os struct {
Arch string
Major string
Minor string
Name string
Platform string
Version string
}
但是,这样在序列化成json字符串的时候,会以成员名称作为json字符串中的名称:
func main() {
linux := Os{
Arch: "x86_64",
Major: "7",
Minor: "7",
Name: "CentOS Linux",
Version: "7.7",
}
js, err := json.Marshal(linux)
if err != nil {
return
}
fmt.Println(string(js))
}
输出:
{"Arch":"x86_64","Major":"7","Minor":"7","Name":"CentOS Linux","Platform":"","Version":"7.7"}
有些情况下,我们的json字符串想全部以小写开头,则可以用如下方法指定输出时的字符串名称:
type Os struct {
Arch string `json:"arch"`
Major string `json:"major"`
Minor string `json:"minor"`
Name string `json:"name"`
Platform string `json:"platform"`
Version string `json:"version"`
}
可以直接用内置的接口进行序列化处理:
func main() {
linux := Os{
Arch: "x86_64",
Major: "7",
Minor: "7",
Name: "CentOS Linux",
Platform: "centos",
Version: "7.7",
}
js, err := json.Marshal(linux)
if err != nil {
return
}
fmt.Println(string(js))
}
输出:
{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","platform":"centos","version":"7.7"}
如果某个字段没有赋值,例如Platform:
func main() {
linux := Os{
Arch: "x86_64",
Major: "7",
Minor: "7",
Name: "CentOS Linux",
Version: "7.7",
}
js, err := json.Marshal(linux)
if err != nil {
return
}
fmt.Println(string(js))
}
输出:
{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","platform":"","version":"7.7"}
可以看到json字符串中仍然生成了platform这个字段,如果想对于某些不赋值的字段在生成json串的时候忽略掉,则可以在定义结构体的时候,使用omitempty,定义如下:
type Os struct {
Arch string `json:"arch"`
Major string `json:"major"`
Minor string `json:"minor"`
Name string `json:"name"`
Platform string `json:"platform,omitempty"`
Version string `json:"version"`
}
func main() {
linux := Os{
Arch: "x86_64",
Major: "7",
Minor: "7",
Name: "CentOS Linux",
Version: "7.7",
}
js, err := json.Marshal(linux)
if err != nil {
return
}
fmt.Println(string(js))
}
输出:
{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","version":"7.7"}
注意注意注意:
`json:"platform,omitempty"`
这个字符串中不能包含空格,否则无效
例如结构体中定义为:
Platform string `json: "platform,omitempty"`
这种json:后面跟空格,则不能指定json序列化时候显示的成员名称,默认是结构体成员名,即Platform
{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","Platform":"","version":"7.7"}
xml结构体
1.普通XML
type logcollect struct {
XMLName xml.Name `xml:"localfile"`
Logformat string `xml:"log_format"`
Location string `xml:"location"`
}
func main() {
lc := logcollect{Logformat: "syslog", Location: "/home/log/*.log"}
output, err := xml.MarshalIndent(lc, "", " ")
if err != nil {
return
}
fmt.Println(string(output))
}
输出
<localfile>
<log_format>syslog</log_format>
<location>/home/log/*.log</location>
</localfile>
2.嵌套xml
如果log_format或者location并不是简单的string类型,或者带一些属性,可以单独定义,如下:
type logformat struct {
XMLName xml.Name `xml:"log_format"`
Id int `xml:"id,attr"`
Log_format string `xml:",chardata"`
}
type location struct {
XMLName xml.Name `xml:"location"`
Length int `xml:"length,attr"`
Location string `xml:",chardata"`
}
type logcollect struct {
XMLName xml.Name `xml:"localfile"`
Logformat logformat
Location location
}
序列化:
func main() {
lf := logformat{Log_format: "syslog", Id: 1000}
loc := location{Location: "/home/log/*.log", Length: 64}
lc := logcollect{Logformat: lf, Location: loc}
output, err := xml.MarshalIndent(lc, "", " ")
if err != nil {
return
}
fmt.Println(string(output))
}
输出
<localfile>
<log_format id="1000">syslog</log_format>
<location length="64">/home/log/*.log</location>
</localfile>
3.动态设置XML的标签名
如上面定义的xml标签名字,都是在XMLName后面用xml:
方式来指定的,如果想要程序中动态指定,
可以用如下方式设置,以logformat的标签名为例:
type logformat struct {
XMLName xml.Name
Id int `xml:"id,attr"`
Log_format string `xml:",chardata"`
}
type location struct {
XMLName xml.Name `xml:"location"`
Length int `xml:"length,attr"`
Location string `xml:",chardata"`
}
type logcollect struct {
XMLName xml.Name `xml:"localfile"`
Logformat logformat
Location location
}
序列化:
func main() {
lf := logformat{XMLName: xml.Name{Local:"LOGFORMAT"}, Log_format: "syslog", Id: 1000}
loc := location{Location: "/home/log/*.log", Length: 64}
lc := logcollect{Logformat: lf, Location: loc}
output, err := xml.MarshalIndent(lc, "", " ")
if err != nil {
return
}
fmt.Println(string(output))
}
输出:
<localfile>
<LOGFORMAT id="1000">syslog</LOGFORMAT>
<location length="64">/home/log/*.log</location>
</localfile>
可以看到标签名为LOGFORMAT了
注意:如果结构体定义中未指定标签名,代码中也没有赋值,则会以结构体名称作为标签名,同时首字母大写
4.动态设置XML的属性名
同样的也可以动态修改属性名称,如果属性在定义结构体的时候不确定,可以用如下方式定义:
type logformat struct {
XMLName xml.Name `xml:"log_format"`
XMLAttr xml.Attr `xml:"id,attr"`
Log_format string `xml:",chardata"`
}
type location struct {
XMLName xml.Name `xml:"location"`
Length int `xml:"length,attr"`
Location string `xml:",chardata"`
}
type logcollect struct {
XMLName xml.Name `xml:"localfile"`
Logformat logformat
Location location
}
func main() {
lf := logformat{
Log_format: "syslog",
XMLAttr:
xml.Attr{
Name:xml.Name{
Local:"ID",
},
Value:"100",
},
}
loc := location{Location: "/home/log/*.log", Length: 64}
lc := logcollect{Logformat: lf, Location: loc}
output, err := xml.MarshalIndent(lc, "", " ")
if err != nil {
return
}
fmt.Println(string(output))
}
输出:
<localfile>
<log_format ID="100">syslog</log_format>
<location length="64">/home/log/*.log</location>
</localfile>
如果有多个属性,可以将其定义为数组
type logformat struct {
XMLName xml.Name `xml:"log_format"`
XMLAttr []xml.Attr `xml:"id,attr"`
Log_format string `xml:",chardata"`
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。