需求背景
- 在同其他应用交互的过程中,我们常常需要提供以接口的方式,暴露应用数据或功能。在提供数据这块内容中,常常是需要约定双方的各自的数据格式要求,以满足通讯的需要
- 现在大部分的数据格式为json格式,在go语言中,json也得到了很好的支持,可以很方便的将结构体转换为json数据
- 在这篇文章中,我简要地演示下针对特定的数据格式,我们在go语言中如何去设计相关的结构体,从而方便我们的编程,更清晰地写出较强逻辑性的代码
演示
数据要求
- 假设我们需要提供的数据格式内容如下:
[
{
"name": "offline",
"servity": [
{
"level": "3级",
"count": 0
},
{
"level": "4级",
"count": 0
},
{
"level": "5级",
"count": 4
}
]
},
{
"name": "SQL",
"servity": [
{
"level": "3级",
"count": 0
},
{
"level": "4级",
"count": 0
},
{
"level": "5级",
"count": 4
}
]
},
...
]
数据格式分析
- 分析这个数据格式,这是一个列表,里面包含了多个元素,每个元素为map类型,在键值为“servity”中,又包含了一个列表,在该列表中又是嵌套了一个map类型
结构体设计
- 说到嵌套,这个是不是跟go语言的结构体嵌套不谋而合了呢?所以在设计该结构体时,我们也同样使用结构体嵌套的方式来设计
type AlertSubType struct {
Name string
ServeritySlice [3]*Serverity
}
type Serverity struct {
Level string
Num int
}
- 为了方便生成一个实例,设计一个New函数,用于生成在数据格式中列表的一个元素
func NewAlertSubType(name string, Level ...int) (ret *AlertSubType) {
ServeritySlice := [3]*Serverity{{Level: "3级", Num: Level[0]}, {Level: "4级", Num: Level[1]}, {Level: "5级", Num: Level[2]}}
return &AlertSubType{
Name: name,
ServeritySlice: ServeritySlice,
}
}
结果验证
- 我们来测试一下这个元素是否满足我们的格式需求
a1 := NewAlertSubType("test", 3, 4, 5)
ret, _ := json.Marshal(a1)
fmt.Println(string(ret))
- 结果如下:
{
"Name": "test",
"ServeritySlice": [
{
"Level": "3级",
"Num": 3
},
{
"Level": "4级",
"Num": 4
},
{
"Level": "5级",
"Num": 5
}
]
}
遇到问题
显然,字段名称跟原格式不一致,因为在go里面,我们设置字段名称为大写是为了方便在包外进行访问调用,类似java的公有字段,在这里我们需要对json进行字段的重定义,在go语言中非常方便,只需要在声明结构体后面添加需要转义的内容即可,如下所示:
type AlertSubType struct {
Name string `json:"name"`
ServeritySlice [3]*Serverity `json:"servity"`
}
type Serverity struct {
Level string `json:"level"`
Num int `json:"count"`
}
- 再次运行程序
{
"name": "test",
"servity": [
{
"level": "3级",
"count": 3
},
{
"level": "4级",
"count": 4
},
{
"level": "5级",
"count": 5
}
]
}
- 已经满足了我们要求的输出的格式里面,一个元素的需求了,接下来就将这些元素封装进列表
[
{
"name": "test1",
"servity": [
{
"level": "3级",
"count": 3
},
{
"level": "4级",
"count": 4
},
{
"level": "5级",
"count": 5
}
]
},
{
"name": "test2",
"servity": [
{
"level": "3级",
"count": 3
},
{
"level": "4级",
"count": 4
},
{
"level": "5级",
"count": 5
}
]
},
{
"name": "test3",
"servity": [
{
"level": "3级",
"count": 3
},
{
"level": "4级",
"count": 4
},
{
"level": "5级",
"count": 5
}
]
},
{
"name": "test4",
"servity": [
{
"level": "3级",
"count": 3
},
{
"level": "4级",
"count": 4
},
{
"level": "5级",
"count": 5
}
]
}
]
- 可以看出,我们的数据已经符合约定的格式,就可以愉快地跟对接厂商玩耍了
将json转换为结构体
- 假设对方接口的请求返回得到的是如下的json数据
[
{
"Containers": -1,
"Created": 1578614289,
"Id": "sha256:26a593639481a26d04bea1b8b3512491f3cad0197cde647165c1238a7a808b33",
"Labels": null,
"ParentId": "sha256:aed5cfe9b45b3d4dca7feeecf6527ef26f1320e8df3a48eb450e72d3e45b5aaa",
"RepoDigests": null,
"RepoTags": [
"multi:stage"
],
"SharedSize": -1,
"Size": 903258166,
"VirtualSize": 903258166
},
{
"Containers": -1,
"Created": 1578554633,
"Id": "sha256:076e1bff9a55c22082a7348e359350f7ea756c7e6cfe14ebfd7ddf1da6b21856",
"Labels": null,
"ParentId": "sha256:456aa8589c8ec4e0add1135c32c8dd96f6b5c584c18447e7a923accb89a7a00b",
"RepoDigests": [
"<none>@<none>"
],
"RepoTags": [
"<none>:<none>"
],
"SharedSize": -1,
"Size": 903258166,
"VirtualSize": 903258166
},
...
]
数据格式分析
- 从请求返回的数据信息来看,这是一个列表,包含了多个元素,同样的思路,我们把这个列表的元素提取一些我们关注的字段,作为我们设计的一个结构体。设计的结构体,并且为了方便看输出,定义了一个结构体方法,代码如下:
type DockerImages struct {
Containers int
Created int
Id string
Size int
RepoTags []string
}
func (this *DockerImages) DisplayInfo() {
fmt.Printf("Containers:%d Created:%d Id:%s Size:%d RepoTages:%s\n", this.Containers, this.Created, this.Id, this.Size, this.RepoTags[0])
}
请求接口数据
ret, _ := http.Get("http://192.168.55.128:2376/images/json")
defer ret.Body.Close()
body, _ := ioutil.ReadAll(ret.Body)
定义一个结构体数组
var images []*models.DockerImages
使用json提供的Unmarshal方法进行解析
json.Unmarshal(body, &images)
验证结果
for k, _ := range images {
images[k].DisplayInfo()
}
- 输出如下:
Containers:-1 Created:1578614289 Id:sha256:26a593639481a26d04bea1b8b3512491f3cad0197cde647165c1238a7a808b33 Size:903258166 RepoTages:multi:stage
Containers:-1 Created:1578554633 Id:sha256:076e1bff9a55c22082a7348e359350f7ea756c7e6cfe14ebfd7ddf1da6b21856 Size:903258166 RepoTages:<none>:<none>
Containers:-1 Created:1578553962 Id:sha256:a854fa05e36c5971b855c8618bc2fa1cdd7a97f4b93d0cded4b0d0264b05c937 Size:903258166 RepoTages:<none>:<none>
小结
- 在设计时,json的列表对应go的列表,map对应map,按照数据格式进行相应的嵌套,这样很容易设计出跟对接数据一致的格式
- 为了方便,最好将一个元素的生成封装成一个方法,因为在多嵌套的情况下,生成一个实例会比较麻烦,另外可以在该方法中设置有些属性的默认值
- go提供的json包中,太智能了,无论是从数据的封装还是解析到结构体,都非常方便
思考
- 在示例中,我们的结构体跟数据请求的数据基本是一致的,包括数据内容和最终格式,假如我们对接收到的数据需要进一步进行修改,那么我们该如何去解析并生成结构体呢?比如接收到的json数据中,得到的是一个unixtimestamp,想要转换为date格式
来源:CSDN
作者:xiaojinran
链接:https://blog.csdn.net/xiaojinran/article/details/103850167