接口服务数据格式技巧

做~自己de王妃 提交于 2020-01-22 07:59:17

需求背景

  • 在同其他应用交互的过程中,我们常常需要提供以接口的方式,暴露应用数据或功能。在提供数据这块内容中,常常是需要约定双方的各自的数据格式要求,以满足通讯的需要
  • 现在大部分的数据格式为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格式
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!