基于Golang的对象序列化的程序包开发——myJsonMarshal
基于Golang的對(duì)象序列化的程序包開發(fā)——myJsonMarshal【閱讀時(shí)間:約10分鐘】
- 一、對(duì)象序列化概述
- 二、系統(tǒng)環(huán)境&項(xiàng)目介紹
- 1.系統(tǒng)環(huán)境
- 2.項(xiàng)目的任務(wù)要求
- 三、具體程序設(shè)計(jì)及Golang代碼實(shí)現(xiàn)
- 1.程序設(shè)計(jì)
- 2.JsonMarshal函數(shù)
- 3.Marshal函數(shù)
- 4.StringTrans函數(shù)
- 5.StructTrans函數(shù)
- 6.MapTrans函數(shù)
- 7.SliceTrans函數(shù)
- 8.ArrayTrans函數(shù)
- 9.PtrTrans函數(shù)
- 四、程序測試
- 1.封裝并使用程序包
- 2.功能測試
- 3.單元測試
- 五、中文 api 文檔
- 六、完整代碼
- 七、References
一、對(duì)象序列化概述
將一個(gè)對(duì)象寫成特定文本格式的字符流,稱為序列化。在本次開發(fā)的程序包myJsonMarshal中,我們可以利用它將將結(jié)構(gòu)數(shù)據(jù)格式化為 json 字符流。
二、系統(tǒng)環(huán)境&項(xiàng)目介紹
1.系統(tǒng)環(huán)境
操作系統(tǒng):CentOS7
硬件信息:使用virtual box配置虛擬機(jī)(內(nèi)存3G、磁盤30G)
編程語言:GO 1.15.2
2.項(xiàng)目的任務(wù)要求
- 參考官方 encoding/json 包 Marshal 函數(shù),將結(jié)構(gòu)數(shù)據(jù)格式化為 json 字符流
- 必須導(dǎo)出
func JsonMarshal(v interface{}) ([]byte, error) - 可以參考、甚至復(fù)制原來的代碼
- 支持字段的標(biāo)簽(Tag),標(biāo)簽滿足
mytag:"你自己的定義" - 不允許使用第三方包
- 必須導(dǎo)出
- 包必須包括以下內(nèi)容:
- 生成的中文 api 文檔
- 有較好的 Readme 文件,包括一個(gè)簡單的使用案例
- 每個(gè)go文件必須有對(duì)應(yīng)的測試文件
三、具體程序設(shè)計(jì)及Golang代碼實(shí)現(xiàn)
1.程序設(shè)計(jì)
本次程序包開發(fā)實(shí)現(xiàn)較為簡單,只要根據(jù)輸入的結(jié)構(gòu)數(shù)據(jù)的類型,逐步分為多種基礎(chǔ)的類型,如int、string和array等,然后將輸入數(shù)據(jù)及其類型轉(zhuǎn)為byte[],再轉(zhuǎn)為json格式的字符流即可。
其中,參考官方 encoding/json 包 Marshal 函數(shù),在判斷數(shù)據(jù)類型時(shí)用到了reflect程序包。
myJsonMarshal程序包的函數(shù)架構(gòu)如下:
下面按照myJsonMarshal程序包的源碼順序來依次介紹數(shù)據(jù)結(jié)構(gòu)和相關(guān)函數(shù)。
2.JsonMarshal函數(shù)
//JsonMarshal 輸入結(jié)構(gòu)化數(shù)據(jù),返回json字符流和error
func JsonMarshal(v interface{}) ([]byte, error) {b, err := Marshal(v)if err != nil {return nil, err}return b, nil
}
JsonMarshal函數(shù)是本程序包的主要接口,通過輸入結(jié)構(gòu)化數(shù)據(jù),可以返回json字符流和報(bào)錯(cuò)信息error。
3.Marshal函數(shù)
//Marshal 將接口數(shù)據(jù)類型的數(shù)據(jù)轉(zhuǎn)為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())), nilcase 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")}
}
Marshal函數(shù)調(diào)用reflect程序包,通過switch-case語句,將不同接口數(shù)據(jù)類型的數(shù)據(jù)轉(zhuǎn)為json字符流。
4.StringTrans函數(shù)
//StringTrans 將string數(shù)據(jù)轉(zhuǎn)為json字符流
func StringTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)return []byte("\"" + json.String() + "\""), nil
}
StringTrans函數(shù)將string數(shù)據(jù)轉(zhuǎn)為json字符流
5.StructTrans函數(shù)
//StructTrans 將struct數(shù)據(jù)轉(zhuǎn)為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
}
StructTrans函數(shù)將struct數(shù)據(jù)轉(zhuǎn)為json字符流,并且支持字段的標(biāo)簽(Tag),標(biāo)簽滿足 mytag:"你自己的定義"。如果變量打上了mytag標(biāo)簽,如Name旁邊的 mytag:"name" ,那么轉(zhuǎn)化成的json key就用該標(biāo)簽“name”,否則取變量名作為key。
6.MapTrans函數(shù)
//MapTrans 將map數(shù)據(jù)轉(zhuǎn)為json字符流
func MapTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)i := json.MapRange()first := trueb := "{"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函數(shù)將map數(shù)據(jù)轉(zhuǎn)為json字符流
7.SliceTrans函數(shù)
//SliceTrans 將slice數(shù)據(jù)轉(zhuǎn)為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函數(shù)將slice數(shù)據(jù)轉(zhuǎn)為json字符流
8.ArrayTrans函數(shù)
//ArrayTrans 將array數(shù)據(jù)轉(zhuǎn)為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函數(shù)將array數(shù)據(jù)轉(zhuǎn)為json字符流
9.PtrTrans函數(shù)
//PtrTrans 將ptr數(shù)據(jù)轉(zhuǎn)為json字符流
func PtrTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)return Marshal(json.Elem().Interface())
}
StringTrans函數(shù)將ptr數(shù)據(jù)轉(zhuǎn)為json字符流,指針變量編碼時(shí)自動(dòng)轉(zhuǎn)換為它所指向的值。
四、程序測試
1.封裝并使用程序包
將項(xiàng)目myJsonMarshal的myJsonMarshal.go文件的main函數(shù)注釋掉,package改為package myJsonMarshal,然后執(zhí)行如下指令:
go build
在其他路徑下建立main.go,并調(diào)用myJsonMarshal.JsonMarshal函數(shù)即可。
2.功能測試
功能測試主要從用戶角度測試程序包的功能,步驟如下:
創(chuàng)建main.go文件,內(nèi)容如下(結(jié)構(gòu)化數(shù)據(jù)類型及其數(shù)值可自定義):
package mainimport ("fmt""github.com/user/myJsonMarshal"
)type Stu struct {Name string `mytag:"name"`Age intHIgh boolClass *Class `mytag:"class"`
}type Class struct {Name stringGrade int
}func main() {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串stu := Stu{Name: "張三",Age: 18,HIgh: true,}//指針變量cla := new(Class)cla.Name = "1班"cla.Grade = 3stu.Class = cla//Marshal失敗時(shí)err!=niljsonStu, err := myJsonMarshal.JsonMarshal(stu)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}//jsonStu是[]byte類型,轉(zhuǎn)化成string類型便于查看fmt.Println(string(jsonStu))
}
運(yùn)行結(jié)果:
由此可知程序包的功能測試正常,符合的程序包的幾個(gè)要求:
①結(jié)構(gòu)化數(shù)據(jù)轉(zhuǎn)為json字符流
②支持字段的標(biāo)簽(Tag),標(biāo)簽滿足 mytag:"你自己的定義"。
③指針變量編碼時(shí)自動(dòng)轉(zhuǎn)換為它所指向的值
3.單元測試
單元測試主要從程序員角度,對(duì)程序包的具體函數(shù)進(jìn)行測試。
建立myJsonMarshal_test.go文件,對(duì)程序包的每個(gè)函數(shù)進(jìn)行單元測試如下,代碼如下:
package myJsonMarshalimport ("fmt""testing"
)type Stu struct {Name string `mytag:"name"`Age intHIgh boolClass *Class `mytag:"class"`
}type Class struct {Name stringGrade int
}func Test_JsonMarshal(t *testing.T) {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串stu := Stu{Name: "張三",Age: 18,HIgh: true,}//指針變量cla := new(Class)cla.Name = "1班"cla.Grade = 3stu.Class = cla//Marshal失敗時(shí)err!=niljsonStu, err := JsonMarshal(stu)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}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) {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串stu := Stu{Name: "張三",Age: 18,HIgh: true,}//指針變量cla := new(Class)cla.Name = "1班"cla.Grade = 3stu.Class = cla//Marshal失敗時(shí)err!=niljsonStu, err := Marshal(stu)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}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) {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串test := "unit test"//Marshal失敗時(shí)err!=niljsonStu, err := StringTrans(test)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}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) {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串test := Class{Name: "1班",Grade: 3,}//Marshal失敗時(shí)err!=niljsonStu, err := StructTrans(test)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}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) {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串test := map[string]int{"one": 1,"two": 2,"three": 3,}//Marshal失敗時(shí)err!=niljsonStu, err := MapTrans(test)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}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) {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串test := []int{1, 2, 3}//Marshal失敗時(shí)err!=niljsonStu, err := SliceTrans(test)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}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) {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串test := [3]int{1, 2, 3}//Marshal失敗時(shí)err!=niljsonStu, err := ArrayTrans(test)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}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) {//實(shí)例化一個(gè)數(shù)據(jù)結(jié)構(gòu),用于生成json字符串test := new(Class)test.Name = "1班"test.Grade = 3//Marshal失敗時(shí)err!=niljsonStu, err := PtrTrans(test)if err != nil {fmt.Println("生成json字符串錯(cuò)誤")}want := "{\"Name\":\"1班\",\"Grade\":3}"got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}
在執(zhí)行單元測試時(shí),我們可以像上一次博客一樣【博客】,在vscode中便捷地對(duì)每個(gè)函數(shù)進(jìn)行test run,當(dāng)編寫單個(gè)函數(shù)的測試函數(shù)時(shí),筆者更推薦這種方式。
當(dāng)然,也可以像本次博客一樣,在項(xiàng)目目錄下,輸入以下指令:
go test -v myJsonMarshal_test.go myJsonMarshal.go
單元測試結(jié)果如下:
[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
五、中文 api 文檔
首先安裝godoc如下:
git clone https://github.com/golang/tools $GOPATH/src/golang.org/x/tools
go build golang.org/x/tools
在項(xiàng)目myJsonMarshal所在目錄下,執(zhí)行如下指令:
go install
go doc
godoc -url="pkg/github.com/user/myJsonMarshal" > API.html
便會(huì)在當(dāng)前目錄下生成API.html文件:
【注意:程序包的函數(shù)名開頭應(yīng)為大寫字母,對(duì)于函數(shù)A若含有注釋,注釋的開頭也應(yīng)為//A。】
六、完整代碼
具體代碼可見gitee倉庫:gitee
七、References
- 課程博客
- go json.Marshal 編組函數(shù)講解
- 官方 encoding/json 包
總結(jié)
以上是生活随笔為你收集整理的基于Golang的对象序列化的程序包开发——myJsonMarshal的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【golang程序包推荐分享】go-in
- 下一篇: 【golang程序包推荐分享】分享亿点点