myJsonMarshal
二、系统环境&项目介绍
1.系统环境
操作系统:CentOS7
硬件信息:使用virtual box配置虚拟机(内存3G、磁盘30G)
编程语言:GO 1.15.2
2.项目的任务要求
func JsonMarshal(v interface{}) ([]byte, error)mytag:"你自己的定义"
1.程序设计
reflectmyJsonMarshalmyJsonMarshal
2.JsonMarshal函数
//JsonMarshal 输入结构化数据,返回json字符流和error
func JsonMarshal(v interface{}) ([]byte, error) {
b, err := Marshal(v)
if err != nil {
return nil, err
}
return b, nil
}
JsonMarshal函数是本程序包的主要接口,通过输入结构化数据,可以返回json字符流和报错信息error。
3.Marshal函数
//Marshal 将接口数据类型的数据转为json字符流
func Marshal(v interface{}) ([]byte, error) {
json := reflect.ValueOf(v)
typeJson := json.Type()
switch typeJson.Kind() {
case reflect.Invalid:
return []byte("Invalid"), errors.New("Invalid")
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64, reflect.Float32, reflect.Float64:
return []byte(fmt.Sprintf("%v", json.Interface())), nil
case reflect.String:
return StringTrans(v)
case reflect.Struct:
return StructTrans(v)
case reflect.Map:
return MapTrans(v)
case reflect.Slice:
return SliceTrans(v)
case reflect.Array:
return ArrayTrans(v)
case reflect.Ptr:
return PtrTrans(v)
default:
return []byte("unsupportedTypeTrans"), errors.New("unsupportedTypeTrans")
}
}
reflect
4.StringTrans函数
//StringTrans 将string数据转为json字符流
func StringTrans(v interface{}) ([]byte, error) {
json := reflect.ValueOf(v)
return []byte("\"" + json.String() + "\""), nil
}
StringTrans函数将string数据转为json字符流
5.StructTrans函数
//StructTrans 将struct数据转为json字符流
func StructTrans(v interface{}) ([]byte, error) {
json := reflect.ValueOf(v)
typeJson := json.Type()
b := "{"
for i := 0; i < json.NumField(); i++ {
if i > 0 {
b = b + ","
}
tag := typeJson.Field(i).Tag.Get("mytag")
if tag == "" {
b = b + "\"" + typeJson.Field(i).Name + "\":"
} else {
b = b + "\"" + tag + "\":"
}
tmp, err := Marshal(json.Field(i).Interface())
if err != nil {
return nil, err
}
b = b + string(tmp)
}
b = b + "}"
return []byte(b), nil
}
mytag:"你自己的定义"mytag:"name"
6.MapTrans函数
//MapTrans 将map数据转为json字符流
func MapTrans(v interface{}) ([]byte, error) {
json := reflect.ValueOf(v)
i := json.MapRange()
first := true
b := "{"
for i.Next() {
if first {
first = false
} else {
b = b + ","
}
b = b + "\"" + fmt.Sprintf("\"%v\":", i.Key()) + "\":"
tmp, err := Marshal(i.Value().Interface())
if err != nil {
return nil, err
}
b = b + string(tmp)
}
b = b + "}"
return []byte(b), nil
}
MapTrans函数将map数据转为json字符流
7.SliceTrans函数
//SliceTrans 将slice数据转为json字符流
func SliceTrans(v interface{}) ([]byte, error) {
json := reflect.ValueOf(v)
b := "["
for i := 0; i < json.Len(); i++ {
if i > 0 {
b = b + ","
}
tmp, err := Marshal(json.Index(i).Interface())
if err != nil {
return nil, err
}
b = b + string(tmp)
}
b = b + "]"
return []byte(b), nil
}
SliceTrans函数将slice数据转为json字符流
8.ArrayTrans函数
//ArrayTrans 将array数据转为json字符流
func ArrayTrans(v interface{}) ([]byte, error) {
json := reflect.ValueOf(v)
b := "["
for i := 0; i < json.Len(); i++ {
if i > 0 {
b = b + ","
}
tmp, err := Marshal(json.Index(i).Interface())
if err != nil {
return nil, err
}
b = b + string(tmp)
}
b = b + "]"
return []byte(b), nil
}
ArrayTrans函数将array数据转为json字符流
9.PtrTrans函数
//PtrTrans 将ptr数据转为json字符流
func PtrTrans(v interface{}) ([]byte, error) {
json := reflect.ValueOf(v)
return Marshal(json.Elem().Interface())
}
StringTrans函数将ptr数据转为json字符流,指针变量编码时自动转换为它所指向的值。
1.封装并使用程序包
将项目myJsonMarshal的myJsonMarshal.go文件的main函数注释掉,package改为package myJsonMarshal,然后执行如下指令:
go build
在其他路径下建立main.go,并调用myJsonMarshal.JsonMarshal函数即可。
2.功能测试
功能测试主要从用户角度测试程序包的功能,步骤如下:
创建main.go文件,内容如下(结构化数据类型及其数值可自定义):
package main
import (
"fmt"
"github.com/user/myJsonMarshal"
)
type Stu struct {
Name string `mytag:"name"`
Age int
HIgh bool
Class *Class `mytag:"class"`
}
type Class struct {
Name string
Grade int
}
func main() {
//实例化一个数据结构,用于生成json字符串
stu := Stu{
Name: "张三",
Age: 18,
HIgh: true,
}
//指针变量
cla := new(Class)
cla.Name = "1班"
cla.Grade = 3
stu.Class = cla
//Marshal失败时err!=nil
jsonStu, err := myJsonMarshal.JsonMarshal(stu)
if err != nil {
fmt.Println("生成json字符串错误")
}
//jsonStu是[]byte类型,转化成string类型便于查看
fmt.Println(string(jsonStu))
}
mytag:"你自己的定义"
3.单元测试
单元测试主要从程序员角度,对程序包的具体函数进行测试。
建立myJsonMarshal_test.go文件,对程序包的每个函数进行单元测试如下,代码如下:
package myJsonMarshal
import (
"fmt"
"testing"
)
type Stu struct {
Name string `mytag:"name"`
Age int
HIgh bool
Class *Class `mytag:"class"`
}
type Class struct {
Name string
Grade int
}
func Test_JsonMarshal(t *testing.T) {
//实例化一个数据结构,用于生成json字符串
stu := Stu{
Name: "张三",
Age: 18,
HIgh: true,
}
//指针变量
cla := new(Class)
cla.Name = "1班"
cla.Grade = 3
stu.Class = cla
//Marshal失败时err!=nil
jsonStu, err := JsonMarshal(stu)
if err != nil {
fmt.Println("生成json字符串错误")
}
want := "{\"name\":\"张三\",\"Age\":18,\"HIgh\":true,\"class\":{\"Name\":\"1班\",\"Grade\":3}}"
got := string(jsonStu)
if got != want {
t.Errorf("\n got %s\n want %s\n", got, want)
}
}
func Test_Marshal(t *testing.T) {
//实例化一个数据结构,用于生成json字符串
stu := Stu{
Name: "张三",
Age: 18,
HIgh: true,
}
//指针变量
cla := new(Class)
cla.Name = "1班"
cla.Grade = 3
stu.Class = cla
//Marshal失败时err!=nil
jsonStu, err := Marshal(stu)
if err != nil {
fmt.Println("生成json字符串错误")
}
want := "{\"name\":\"张三\",\"Age\":18,\"HIgh\":true,\"class\":{\"Name\":\"1班\",\"Grade\":3}}"
got := string(jsonStu)
if got != want {
t.Errorf("\n got %s\n want %s\n", got, want)
}
}
func Test_StringTrans(t *testing.T) {
//实例化一个数据结构,用于生成json字符串
test := "unit test"
//Marshal失败时err!=nil
jsonStu, err := StringTrans(test)
if err != nil {
fmt.Println("生成json字符串错误")
}
want := "\"unit test\""
got := string(jsonStu)
if got != want {
t.Errorf("\n got %s\n want %s\n", got, want)
}
}
func Test_StructTrans(t *testing.T) {
//实例化一个数据结构,用于生成json字符串
test := Class{
Name: "1班",
Grade: 3,
}
//Marshal失败时err!=nil
jsonStu, err := StructTrans(test)
if err != nil {
fmt.Println("生成json字符串错误")
}
want := "{\"Name\":\"1班\",\"Grade\":3}"
got := string(jsonStu)
if got != want {
t.Errorf("\n got %s\n want %s\n", got, want)
}
}
func Test_MapTrans(t *testing.T) {
//实例化一个数据结构,用于生成json字符串
test := map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
//Marshal失败时err!=nil
jsonStu, err := MapTrans(test)
if err != nil {
fmt.Println("生成json字符串错误")
}
want := "{\"\"one\":\":1,\"\"two\":\":2,\"\"three\":\":3}"
got := string(jsonStu)
if got != want {
t.Errorf("\n got %s\n want %s\n", got, want)
}
}
func Test_SliceTrans(t *testing.T) {
//实例化一个数据结构,用于生成json字符串
test := []int{1, 2, 3}
//Marshal失败时err!=nil
jsonStu, err := SliceTrans(test)
if err != nil {
fmt.Println("生成json字符串错误")
}
want := "[1,2,3]"
got := string(jsonStu)
if got != want {
t.Errorf("\n got %s\n want %s\n", got, want)
}
}
func Test_ArrayTrans(t *testing.T) {
//实例化一个数据结构,用于生成json字符串
test := [3]int{1, 2, 3}
//Marshal失败时err!=nil
jsonStu, err := ArrayTrans(test)
if err != nil {
fmt.Println("生成json字符串错误")
}
want := "[1,2,3]"
got := string(jsonStu)
if got != want {
t.Errorf("\n got %s\n want %s\n", got, want)
}
}
func Test_PtrTrans(t *testing.T) {
//实例化一个数据结构,用于生成json字符串
test := new(Class)
test.Name = "1班"
test.Grade = 3
//Marshal失败时err!=nil
jsonStu, err := PtrTrans(test)
if err != nil {
fmt.Println("生成json字符串错误")
}
want := "{\"Name\":\"1班\",\"Grade\":3}"
got := string(jsonStu)
if got != want {
t.Errorf("\n got %s\n want %s\n", got, want)
}
}
在执行单元测试时,我们可以像上一次博客一样【博客】,在vscode中便捷地对每个函数进行test run,当编写单个函数的测试函数时,笔者更推荐这种方式。
当然,也可以像本次博客一样,在项目目录下,输入以下指令:
go test -v myJsonMarshal_test.go myJsonMarshal.go
单元测试结果如下:
[henryhzy@localhost myJsonMarshal]$ go test -v myJsonMarshal_test.go myJsonMarshal.go
=== RUN Test_JsonMarshal
--- PASS: Test_JsonMarshal (0.00s)
=== RUN Test_Marshal
--- PASS: Test_Marshal (0.00s)
=== RUN Test_StringTrans
--- PASS: Test_StringTrans (0.00s)
=== RUN Test_StructTrans
--- PASS: Test_StructTrans (0.00s)
=== RUN Test_MapTrans
--- PASS: Test_MapTrans (0.00s)
=== RUN Test_SliceTrans
--- PASS: Test_SliceTrans (0.00s)
=== RUN Test_ArrayTrans
--- PASS: Test_ArrayTrans (0.00s)
=== RUN Test_PtrTrans
--- PASS: Test_PtrTrans (0.00s)
PASS
ok command-line-arguments 0.002s
首先安装godoc如下:
git clone https://github.com/golang/tools $GOPATH/src/golang.org/x/tools
go build golang.org/x/tools
在项目myJsonMarshal所在目录下,执行如下指令:
go install
go doc
godoc -url="pkg/github.com/user/myJsonMarshal" > API.html
便会在当前目录下生成API.html文件:
【注意:程序包的函数名开头应为大写字母,对于函数A若含有注释,注释的开头也应为//A。】
具体代码可见gitee仓库:gitee