文章目錄
- 課程任務
- 設計說明
- 測驗
- 生成API檔案
- 作業提交
- 我的完整代碼
課程任務
-
參考官方 encoding/json 包 Marshal 函式,將結構資料格式化為 json 字符流
必須匯出
func JsonMarshal(v interface{}) ([]byte, error)可以參考、甚至復制原來的代碼
支持欄位的標簽(Tag),標簽滿足
mytag:"你自己的定義"不允許使用第三方包
-
包必須包括以下內容:
生成的中文 api 檔案
有較好的 Readme 檔案,包括一個簡單的使用案例
每個go檔案必須有對應的測驗檔案
設計說明
新建一個包 json,新建檔案 json.go,里面實作函式func JsonMarshal(v interface{}) ([]byte, error),
在前面的學習里,我們知道,在go語言里面,首字母大寫的表示在外面是可訪問的,非大寫的就是不可訪問的,在JsonMarshal函式的實作里面,我們不可避免地要呼叫其他函式套娃再套娃,可以把其他不必要的函式封裝起來,也就是以小寫字母開頭,
通過官方原始碼encoding/json/encode.go ,我們可以知道Marshal函式的大致實作程序,用的是reflect包,在我們后續的實作中同樣也是呼叫這個包,
官方檔案里面的實作相對比較復雜,因為考慮了諸多因素,而我們這實作一個簡化能用版的話就沒考慮這么多了,
首先是函式func JsonMarshal(v interface{}) ([]byte, error),
func JsonMarshal(v interface{}) ([]byte, error) {
b, err := marshal(v)
if err != nil {
return nil, err
}
return b, nil
}
通過呼叫一個包內私有函式marshal來實作,
再到函式func marshal(v interface{}) ([]byte, error),
func marshal(v interface{}) ([]byte, error) {
if v == nil {
return []byte("null"), nil
}
s := reflect.ValueOf(v)
typeOfS := s.Type()
switch typeOfS.Kind() {
case reflect.Slice, reflect.Array :
return sliceMarshal(v)
case reflect.Struct :
return structMarshal(v)
case reflect.Map :
return mapMarshal(v)
case reflect.Ptr :
return marshal(s.Elem().Interface())
case reflect.String :
return []byte("\"" + s.String() + "\""), nil
default :
return []byte(fmt.Sprintf("%v", s.Interface())), nil
}
}
先是要判斷型別,除了string、int等基本型別可以直接回傳以外,Ptr型別則需要取地址,其他的都要下交給對應的函式實作,
先看func sliceMarshal(v interface{}) ([]byte, error),
func sliceMarshal(v interface{}) ([]byte, error) {
var b strings.Builder
b.WriteByte('[')
s := reflect.ValueOf(v)
for i := 0; i < s.Len(); i++ {
f := s.Index(i)
if i > 0 {
b.WriteByte(',')
}
tempB, e := marshal(f.Interface())
if e != nil {
return nil, e
}
b.Write(tempB)
}
b.WriteByte(']')
return []byte(b.String()), nil
}
將陣列/分片遍歷一遍,對應的值就再次呼叫marshal函式來決議,套娃
再到func structMarshal(v interface{}) ([]byte, error)
func structMarshal(v interface{}) ([]byte, error) {
var b strings.Builder
b.WriteByte('{')
s := reflect.ValueOf(v)
typeOfS := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
if i > 0 {
b.WriteByte(',')
}
tag := typeOfS.Field(i).Tag.Get("mytag")
if tag == "" {
b.WriteString(fmt.Sprintf("\"%s\":", typeOfS.Field(i).Name))
} else {
b.WriteString(fmt.Sprintf("\"%s\":", tag))
}
tempB, e := marshal(f.Interface())
if e != nil {
return nil, e
}
b.Write(tempB)
}
b.WriteByte('}')
return []byte(b.String()), nil
}
對于struct型別的決議相對來說有一丟丟復雜,但也差不太多,只是需要列印變數名,有Tag的情況下就列印對應Tag,所對應的值同樣也是再次呼叫marshal函式來決議,套娃
最后是func mapMarshal(v interface{}) ([]byte, error),
func mapMarshal(v interface{}) ([]byte, error) {
var b strings.Builder
b.WriteByte('{')
s := reflect.ValueOf(v)
it := s.MapRange()
first := true
for it.Next() {
if first {
first = false
} else {
b.WriteByte(',')
}
b.WriteString(fmt.Sprintf("\"%v\":", it.Key()))
tempB, e := marshal(it.Value().Interface())
if e != nil {
return nil, e
}
b.Write(tempB)
}
b.WriteByte('}')
return []byte(b.String()), nil
}
依舊是將Map遍歷一遍,列印Key跟Value,套娃
至此,函式func JsonMarshal(v interface{}) ([]byte, error)就完成了,
測驗
對json.go里面的每一個函式都撰寫一個測驗,好像也沒什么大作用,因為每個都是互相呼叫套娃的,
type Monitor struct {
ID int
Name string
}
type Group struct {
ID int
Name string
Members []string
Nums [3]int
M Monitor `mytag:"GG"`
Manage map[int][]int
}
func ExampleSliceMarshal() {
b, err := sliceMarshal([]int{1,2,3})
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:[1,2,3]
}
func ExampleStructMarshal() {
group := Group{
ID:1,
Name:"Reds",
Members:[]string{"Crimson", "Red", "Ruby", "Maroon"},
Nums:[3]int{1,2,3},
M:Monitor{18666,"yzdl"},
}
group.Manage= make(map[int][]int)
group.Manage[2] = []int{1,2,3}
b, err := structMarshal(group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:{"ID":1,"Name":"Reds","Members":["Crimson","Red","Ruby","Maroon"],"Nums":[1,2,3],"GG":{"ID":18666,"Name":"yzdl"},"Manage":{"2":[1,2,3]}}
}
func ExampleMapMarshal() {
M := make(map[int][]int)
M[2] = []int{1,2,3}
b, err := mapMarshal(M)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:{"2":[1,2,3]}
}
func ExampleMarshal() {
b, err := marshal(123)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:123
}
func ExampleJsonMarshal() {
group := Group{
ID:1,
Name:"Reds",
Members:[]string{"Crimson", "Red", "Ruby", "Maroon"},
Nums:[3]int{1,2,3},
M:Monitor{18666,"yzdl"},
}
group.Manage= make(map[int][]int)
group.Manage[2] = []int{1,2,3}
b, err := JsonMarshal(&group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:{"ID":1,"Name":"Reds","Members":["Crimson","Red","Ruby","Maroon"],"Nums":[1,2,3],"GG":{"ID":18666,"Name":"yzdl"},"Manage":{"2":[1,2,3]}}
}
基本上就是每種型別的值都讓marshal執行一遍,

然后執行指令go build github.com/github-user/HHH/json,其他程式就能夠通過import "github.com/github-user/HHH/json"來呼叫這個包里面的函式,
撰寫一個test.go試一試,
package main
import (
"fmt"
"github.com/github-user/HHH/json"
)
type Group struct {
ID int
Name string
}
type ColorGroup struct {
ID int
Name string
Colors []string
Nums [3]int
G Group `mytag:"GG"`
M map[int][]int
}
func main() {
group := ColorGroup{
ID:1,
Name:"Reds",
Colors:[]string{"Crimson", "Red", "Ruby", "Maroon"},
Nums:[3]int{1,2,3},
G:Group{1,"123"},
}
group.M = make(map[int][]int)
group.M[2] = []int{1,2,3}
b, err := json.JsonMarshal(group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
b, err = json.JsonMarshal(&group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
b, err = json.JsonMarshal("123456")
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
}
執行指令go run test.go,程式正常運行,

生成API檔案
就像上一次作業一樣,執行godoc就能夠通過相應路徑訪問API了,

作業提交
我的完整代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/194219.html
標籤:其他
