基于Golang的对象序列化的程序包开发——myJsonMarshal
基于Golang的對象序列化的程序包開發——myJsonMarshal【閱讀時間:約10分鐘】
- 一、對象序列化概述
- 二、系統環境&項目介紹
- 1.系統環境
- 2.項目的任務要求
 
- 三、具體程序設計及Golang代碼實現
- 1.程序設計
- 2.JsonMarshal函數
- 3.Marshal函數
- 4.StringTrans函數
- 5.StructTrans函數
- 6.MapTrans函數
- 7.SliceTrans函數
- 8.ArrayTrans函數
- 9.PtrTrans函數
 
- 四、程序測試
- 1.封裝并使用程序包
- 2.功能測試
- 3.單元測試
 
- 五、中文 api 文檔
- 六、完整代碼
- 七、References
一、對象序列化概述
將一個對象寫成特定文本格式的字符流,稱為序列化。在本次開發的程序包myJsonMarshal中,我們可以利用它將將結構數據格式化為 json 字符流。
 
二、系統環境&項目介紹
1.系統環境
操作系統:CentOS7
 硬件信息:使用virtual box配置虛擬機(內存3G、磁盤30G)
 編程語言:GO 1.15.2
2.項目的任務要求
- 參考官方 encoding/json 包 Marshal 函數,將結構數據格式化為 json 字符流 - 必須導出 func JsonMarshal(v interface{}) ([]byte, error)
- 可以參考、甚至復制原來的代碼
- 支持字段的標簽(Tag),標簽滿足 mytag:"你自己的定義"
- 不允許使用第三方包
 
- 必須導出 
- 包必須包括以下內容: - 生成的中文 api 文檔
- 有較好的 Readme 文件,包括一個簡單的使用案例
- 每個go文件必須有對應的測試文件
 
三、具體程序設計及Golang代碼實現
1.程序設計
本次程序包開發實現較為簡單,只要根據輸入的結構數據的類型,逐步分為多種基礎的類型,如int、string和array等,然后將輸入數據及其類型轉為byte[],再轉為json格式的字符流即可。
 其中,參考官方 encoding/json 包 Marshal 函數,在判斷數據類型時用到了reflect程序包。
 myJsonMarshal程序包的函數架構如下:
 
 下面按照myJsonMarshal程序包的源碼順序來依次介紹數據結構和相關函數。
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())), 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函數調用reflect程序包,通過switch-case語句,將不同接口數據類型的數據轉為json字符流。
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
}
StructTrans函數將struct數據轉為json字符流,并且支持字段的標簽(Tag),標簽滿足 mytag:"你自己的定義"。如果變量打上了mytag標簽,如Name旁邊的 mytag:"name" ,那么轉化成的json key就用該標簽“name”,否則取變量名作為key。
 
6.MapTrans函數
//MapTrans 將map數據轉為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函數將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 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() {//實例化一個數據結構,用于生成json字符串stu := Stu{Name: "張三",Age:  18,HIgh: true,}//指針變量cla := new(Class)cla.Name = "1班"cla.Grade = 3stu.Class = cla//Marshal失敗時err!=niljsonStu, err := myJsonMarshal.JsonMarshal(stu)if err != nil {fmt.Println("生成json字符串錯誤")}//jsonStu是[]byte類型,轉化成string類型便于查看fmt.Println(string(jsonStu))
}運行結果:
 
 由此可知程序包的功能測試正常,符合的程序包的幾個要求:
 ①結構化數據轉為json字符流
 ②支持字段的標簽(Tag),標簽滿足 mytag:"你自己的定義"。
 ③指針變量編碼時自動轉換為它所指向的值
3.單元測試
單元測試主要從程序員角度,對程序包的具體函數進行測試。
 建立myJsonMarshal_test.go文件,對程序包的每個函數進行單元測試如下,代碼如下:
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) {//實例化一個數據結構,用于生成json字符串stu := Stu{Name: "張三",Age:  18,HIgh: true,}//指針變量cla := new(Class)cla.Name = "1班"cla.Grade = 3stu.Class = cla//Marshal失敗時err!=niljsonStu, 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 = 3stu.Class = cla//Marshal失敗時err!=niljsonStu, 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!=niljsonStu, 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!=niljsonStu, 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!=niljsonStu, 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!=niljsonStu, 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!=niljsonStu, 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!=niljsonStu, 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
五、中文 api 文檔
首先安裝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
 
七、References
- 課程博客
- go json.Marshal 編組函數講解
- 官方 encoding/json 包
總結
以上是生活随笔為你收集整理的基于Golang的对象序列化的程序包开发——myJsonMarshal的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 【golang程序包推荐分享】go-in
- 下一篇: 【golang程序包推荐分享】分享亿点点
